keith@reed.UUCP (06/29/87)
DESCRIPTION:
And I thought my lisp interpreter was at fault! The snazzy new 4.3 putc
macro is supposed to speed up output to _IOLBF descriptors, and it uses the
fields in _iob in non-standard ways. More specifically, it uses -(_cnt) to
count the number of characters in the buffer and doesn't _flsbuf until
-(_cnt) == _bufsiz (or putc'ing a \n).
A cute idea, but _doprnt doesn't agree. _doprnt *always* sets the _cnt
field to 0 on an _IOLBF descriptor. This is not normally a problem unless
the buffer is filled up (i.e. sent no \n). In this case, putc merrily
tromps through memory past the end of the buffer.
REPEAT BY:
execute this program and observe the abort().
/*
* demonstrates a bug in _IOLBF mode between putc and _doprnt
* in Mt. Xinu's 4.3BSD for VAX 11/780
*/
# include <stdio.h>
main ()
{
FILE *test;
test = fopen ("/dev/null", "w");
setlinebuf (test);
/*
* here's the magic: _doprnt doesn't decrement
* test->_cnt by the number of characters printed
* if IOLBF is set. putc is expecting it to and
* steps beyond the end of the buffer 7 times
*/
fprintf (test, " hello ");
for (;;) {
putc ('*', test);
/*
* a test to see if _ptr is magically beyond
* the end of the buffer. This can *never*
* happen :-)
*/
if (test->_ptr >= test->_base + test->_bufsiz) {
printf ("test->_ptr: 0x%x\n", test->_ptr);
printf ("test->_base: 0x%x\n", test->_base);
printf ("test->_bufsiz: 0x%x\n", test->_bufsiz);
abort ();
}
}
}
FIX:
I'd love to fix this inside _doprnt, but _doprnt is rather adamant about
having _cnt always >= 0. Fortunately, _doprnt returns the number of
characters printed, and so fprintf and printf can set up the FILE for doprnt
and restore it to correctness on return:
*** /usr/src/lib/libc/stdio/fprintf.c Sun Mar 9 20:50:32 1986
--- fprintf.c Sun Jun 28 18:08:30 1987
***************
*** 26,31 ****
--- 26,38 ----
iop->_base = NULL;
iop->_bufsiz = NULL;
iop->_cnt = 0;
+ } else if (iop->_flag & _IOLBF) {
+ int old_cnt;
+
+ old_cnt = iop->_cnt;
+ iop->_cnt = 0;
+ old_cnt -= _doprnt(fmt, &args, iop);
+ iop->_cnt = old_cnt;
} else
_doprnt(fmt, &args, iop);
return(ferror(iop)? EOF: 0);
*** /usr/src/lib/libc/stdio/printf.c Sun Mar 9 20:52:47 1986
--- printf.c Sun Jun 28 18:09:14 1987
***************
*** 7,12 ****
printf(fmt, args)
char *fmt;
{
! _doprnt(fmt, &args, stdout);
return(ferror(stdout)? EOF: 0);
}
--- 7,20 ----
printf(fmt, args)
char *fmt;
{
! if (stdout->_flag & _IOLBF) {
! int old_cnt;
!
! old_cnt = stdout->_cnt;
! stdout->_cnt = 0;
! old_cnt -= _doprnt(fmt, &args, stdout);
! stdout->_cnt = old_cnt;
! } else
! _doprnt(fmt, &args, stdout);
return(ferror(stdout)? EOF: 0);
}
----------------------------------------------
Keith Packard
tektronix!reed!keith