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) ***************