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