[net.bugs.usg] Bug with multiple stdio streams on one descriptor, FIX

bruce@stride.UUCP (Bruce Robertson) (03/26/86)

Description:
	There is a bug in Standard I/O for System V Release 2 Version 2.  If
	you have more than one FILE stream open on a single file descriptor,
	bad things to your malloc() heap space happen.

	Throughout the stdio code, the end of the buffer associated with
	a stream is referenced by the macro _bufend().  This macro is
	defined in /usr/include/stdio.h, and references a table of pointers
	the end of the buffers.  The problem is, the table is referenced
	like this:

		_bufendtab[(p)->_file]

	where `p' is your FILE pointer.  This is wrong.  What you really
	want to do is reference the table like this:

		_bufendtab[(p) - &_iob[0]]

	which associates slots in the table of pointers with slots in
	the table of _iob structures.

Repeat-By:
	int fd;
	FILE *fp1, *fp2;

	fd = open("/dev/tty", 2);
	fp1 = fdopen(fd, "r");
	fp2 = fdopen(fd, "w");

	Subsequent fputs() calls to `fp2' will cause random areas of
	memory to be modified, because the data structures that describe
	the length of the buffer have been clobbered.

Fix:
	Two fixes - one to /usr/include/stdio.h, and another to
	/usr/src/lib/libc/port/print/doprnt.c, which also references
	the table incorrectly, without using the macro.  Note that
	after making the change to /usr/include/stdio.h, you must
	recompile everything in /usr/src/lib/libc/port/stdio.


*** /usr/include/stdio.h-old	Mon Jul  2 19:32:20 1984
--- /usr/include/stdio.h	Tue Mar  4 22:14:47 1986
***************
*** 54,60
  #define stdout		(&_iob[1])
  #define stderr		(&_iob[2])
  
! #define _bufend(p)	_bufendtab[(p)->_file]
  #define _bufsiz(p)	(_bufend(p) - (p)->_base)
  
  #ifndef lint

--- 54,60 -----
  #define stdout		(&_iob[1])
  #define stderr		(&_iob[2])
  
! #define _bufend(p)	_bufendtab[(p) - &_iob[0]]
  #define _bufsiz(p)	(_bufend(p) - (p)->_base)
  
  #ifndef lint


*** /usr/src/lib/libc/port/print/doprnt.c-old	Tue Mar 25 21:26:41 1986
--- /usr/src/lib/libc/port/print/doprnt.c	Tue Mar 25 21:26:43 1986
***************
*** 159,165
  	bufptr = iop->_ptr;
  	bufferend = (fno == _NFILE) ? 
  			(unsigned char *)((long) bufptr | (-1L & ~HIBITL))
! 				 : _bufendtab[fno];
  	}
  
  	/*

--- 159,165 -----
  	bufptr = iop->_ptr;
  	bufferend = (fno == _NFILE) ? 
  			(unsigned char *)((long) bufptr | (-1L & ~HIBITL))
! 				 : _bufend(iop);
  	}
  
  	/*
-- 

	Bruce Robertson
	UUCP: cbosgd!utah-cs!utah-gr!stride!bruce
	ARPA: stride!bruce@utah-gr.arpa

kwh@bentley.UUCP (03/28/86)

In article <562@stride.stride.UUCP> stride!bruce (Bruce Robertson) writes:
>The problem is, the table is referenced like this:
>	_bufendtab[(p)->_file]
>where `p' is your FILE pointer.  This is wrong.  What you really
>want to do is reference the table like this:
>	_bufendtab[(p) - &_iob[0]]
>which associates slots in the table of pointers with slots in
>the table of _iob structures.

This assumes that all objects of type FILE are in the _iob[] array.
This is not necessarily the case; e.g. there is an auto variable of type
FILE (not FILE*) in sprintf(); there may be others.

Are you sure that having two streams on one descriptor is legal?  Is there
any documentation that explicitly permits or forbids it?

Now, the *real* question.  Why is _bufendtab[] in a separate array, rather
than being another field in the FILE structure where it seems to belong?

Karl W. Z. Heuer (ihnp4!bentley!kwh), The Walking Lint