Austin Group Defect Tracker

Aardvark Mark IV


Viewing Issue Simple Details Jump to Notes ] Issue History ] Print ]
ID Category Severity Type Date Submitted Last Update
0000816 [1003.1(2013)/Issue7+TC1] System Interfaces Objection Clarification Requested 2014-01-17 21:38 2019-08-29 16:09
Reporter eblake View Status public  
Assigned To
Priority normal Resolution Accepted  
Status Closed  
Name Eric Blake
Organization Red Hat
User Reference ebb.fflush
Section fflush
Page Number 850
Line Number 28391
Interp Status ---
Final Accepted Text See desired action
Summary 0000816: does fflush(NULL) affect seekable read streams?
Description As a C extension, POSIX requires fflush(stream) to affect the underlying position of a seekable file descriptor associated with any input stream. But it is not obvious whether this effect is also required for fflush(NULL), even though such behavior is observable and desirable on POSIX platforms. The existing wording describes fflush(NULL) in terms of actions "defined above", but where the above text only describes output streams, and the CX behavior of input streams is described below, so someone could argue that input streams are unaffected by such a flush, but that contradicts existing behavior.

I noticed the problem while trying to explain why the program below violates XSH 2.5.1 (page 496) and therefore exhibits undefined behavior when called with 0 arguments [argc==1] (in particular, Solaris reports different output than glibc, and the program goes into an infinite loop on any build of cygwin from 2013. Cygwin has since been patched to behave more like glibc, although I still contend that glibc has never fully obeyed the rules regarding fflush on exit in the first place). The reason that the argc==1 case is undefined is that the first handle (the use of the stream prior to fork()) used fgetc() to alter the position of the stream, but did not fflush or fseek to reflect that position back to the underlying fd; the second stream then also alters the position of the stream (via the implicit flush done by the implicit exit() at the end of main) leaving the next byte read from the parents stream indeterminate. My immediate suggestion to insert an fflush(NULL) prior to fork() won't work if fflush(NULL) doesn't guarantee an effect on input streams [the behavior demonstrated with argc==3 - and evidence of the glibc bug of not flushing on exit], but for all systems that already comply with POSIX, my testing showed identical to the behavior of fflush(fp) [argc==2], which is why I prefer mandating the behavior even if it further points out problems in the glibc implementation.

$ cat foo.c
#include <stdio.h>
#include <unistd.h>

int main(int argc, char **argv)
{
    FILE *fp = fopen("foo.c", "r");
    printf("parent: %d\n", (int)getpid());
    fgetc(fp);
    switch (argc) {
    case 2:
        fflush(fp);
        break;
    case 3:
        fflush(NULL);
        break;
    }
    switch (fork()) {
    case -1:
        _exit(1);
    case 0:
        break;
    default:
        sleep(1);
    }
    printf("process %d, offset %d\n", (int)getpid(),
           (int)lseek(fileno(fp), 0, SEEK_CUR));
    return 0;
}

Linux:
$ ./foo
parent: 24952
process 24953, offset 526
process 24952, offset 526
$ ./foo 1
parent: 24960
process 24961, offset 1
process 24960, offset 1
$ ./foo 1 2
parent: 25702
process 25703, offset 526
process 25702, offset 526

Solaris:
$ ./foo
parent: 16333
process 16334, offset 526
process 16333, offset 1
$ ./foo 1
parent: 16336
process 16337, offset 1
process 16336, offset 1
$ ./foo 1 2
parent: 16347
process 16348, offset 1
process 16347, offset 1

Intuitively, just as:
putc('a');
if (cond)
  fflush(NULL);
fork();
exit();
needs 'cond' to be non-zero to guarantee that 'a' is not output twice, the counterpart for reading needs fflush(NULL) before fork to guarantee that 'a' is not read twice.

And the reason that Linux behavior is buggy is that we require that:

{ sed -n 1q; sed -n 1q; cat; } < file

behave the same as

tail -n+3 file

for any seekable file, but because Linux fails to reset the position of fds on normal exit(), the subsequent processes in the group do not pick up processing where the earlier processes left off.
Desired Action On page 850 (XSH fflush DESCRIPTION), swap lines 28391-2 with lines 28393-7. The resulting order of the two paragraphs (as shown below) then makes it clear that fflush(NULL) must affect underlying positions of seekable fds associated with read streams:

For a stream open for reading, if the file is not already at EOF, 
and the file is one capable of seeking, the file offset of the 
underlying open file description shall be set to the file position
of the stream, and any characters pushed back onto the stream by 
ungetc( ) or ungetwc( ) that have not subsequently been read from 
the stream shall be discarded (without further changing the file offset).

If stream is a null pointer, fflush( ) shall perform this flushing 
action on all streams for which the behavior is defined above.
Tags tc2-2008
Attached Files

- Relationships
related to 0000087Closedajosey 1003.1(2008)/Issue 7 fflush and ungetc 
related to 0000701Applied 1003.1(2013)/Issue7+TC1 unget[w]c() and file position after discarding push back 

-  Notes
(0002108)
eblake (manager)
2014-01-17 22:04

For anyone that read the original problem description, I ended up tweaking it to fix some errors in my writeup that I noticed in subsequent testing.

- Issue History
Date Modified Username Field Change
2014-01-17 21:38 eblake New Issue
2014-01-17 21:38 eblake Name => Eric Blake
2014-01-17 21:38 eblake Organization => Red Hat
2014-01-17 21:38 eblake User Reference => ebb.fflush
2014-01-17 21:38 eblake Section => fflush
2014-01-17 21:38 eblake Page Number => 850
2014-01-17 21:38 eblake Line Number => 28391
2014-01-17 21:38 eblake Interp Status => ---
2014-01-17 21:52 eblake Description Updated
2014-01-17 22:03 eblake Description Updated
2014-01-17 22:04 eblake Note Added: 0002108
2014-01-17 22:13 eblake Relationship added related to 0000087
2014-03-13 16:00 nick Final Accepted Text => See desired action
2014-03-13 16:00 nick Status New => Resolved
2014-03-13 16:00 nick Resolution Open => Accepted
2014-03-13 16:00 nick Tag Attached: tc2-2008
2014-03-13 16:01 nick Desired Action Updated
2019-06-10 08:54 agadmin Status Resolved => Closed
2019-08-29 16:09 eblake Relationship added related to 0000701


Mantis 1.1.6[^]
Copyright © 2000 - 2008 Mantis Group
Powered by Mantis Bugtracker