Menu

#72 fgetc/ungetc pollutes fread return value

CRTL Bug
open
nobody
CRTL_BUG (27)
5
2015-02-11
2013-10-13
No

In the attached example, calling fgetc followed by ungetc to snoop a character from the input stream and then put it back corrupts the return value of a subsequent fread call. Specifically, the return value of fread is a large, apparently random number after requesting one item of one byte. As the example demonstrates, the problem does not happen unless you call fgetc/ungetc first.

For ease of reading, I'm including the reproducer inline below in addition to the attachment.

$ cc/vers
HP C V7.3-020 on OpenVMS IA64 V8.4
$ type ungetc_bug.c
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

is(int ok, char *test, int got, int expected) {
  static int test_count = 0;
  test_count++;
  printf("%sok %d - %s\n", ok ? "" : "NOT ", test_count, test);
  if (!ok) printf("     got: %d\nexpected: %d\n", got, expected);
}

main() {
  static const char test_file[] = "ungetc_bug.tmp";
  int char_borrowed, char_returned, test_count = 0;
  size_t num_items;
  FILE *f;
  char c;

  if ((f = fopen(test_file, "w")) == NULL) {
      perror("fopen() for write error");
      exit(44);
  }

  num_items = fwrite("abcdefg\n", sizeof("abcdefg\n"), 1, f);
  if (num_items == (size_t)EOF)
      perror("fwrite() error");

  fclose(f);

  /* Now open it again for read. */
  if ((f = fopen(test_file, "r")) == NULL) {
      perror("fopen() for read error");
      exit(44);
  }

  /* Read one byte and verify that's all we got */
  num_items = fread(&c, 1, 1, f);
  if (num_items == (size_t)EOF) {
      perror("fread() error");
      exit(44);
  }

  is(num_items == 1,
     "Before fgetc/ungetc, asked fread for one item of one byte",
     (int)num_items,
     1);

  /* Pop off a character from the stream... */
  char_borrowed = fgetc(f);
  if (char_borrowed == EOF)
      perror("fgetc() error");

  /* ... and push it back on. */
  char_returned = ungetc(char_borrowed, f);
  if (char_returned == EOF)
      perror("ungetc() error");

  is(char_returned == char_borrowed,
     "ungetc returns same character as fgetc",
     char_returned,
     char_borrowed);

  /* Now ask for another byte and see how many we get. */
  num_items = fread(&c, 1, 1, f);
  if (num_items == (size_t)EOF)
      perror("fread() error");

  is(num_items == 1,
     "After fgetc/ungetc, asked fread for one item of one byte",
     (int)num_items,
     1);

  is(char_borrowed == (int)c,
     "fread gets the same character we snooped and put back",
     (int)c,
     char_borrowed);

  fclose(f);
  unlink(test_file);
}

$ cc ungetc_bug
$ link ungetc_bug
$ run ungetc_bug
ok 1 - Before fgetc/ungetc, asked fread for one item of one byte
ok 2 - ungetc returns same character as fgetc
NOT ok 3 - After fgetc/ungetc, asked fread for one item of one byte
     got: 412737
expected: 1
ok 4 - fread gets the same character we snooped and put back
$
1 Attachments

Related

Tickets: #72

Discussion

  • Chippagiri Murali Krishna

    Hi,

    Thanks for reporting the issue. We are able to reproduce the problem using the given
    C program when compiled with default optimization level. But the problem is not seen
    when compiled with /NOOPT qualifier.

    Currently we are trying to add traces to the portion of CRTL code where we are
    suspecting the problem and analyze.

    We will keep you updated with the progress.

    Thanks,
    Murali

     
  • Craig A. Berry

    Craig A. Berry - 2013-10-31

    I can reproduce the problem when compiling the example with /NOOPT. Or did you mean running against a version of the CRTL compiled with /NOOPT?

    On Oct 31, 2013, at 7:06 AM, Chippagiri Murali Krishna chippagiri@users.sf.net wrote:

    Hi,

    Thanks for reporting the issue. We are able to reproduce the problem using the given
    C program when compiled with default optimization level. But the problem is not seen
    when compiled with /NOOPT qualifier.

    Currently we are trying to add traces to the portion of CRTL code where we are
    suspecting the problem and analyze.

    We will keep you updated with the progress.

    Thanks,
    Murali

    [tickets:#72] fgetc/ungetc pollutes fread return value

    Status: open
    Labels: CRTL_BUG
    Created: Sun Oct 13, 2013 10:59 PM UTC by Craig A. Berry
    Last Updated: Sun Oct 13, 2013 10:59 PM UTC
    Owner: nobody

    In the attached example, calling fgetc followed by ungetc to snoop a character from the input stream and then put it back corrupts the return value of a subsequent fread call. Specifically, the return value of fread is a large, apparently random number after requesting one item of one byte. As the example demonstrates, the problem does not happen unless you call fgetc/ungetc first.

    For ease of reading, I'm including the reproducer inline below in addition to the attachment.

    $ cc/vers
    HP C V7.3-020 on OpenVMS IA64 V8.4
    $ type ungetc_bug.c

    include <unistd.h>

    include <stdio.h>

    include <stdlib.h>

    is(int ok, char *test, int got, int expected) {

    static int test_count = 0;

    test_count++;

    printf("%sok %d - %s\n", ok ? "" : "NOT ", test_count, test);

    if (!ok) printf(" got: %d\nexpected: %d\n", got, expected);
    }

    main() {

    static const char test_file[] = "ungetc_bug.tmp";

    int char_borrowed, char_returned, test_count = 0;

    size_t num_items;

    FILE *f;

    char c;

    if ((f = fopen(test_file, "w")) == NULL) {

    perror("fopen() for write error");

    exit(44);

    }

    num_items = fwrite("abcdefg\n", sizeof("abcdefg\n"), 1, f);

    if (num_items == (size_t)EOF)

    perror("fwrite() error");

    fclose(f);

    / Now open it again for read. /

    if ((f = fopen(test_file, "r")) == NULL) {

    perror("fopen() for read error");

    exit(44);

    }

    / Read one byte and verify that's all we got /

    num_items = fread(&c, 1, 1, f);

    if (num_items == (size_t)EOF) {

    perror("fread() error");

    exit(44);

    }

    is(num_items == 1,

    "Before fgetc/ungetc, asked fread for one item of one byte",

    (int)num_items,

    1);

    / Pop off a character from the stream... /

    char_borrowed = fgetc(f);

    if (char_borrowed == EOF)

    perror("fgetc() error");

    / ... and push it back on. /

    char_returned = ungetc(char_borrowed, f);

    if (char_returned == EOF)

    perror("ungetc() error");

    is(char_returned == char_borrowed,

    "ungetc returns same character as fgetc",

    char_returned,

    char_borrowed);

    / Now ask for another byte and see how many we get. /

    num_items = fread(&c, 1, 1, f);

    if (num_items == (size_t)EOF)

    perror("fread() error");

    is(num_items == 1,

    "After fgetc/ungetc, asked fread for one item of one byte",

    (int)num_items,

    1);

    is(char_borrowed == (int)c,

    "fread gets the same character we snooped and put back",

    (int)c,

    char_borrowed);

    fclose(f);

    unlink(test_file);
    }

    $ cc ungetc_bug
    $ link ungetc_bug
    $ run ungetc_bug
    ok 1 - Before fgetc/ungetc, asked fread for one item of one byte
    ok 2 - ungetc returns same character as fgetc
    NOT ok 3 - After fgetc/ungetc, asked fread for one item of one byte

    got: 412737
    expected: 1
    ok 4 - fread gets the same character we snooped and put back
    $
    Sent from sourceforge.net because you indicated interest in https://sourceforge.net/p/vms-ports/tickets/72/

    To unsubscribe from further messages, please visit https://sourceforge.net/auth/subscriptions/


    Craig A. Berry
    mailto:craigberry@mac.com

    "... getting out of a sonnet is much more
    difficult than getting in."
    Brad Leithauser

     

    Related

    Tickets: #72

  • Chippagiri Murali Krishna

    Hi Craig,

    You are right, the problem is seen even with /NOOPT compiler qualifier. Actually we
    observed that the fread is returning correct value while debugging which is compiled
    with noopt; now we have observed this also not consistent i.e. return value varies
    system to system in debug mode. Probably some buffer corruption/override issue which
    is leading to this issue. Sorry for misleading you and Thanks for the information.

    We will look into it.

    Thanks,
    Murali

     
  • Chippagiri Murali Krishna

    Analysis:

    CRTL fread() function has to read the characters from the file stream. If ungetc() is used
    before fread() to push the character in to the input stream, then first fread() would read
    the character pushed by the ungetc() call, followed by the main buffer.

    OpenVMS CRTL maintains some local members to calculate and return the number of items read
    using fread() call; there are some instances where these local members would have some
    garbage values which leads fread() to return wrong values.

    We have modified the CRTL code to fix the reported problem; currently performing the
    testing.

    Thanks,
    Murali

     
  • Bill Pedersen

    Bill Pedersen - 2014-04-18
    • Priority: --> 5
    • Group: 1.0 --> CRTL Bug
     
  • Craig A. Berry

    Craig A. Berry - 2014-11-09

    It looks like this one was fixed in VMS84I_ACRTL-V0300 so I think this ticket can be closed.

     

Log in to post a comment.