[comp.bugs.4bsd] fixes to 4.3 delivery of out of band data: fixes rlogin problems

hedrick@topaz.UUCP (03/14/87)

I have been annoyed by the fact that rlogin on our Suns gets the
terminal modes wrong now and then.  We are running the 4.3 TCP code,
and the problem turns out to be what I believe to be generic 4.3
problems with delivery of out of band data.  I am unable to verify
whether the problem is present in a vanilla 4.3 distribution, as we
still haven't gotten our tapes from Berkeley.  However the problems
are present on two different 4.3-based products, so I believe they are
generic.

The first bug is a race condition that can cause out of band data to
be ignored.  The original code computes a thing called "tomark", which
is the offset of the next out of band byte in the input stream.  As it
removes data from the stream, it adjusts tomark appropriately.  The
problem is that inside the loop, it does splx in order to do a
uiomove.  If a packet should arrive when the code is unprotected, out
of band data might be added to the input stream, changing so_oobmark.
The next time through the loop, tomark and so_oobmark will disagree,
and the new out of band data might be passed over.  I recommend
recomputing tomark each time through the loop.  Unfortunately, that
won't work for the case of MSG_PEEK.  I have chosen to ignore that
case, treating it as the old code did.  I'll leave that to Berkeley
and/or Sun to fix.  It doesn't cause any problem for any of our code.

The second bug causes SIGURG to be delivered at the wrong time.  This
causes rlogin to miss certain terminal mode changes.  The symptom on
our Suns is that you will go into Emacs and find that ^S stops output
instead of acting as a search command.  Rlogin has not put the
terminal into the right mode.  Rlogin requires that SIGURG be
delivered as soon as it is applicable.  If you read over out of band
data using a normal read, it will vanish.  (I.e. you will be EINVAL
when you try to access it.)  Thus SIGURG must trigger the processing
of out of band data, and must interrupt any ongoing normal read, so
that that read does not attempt to process the out of band data.
Unfortunately, there are cases where this doesn't work.  Suppose that
rlogin has started executing the read system call, and a packet
arrives early in the code for that call.  tcp_input will notice the
urgent data and schedule a SIGURG.  However because the process is not
sleeping, the system call is not interrupted.  If some input data
arrives after the out of band data, soreceive will not do the sleep.
It will clear the ATMARK condition and return the data, causing the
out of band data to be ignored.  The fix I have adopted may not be a
very elegant one.  I have caused soreceive to return if there is a
signal waiting to be processed.  Note that two different tests are
needed.  The first time through the loop, it must set EINTR, since
otherwise returning no data can confuse certain programs.  However if
this condition occurs at a later iteration, data has been returned,
and error should not be set.


*** uipc_socket.c.ORIG	Thu Sep 25 09:18:58 1986
--- uipc_socket.c	Sat Mar 14 13:23:39 1987
***************
*** 415,420 ****
--- 415,421 ----
  	register int len, error = 0, s, eor, tomark;
  	struct protosw *pr = so->so_proto;
  	int moff;
+ 	register struct proc *p = u.u_procp;
  
  	if (rightsp)
  		*rightsp = 0;
***************
*** 527,534 ****
  	}
  	eor = 0;
  	moff = 0;
! 	tomark = so->so_oobmark;
! 	do {
  		if (uio->uio_resid <= 0)
  			break;
  		len = uio->uio_resid;
--- 528,542 ----
  	}
  	eor = 0;
  	moff = 0;
! 	if (flags & MSG_PEEK)
! 	  tomark = so->so_oobmark;
! 	if ((p)->p_sig && ((p)->p_flag&STRC ||
! 			   ((p)->p_sig &~ 
! 			    ((p)->p_sigignore | (p)->p_sigmask))))
! 	  error = EINTR;
! 	else do {
! 		if ((flags & MSG_PEEK) == 0)
! 	    	  tomark = so->so_oobmark;
  		if (uio->uio_resid <= 0)
  			break;
  		len = uio->uio_resid;
***************
*** 573,579 ****
  			if (tomark == 0)
  				break;
  		}
! 	} while (m && error == 0 && !eor);
  	if (flags & MSG_PEEK)
  		goto release;
  	if ((so->so_proto->pr_flags & PR_ATOMIC) && eor == 0)
--- 581,590 ----
  			if (tomark == 0)
  				break;
  		}
! 	} while (m && error == 0 && !eor && !
! 		   ((p)->p_sig && ((p)->p_flag&STRC ||
! 				   ((p)->p_sig &~ 
! 				    ((p)->p_sigignore | (p)->p_sigmask)))));
  	if (flags & MSG_PEEK)
  		goto release;
  	if ((so->so_proto->pr_flags & PR_ATOMIC) && eor == 0)
***************