Bug 5994 - fflush after ungetc on seekable input stream
Summary: fflush after ungetc on seekable input stream
Status: NEW
Alias: None
Product: glibc
Classification: Unclassified
Component: stdio (show other bugs)
Version: 2.3.4
: P2 normal
Target Milestone: ---
Assignee: Ulrich Drepper
URL:
Keywords:
: 9754 (view as bug list)
Depends on:
Blocks:
 
Reported: 2008-03-29 05:42 UTC by Eric Blake
Modified: 2014-07-01 21:14 UTC (History)
5 users (show)

See Also:
Host:
Target:
Build:
Last reconfirmed:
fweimer: security-


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Eric Blake 2008-03-29 05:42:37 UTC
Consider this program:

$ cat foo.c
#include <stdio.h>
#include <unistd.h>
int
main (int argc, char **argv)
{
  /* Check that fflush after a non-backup ungetc() call discards the ungetc
     buffer.  */
  int c;

  c = fgetc (stdin);
  printf ("c = '%c'\n", c);

  c = fgetc (stdin);
  printf ("c = '%c'\n", c);

  c = ungetc (argc > 1 ? c : '@', stdin);
  printf ("ungetc result = '%c'\n", c);

  fflush (stdin);
  printf ("after fflush, location = %d\n",
	  (int) lseek (fileno (stdin), 0, SEEK_CUR));

  c = fgetc (stdin);
  printf ("c = '%c'\n", c);

  c = fgetc (stdin);
  printf ("c = '%c'\n", c);

  return 0;
}
$ ./foo < foo.c
c = '#'
c = 'i'
ungetc result = '@'
after fflush, location = 582
c = 'n'
c = 'c'
$ ./foo 1 < foo.c
c = '#'
c = 'i'
ungetc result = 'i'
after fflush, location = 1
c = 'i'
c = 'n'


I found it inconsistent that fflush on seekable input streams sets the position
of the underlying file descriptor to a different location based on whether the
byte that was pushed back was what was originally read.  And I'm not the only
one - the Austin Group admitted that Interp-002 is fishy, and that an aardvark
will be needed after POSIX 200x is released to require that fflush discards all
ungetc data and sets the offset of the file descriptor to the stream position
(ie. like the 2nd run, when pushing back the byte that was read, and NOT like
the behavior of the 1st run when pushing back random bytes).

https://www.opengroup.org/sophocles/show_mail.tpl?CALLER=index.tpl&source=L&listname=austin-group-l&id=11398
Comment 1 Ulrich Drepper 2008-03-29 16:37:06 UTC
I'm not even going to look at this until something at the standard level is decided.
Comment 2 Ondrej Bilka 2013-10-17 11:49:45 UTC
> I'm not even going to look at this until something
> at the standard level is decided.

A gnulib had a patch for this from 2009. 
http://lists.gnu.org/archive/html/bug-gnulib/2009-01/msg00131.html

Was this also send to libc?
Comment 3 Eric Blake 2013-10-17 12:05:27 UTC
(In reply to Ondrej Bilka from comment #2)
> > I'm not even going to look at this until something
> > at the standard level is decided.
> 
> A gnulib had a patch for this from 2009. 
> http://lists.gnu.org/archive/html/bug-gnulib/2009-01/msg00131.html
> 
> Was this also send to libc?

Not to my knowledge; POSIX 2013 is now explicit about fflush behavior, and gnulib is still claiming that glibc fflush() is broken and needs a workaround.
Comment 4 Eric Blake 2013-10-17 12:09:06 UTC
*** Bug 9754 has been marked as a duplicate of this bug. ***
Comment 5 Rich Felker 2013-10-17 23:18:23 UTC
I'm not clear what the current glibc behavior is. Does it reflect the Austin Group decision cited in the text of glibc bug 9754 (the one marked duplicate) or is it still somewhere in between the correct (per Austin Group) behavior and the (horribly wrong) behavior the original text of the standard demanded?
Comment 6 Eric Blake 2013-10-17 23:30:54 UTC
(In reply to Rich Felker from comment #5)
> I'm not clear what the current glibc behavior is. Does it reflect the Austin
> Group decision cited in the text of glibc bug 9754 (the one marked
> duplicate) or is it still somewhere in between the correct (per Austin
> Group) behavior and the (horribly wrong) behavior the original text of the
> standard demanded?

Current glibc behavior is still broken.  The test program in this report is what drove the Austin Group decision cited in glibc bug 9754.

When compiled, the foo.c in this report should output:

$ ./foo < foo.c
c = '#'
c = 'i'
ungetc result = '@'
after fflush, location = 1
c = 'i'
c = 'n'
$ ./foo 1 < foo.c
c = '#'
c = 'i'
ungetc result = 'i'
after fflush, location = 1
c = 'i'
c = 'n'

I tested on rawhide with glibc 2.18.90 gets it right in the second instance, but is still broken in the first:

$ ./foo < foo.c
c = '#'
c = 'i'
ungetc result = '@'
after fflush, location = 581
c = 'n'
c = 'c'
Comment 7 Rich Felker 2013-10-18 11:11:57 UTC
Thanks Eric. I tested again and got the same results. I was just uncertain, with this bug having been around so long, whether anything had changed since it was first reported. Now I guess we need someone familiar with the code to look at what's involved in fixing it.