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