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