From tobez@plab.ku.dk Wed Jul 28 08:42:03 1999
Return-Path: <tobez@plab.ku.dk>
Received: from plab.ku.dk (plab.ku.dk [130.225.105.65])
	by hub.freebsd.org (Postfix) with ESMTP id CD83C154A9
	for <FreeBSD-gnats-submit@freebsd.org>; Wed, 28 Jul 1999 08:41:43 -0700 (PDT)
	(envelope-from tobez@plab.ku.dk)
Received: from lion.plab.ku.dk (lion.plab.ku.dk [130.225.105.49])
	by plab.ku.dk (8.9.1/8.9.1) with ESMTP id RAA18465
	for <FreeBSD-gnats-submit@freebsd.org>; Wed, 28 Jul 1999 17:41:24 +0200 (CEST)
Received: (from tobez@localhost)
	by lion.plab.ku.dk (8.9.3/8.9.3) id RAA68304;
	Wed, 28 Jul 1999 17:38:59 +0200 (CEST)
	(envelope-from tobez)
Message-Id: <199907281538.RAA68304@lion.plab.ku.dk>
Date: Wed, 28 Jul 1999 17:38:59 +0200 (CEST)
From: tobez@plab.ku.dk
Reply-To: tobez@plab.ku.dk
To: FreeBSD-gnats-submit@freebsd.org
Subject: Non-standard behavior of fread(3)
X-Send-Pr-Version: 3.2

>Number:         12852
>Category:       bin
>Synopsis:       Non-standard behavior of fread(3)
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    rnordier
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Wed Jul 28 08:50:01 PDT 1999
>Closed-Date:    Tue Aug 10 14:39:16 PDT 1999
>Last-Modified:  Tue Aug 10 14:40:41 PDT 1999
>Originator:     Anton Berezin <tobez@plab.ku.dk>
>Release:        FreeBSD 4.0-CURRENT i386
>Organization:
The Protein Laboratory, University of Copenhagen
>Environment:

This is so on 2.2.8, 3.0, 3.1, 3.2 and 4.0, at least. :-)

>Description:

The following little program:

	 #include <stdio.h>
	 #include <errno.h>
	 int               
	 main(int argc, char **argv)
	 {
	    char buf;
	    int r = fread(&buf,1,1,stdout);
	    printf("%d ferror=%d feof=%d errno=%d\n",
		   r, ferror(stdout), feof(stdout), errno);
	    return 0;
	 }            

produces this output:

	0 ferror=0 feof=0 errno=9

I am not exactly sure that this is a bug, strictly speaking.  However,
there are certain indications that this behavior is non-standard.  Let
me explain this a bit.  The issue was raised when perl5 developers have
added a specific test in the most recent developer's version of perl
(5.005_58, to be precise).  It quickly turned out that libc on different
platforms produce very different results.  To my knowledge, several
versions of Linux's glibc, as well as NetBSD libc and OSF1 4.0 libc do
the same thing as we do.  It was reported on p5p mailing list that many
other systems behave differently.  For example, the program above, being
run on HP-UX B.10.20, produce

	0 ferror=32 feof=0 errno=9

Gurusamy Sarathy communicated this with glibc developers, and they
decided to change the behavior of glibc in future versions.  The reason
was (I quote only relevant part of the message Sarathy forwarded to
p5p):

--- quote start ---
   Message-Id: <u8r9m23tin.fsf@arthur.rhein-neckar.de>
   Date:    21 Jul 1999 17:35:44 +0200                
   From:    Andreas Jaeger <aj@arthur.rhein-neckar.de>
   To:      libc-alpha Mailinglist <libc-alpha@sourceware.cygnus.com>,                     
	    gsar@activestate.com                                                           
   Subject: Re: [Gurusamy Sarathy <gsar@activestate.com>] ferror() after fread() on a FILE* 
	***opened for write                                                                 

   [snipped by tobez]

   The ISO C9x draft I've got here, mentions as return value for fread:

	 [#3] The fread  function  returns  the  number  of  elements
	  successfully  read,  which  may be less than nmemb if a read
	  error or end-of-file is encountered.  If size  or  nmemb  is
	  zero,  fread  returns zero and the contents of the array and
	  the state of the stream remain unchanged.

   fread returned 0 which is less than 1 - therefore either a read error
   or end-of-file is encountered.  But feof and ferror tell me that
   neither is encountered.

   [snipped by tobez]
--- quote end ---

This interpretation is also consistent with FreeBSD's man 3 fread:

   If an error occurs, or the end-of-file is reached, the return value
   is a short object count (or zero).

So I think that it might be a good idea to change the behavior of our
libc, too.

In the Fix section I provide a patch which changes the behavior of
fread() to set __SERR together with setting EBADF.

>How-To-Repeat:

Compile and run the program from the Description section.

>Fix:
	
--- /usr/src/lib/libc/stdio/refill.c.orig	Wed Jul 28 17:30:44 1999
+++ /usr/src/lib/libc/stdio/refill.c	Wed Jul 28 17:31:40 1999
@@ -82,6 +82,7 @@
 	if ((fp->_flags & __SRD) == 0) {
 		if ((fp->_flags & __SRW) == 0) {
 			errno = EBADF;
+			fp->_flags |= __SERR;
 			return (EOF);
 		}
 		/* switch to reading */




>Release-Note:
>Audit-Trail:
Responsible-Changed-From-To: freebsd-bugs->rnordier 
Responsible-Changed-By: rnordier 
Responsible-Changed-When: Wed Jul 28 10:54:47 PDT 1999 
Responsible-Changed-Why:  
I'll handle this. 

From: Chris Torek <torek@BSDI.COM>
To: freebsd-gnats-submit@freebsd.org, tobez@plab.ku.dk
Cc:  
Subject: Re: bin/12852: 20Non-standard 20behavior of fread(3)
Date: Sat, 7 Aug 1999 14:55:07 -0600 (MDT)

 As the author of the original code, I just want to add that,
 while the change seems reasonable, the effect of attempting to
 read from a write-only stream (such as stdout) is undefined.
 It would be legal (from an ANSI C standpoint) to call abort()
 here, for instance, or to write messages to stderr, or randomly
 corrupt memory, etc.
 
 In other words, this is not a bug in stdio, it is a bug in
 the program misusing stdio.  What stdio should do about such
 programs is the real question.
 
 (The same issue applies to writing to read-only streams.)
 
 Chris
 

From: Robert Nordier <rnordier@nordier.com>
To: torek@BSDI.COM (Chris Torek)
Cc: freebsd-gnats-submit@freebsd.org
Subject: Re: bin/12852: 20Non-standard 20behavior of fread(3)
Date: Sun, 8 Aug 1999 10:48:35 +0200 (SAST)

 >  As the author of the original code, I just want to add that,
 >  while the change seems reasonable, the effect of attempting to
 >  read from a write-only stream (such as stdout) is undefined.
 >  It would be legal (from an ANSI C standpoint) to call abort()
 >  here, for instance, or to write messages to stderr, or randomly
 >  corrupt memory, etc.
 
 What wording in the standard are you relying on, or just omission
 of any explicit definition of behaviour?
 
 >  In other words, this is not a bug in stdio, it is a bug in
 >  the program misusing stdio.  What stdio should do about such
 >  programs is the real question.
 
 The concept that, where a stdio I/O function returns EOF, one of
 the feof and ferror will always return nonzero is well-established
 in the standard.  Leaving aside the special case of mixed I/O
 operations on an update stream without fflush or a file positioning
 function intervening, the desired behaviour seems somewhat obvious.
 
 As an example of interpretation -- while this can naturally not be
 regarded as stipulative -- one of the "cursory test programs"
 (conform/tstdio2.c) which accompany the Plauger implementation of
 the Standard C library make a special case of this:
 
     pf = fopen(tname, "w");
     [ ... ]
     assert(fgetc(pf) == EOF && feof(pf) == 0 && ferror(pf) != 0);
 
 --
 Robert Nordier
 

From: Chris Torek <torek@BSDI.COM>
To: rnordier@nordier.com
Cc: freebsd-gnats-submit@freebsd.org
Subject: Re: bin/12852: 20Non-standard 20behavior of fread(3)
Date: Sun, 8 Aug 1999 03:06:50 -0600 (MDT)

 >What wording in the standard are you relying on, or just omission
 >of any explicit definition of behaviour?
 
 The latter.  4.9.3, p. 127, ll. 9--11, say that:
 
 	All input takes place as if characters were read by
 	successive calls to the |fgetc| function; all output
 	takes place as if characters were written by
 	successive calls to the |fputc| function.
 
 In 4.9.7.1 and 4.9.7.3, fgetc and fputc are described in
 terms of their effects on "input streams" and "output streams"
 respectively, without mentioning what happens if fgetc is applied
 to an output stream, or fputc to an input stream.  Nothing else
 seems to say anything one way or another about "mismatched"
 operations, so it would seem to fall to 1.6.
 
 (All numbering is from the original ANSI edition.)
 
 Since my stdio takes pains (that other stdios do not) to handle
 mixed fgetc and fputc "correctly" even without an intervening
 fseek/fflush/rewind/etc. call, it does seem as though it should
 handle fgetc-on-output-only-stream "correctly" as well, whatever
 that means. :-)  (Other stdios have a single "count" field, so
 that if you do:
 
 	FILE *fp = fopen("foo", "w");
 	assert(fp != NULL);
 	printf("putc returns %d\n", putc(97, fp));
 	printf("getc returns %d\n", getc(fp));
 
 the first one returns 97 and the second typically returns 0.  The
 putc sets fp->_cnt, or whatever it is named, to something like
 8191, and the subsequent getc winds up doing:
 
 	--fp->_cnt >= 0 ? *fp->_ptr++ : __fill(fp)
 
 which reduces to:
 
 	--fp->_cnt, *fp->_ptr++
 
 which reads from an uninitialized byte in the malloc'ed buffer
 associated with file "foo".)
 
 >As an example of interpretation -- while this can naturally not be
 >regarded as stipulative -- one of the "cursory test programs"
 >(conform/tstdio2.c) which accompany the Plauger implementation of
 >the Standard C library make a special case of this:
 >    pf = fopen(tname, "w");
 >    [ ... ]
 >    assert(fgetc(pf) == EOF && feof(pf) == 0 && ferror(pf) != 0);
 
 I believe this particular test will fail on many systems if you
 insert an fputc first (as described above).  (It is possible
 that f{ge,pu}tc will check the mode, while {ge,pu}tc will not,
 and of course all I have ever seen is the #defines in stdio.h,
 not the actual code for the function variants.  Might be interesting
 to test, though.)
 
 Chris
 
State-Changed-From-To: open->closed 
State-Changed-By: rnordier 
State-Changed-When: Tue Aug 10 14:39:16 PDT 1999 
State-Changed-Why:  
Patch applied: src/lib/libc/stdio/refill.c rev. 1.7. 
>Unformatted:
