*BSD News Article 8588


Return to BSD News archive

Path: sserve!manuel.anu.edu.au!munnari.oz.au!news.hawaii.edu!ames!agate!dog.ee.lbl.gov!horse.ee.lbl.gov!torek
From: torek@horse.ee.lbl.gov (Chris Torek)
Newsgroups: comp.unix.bsd
Subject: Re: [386bsd] Patch setvbuf() to avoid core dump
Date: 4 Dec 1992 13:53:15 GMT
Organization: Lawrence Berkeley Laboratory, Berkeley CA
Lines: 391
Message-ID: <27804@dog.ee.lbl.gov>
References: <AGlYe7h00K@astral.msk.su>
NNTP-Posting-Host: 128.3.112.15

In article <AGlYe7h00K@astral.msk.su> ache@astral.msk.su writes:
>#include <stdio.h>
>
>main()
>{
>	setvbuf(stdout, NULL, _IOFBF, 10240);
>	putchar('a');
>}

It is not entirely clear to me, but this call is probably in violation
of the ANSI C standard.  The standard says that if the buffer pointer
is not NULL, it points to a buffer of at least the given size.  It does
not say that if it *is* NULL, the size should be honored; in fact, it
does not say anything at all about the size argument when the buffer
pointer is NULL.

Nonetheless, a patch is reasonable; existing software does in fact
call setvbuf with a NULL buffer and a nonzero size.  (This may be
due to misleading documentation; note that the patches below fix that
as well.)  I had earlier added

	if (buf == NULL)
		size = 0;

to setvbuf()---apparently this was after the 386bsd distribution---but
have been reconsidering the change for some time.  I have decided to
apply a similar patch, along with a change to the documentation.

>Manuals says, that NULL argument cause to malloc required buffer
>in the first read or write operation, but _really_ it don't
>malloc at all!

(Yes, because _bf._size is set, which means _w is nonzero, which means
__swbuf is never reached.)

>I look in stdio code and decide to malloc required buffer
>immediately in setvbuf function instead of first read/write
>operation, because of very complex code and very many places need
>to change in this case.

Actually, only two places would need changing.  All the allocation is
concentrated in __smakebuf.  You would have to arrange for _w to be 0
(easy enough to do in setvbuf itself) and---here is the hard part---
store the desired size somewhere in the FILE object.  (This requires
a change to stdio.h itself.)

But this is senseless.  User code is unlikely to call setvbuf with a
specific size without reason (and if it does, that is not the library's
fault).  Instead of deferring the allocation, we should just allocate
immediately as asked.  Your patch does this; mine does it slightly
differently....

(It might still be desirable to allocate immediately on a call to
setvbuf, but this would require some work on __smakebuf.  This may
still be needed, as I believe it is possible to open a tty, setvbuf it
to fully-buffered but with a size of 0, and still wind up with
line-buffered output.  It looks like __smakebuf should be split up a
bit.  Unfortunately, this may require doing an fstat in setvbuf.)

(Note, I made these diffs relative to what I expect most people have.
As you can see, the revision numbers jump by two here.)

*** /tmp/d05645	Fri Dec  4 05:35:40 1992
--- setvbuf.c	Fri Dec  4 05:34:54 1992
***************
*** 36,40 ****
  
  #if defined(LIBC_SCCS) && !defined(lint)
! static char sccsid[] = "@(#)setvbuf.c	5.2 (Berkeley) 2/1/91";
  #endif /* LIBC_SCCS and not lint */
  
--- 36,40 ----
  
  #if defined(LIBC_SCCS) && !defined(lint)
! static char sccsid[] = "@(#)setvbuf.c	5.4 (Berkeley) 12/4/92";
  #endif /* LIBC_SCCS and not lint */
  
***************
*** 53,56 ****
--- 53,57 ----
  	register size_t size;
  {
+ 	register int ret, flags;
  
  	/*
***************
*** 63,82 ****
  
  	/*
! 	 * Write current buffer, if any; drop read count, if any.
! 	 * Make sure putc() will not think fp is line buffered.
! 	 * Free old buffer if it was from malloc().  Clear line and
! 	 * non buffer flags, and clear malloc flag.
  	 */
  	(void) __sflush(fp);
  	fp->_r = 0;
  	fp->_lbfsize = 0;
! 	if (fp->_flags & __SMBF)
  		free((void *)fp->_bf._base);
! 	fp->_flags &= ~(__SLBF|__SNBF|__SMBF);
  
  	/*
! 	 * Now put back whichever flag is needed, and fix _lbfsize
! 	 * if line buffered.  Ensure output flush on exit if the
! 	 * stream will be buffered at all.
  	 */
  	switch (mode) {
--- 64,104 ----
  
  	/*
! 	 * OK so far.  Write current buffer, if any; drop read count, if
! 	 * any.  Make sure putc() will not think fp is line buffered.  Free
! 	 * old buffer if it was from malloc().  Clear line and non-buffer
! 	 * flags, and clear malloc flag.
  	 */
+ 	ret = 0;
  	(void) __sflush(fp);
  	fp->_r = 0;
  	fp->_lbfsize = 0;
! 	flags = fp->_flags;
! 	if (flags & __SMBF)
  		free((void *)fp->_bf._base);
! 	flags &= ~(__SLBF | __SNBF | __SMBF);
  
+ 	if (size == 0)
+ 		buf = NULL;	/* we will make a real one later */
+ 	else if (buf == NULL) {
+ 		/*
+ 		 * Caller wants specific buffering mode and size but did
+ 		 * not provide a buffer.  Produce one of the given size.
+ 		 * If that fails, set the size to 0 and continue, so that
+ 		 * we will try again later with a system-supplied size
+ 		 * (failure here is probably from someone with the bogus
+ 		 * idea that larger is always better, asking for many MB),
+ 		 * but return EOF to indicate failure.
+ 		 */
+ 		if ((buf = malloc(size)) == NULL) {
+ 			ret = EOF;
+ 			size = 0;
+ 		} else
+ 			flags |= __SMBF;
+ 	}
+ 
  	/*
! 	 * Now put back whichever flag is needed, and fix _lbfsize if line
! 	 * buffered.  Ensure output flush on exit if the stream will be
! 	 * buffered at all.
  	 */
  	switch (mode) {
***************
*** 83,87 ****
  
  	case _IONBF:
! 		fp->_flags |= __SNBF;
  		fp->_bf._base = fp->_p = fp->_nbuf;
  		fp->_bf._size = 1;
--- 105,109 ----
  
  	case _IONBF:
! 		flags |= __SNBF;
  		fp->_bf._base = fp->_p = fp->_nbuf;
  		fp->_bf._size = 1;
***************
*** 89,93 ****
  
  	case _IOLBF:
! 		fp->_flags |= __SLBF;
  		fp->_lbfsize = -size;
  		/* FALLTHROUGH */
--- 111,115 ----
  
  	case _IOLBF:
! 		flags |= __SLBF;
  		fp->_lbfsize = -size;
  		/* FALLTHROUGH */
***************
*** 104,110 ****
  	 * Patch up write count if necessary.
  	 */
! 	if (fp->_flags & __SWR)
! 		fp->_w = fp->_flags & (__SLBF|__SNBF) ? 0 : size;
  
! 	return (0);
  }
--- 126,133 ----
  	 * Patch up write count if necessary.
  	 */
! 	if (flags & __SWR)
! 		fp->_w = flags & (__SLBF | __SNBF) ? 0 : size;
! 	fp->_flags = flags;
  
! 	return (ret);
  }

*** /tmp/d05661	Fri Dec  4 05:37:09 1992
--- setbuf.3	Fri Dec  4 05:34:17 1992
***************
*** 2,5 ****
--- 2,9 ----
  .\" All rights reserved.
  .\"
+ .\" This code is derived from software contributed to Berkeley by
+ .\" the American National Standards Committee X3, on Information
+ .\" Processing Systems.
+ .\"
  .\" Redistribution and use in source and binary forms, with or without
  .\" modification, are permitted provided that the following conditions
***************
*** 30,36 ****
  .\" SUCH DAMAGE.
  .\"
! .\"     @(#)setbuf.3	6.9 (Berkeley) 4/19/91
  .\"
! .Dd April 19, 1991
  .Dt SETBUF 3
  .Os BSD 4
--- 34,40 ----
  .\" SUCH DAMAGE.
  .\"
! .\"     @(#)setbuf.3	6.11 (Berkeley) 12/4/92
  .\"
! .Dd December 4, 1992
  .Dt SETBUF 3
  .Os BSD 4
***************
*** 43,49 ****
  .Sh SYNOPSIS
  .Fd #include <stdio.h>
! .Ft int
  .Fn setbuf "FILE *stream" "char *buf"
! .Ft int
  .Fn setbuffer "FILE *stream" "char *buf" "size_t size"
  .Ft int
--- 47,53 ----
  .Sh SYNOPSIS
  .Fd #include <stdio.h>
! .Ft void
  .Fn setbuf "FILE *stream" "char *buf"
! .Ft void
  .Fn setbuffer "FILE *stream" "char *buf" "size_t size"
  .Ft int
***************
*** 65,68 ****
--- 69,73 ----
  (See 
  .Xr fclose 3 . )
+ .Pp
  Normally all files are block buffered.
  When the first
***************
*** 71,75 ****
  .Xr malloc 3
  is called,
! and a buffer is obtained.
  If a stream refers to a terminal
  (as
--- 76,80 ----
  .Xr malloc 3
  is called,
! and an optimally-sized buffer is obtained.
  If a stream refers to a terminal
  (as
***************
*** 83,88 ****
  .Fn setvbuf
  function
! may be used at any time on any open stream
! to change its buffer.
  The
  .Fa mode
--- 88,92 ----
  .Fn setvbuf
  function
! may be used to alter the buffering behavior of a stream.
  The
  .Fa mode
***************
*** 97,101 ****
  .El
  .Pp
! Except for unbuffered files, the 
  .Fa buf
  argument should point to a buffer at least
--- 101,110 ----
  .El
  .Pp
! The
! .Fa size
! parameter may be given as zero
! to obtain deferred optimal-size buffer allocation as usual.
! If it is not zero,
! then except for unbuffered files, the 
  .Fa buf
  argument should point to a buffer at least
***************
*** 103,131 ****
  bytes long;
  this buffer will be used instead of the current buffer.
! If the argument
  .Fa buf
! is NULL,
! only the mode is affected;
! a new buffer will be allocated on the next read or write operation.
  The
  .Fn setvbuf
! function
! may be used at any time,
! but can only change the mode of a stream
! when it is not ``active'':
! that is, before any
! .Tn I/O ,
! or immediately after a call to
! .Xr fflush .
  .Pp
! The other three calls are, in effect, simply aliases
! for calls to
  .Fn setvbuf .
! The
  .Fn setbuf
! function
! is exactly equivalent to the call
  .Pp
! .Dl setvbuf(stream, buf, buf ? _IOFBF : _IONBF, BUFSIZ);
  .Pp
  The
--- 112,147 ----
  bytes long;
  this buffer will be used instead of the current buffer.
! (If the
! .Fa size
! argument
! is not zero but
  .Fa buf
! is
! .Dv NULL ,
! a buffer of the given size will be allocated immediately,
! and released on close.
! This is an extension to ANSI C;
! portable code should use a size of 0 with any
! .Dv NULL
! buffer.)
! .Pp
  The
  .Fn setvbuf
! function may be used at any time,
! but may have peculiar side effects
! (such as discarding input or flushing output)
! if the stream is ``active''.
! Portable applications should call it only once on any given stream,
! and before any 
! .Tn I/O
! is performed.
  .Pp
! The other three calls are, in effect, simply aliases for calls to
  .Fn setvbuf .
! Except for the lack of a return value, the
  .Fn setbuf
! function is exactly equivalent to the call
  .Pp
! .Dl "setvbuf(stream, buf, buf ? _IOFBF : _IONBF, BUFSIZ);"
  .Pp
  The
***************
*** 140,144 ****
  is exactly equivalent to the call:
  .Pp
! .Dl setvbuf(stream, (char *)NULL, _IOLBF, 0);
  .Sh SEE ALSO
  .Xr fopen 3 ,
--- 156,173 ----
  is exactly equivalent to the call:
  .Pp
! .Dl "setvbuf(stream, (char *)NULL, _IOLBF, 0);"
! .Sh RETURN VALUES
! The
! .Fn setvbuf
! function returns 0 on success, or
! .Dv EOF
! if the request cannot be honored
! (note that the stream is still functional in this case).
! .Pp
! The
! .Fn setlinebuf
! function returns what the equivalent
! .Fn setvbuf
! would have returned.
  .Sh SEE ALSO
  .Xr fopen 3 ,
-- 
In-Real-Life: Chris Torek, Lawrence Berkeley Lab CSE/EE (+1 510 486 5427)
Berkeley, CA		Domain:	torek@ee.lbl.gov