Fix race in pipe read code whereby a blocked lock can allow another

process to sneak in and write to or close the pipe.  The read code
    enters a 'piperd' state after doing the lock operation without
    checking to see if the state changed, which can cause the process
    to wait forever.

    The code has also been documented more.
This commit is contained in:
Matthew Dillon 1999-02-04 23:50:49 +00:00
parent 38ebe5624f
commit e198079da2
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=43623

View File

@ -16,7 +16,7 @@
* 4. Modifications may be freely made to this file if the above conditions
* are met.
*
* $Id: sys_pipe.c,v 1.48 1999/01/27 21:49:57 dillon Exp $
* $Id: sys_pipe.c,v 1.49 1999/01/28 00:57:47 dillon Exp $
*/
/*
@ -380,32 +380,15 @@ pipe_read(fp, uio, cred)
}
#endif
} else {
/*
* detect EOF condition
*/
if (rpipe->pipe_state & PIPE_EOF) {
/* XXX error = ? */
break;
}
/*
* If the "write-side" has been blocked, wake it up now.
*/
if (rpipe->pipe_state & PIPE_WANTW) {
rpipe->pipe_state &= ~PIPE_WANTW;
wakeup(rpipe);
}
if (nread > 0)
break;
if (fp->f_flag & FNONBLOCK) {
error = EAGAIN;
break;
}
/*
* If there is no more to read in the pipe, reset
* its pointers to the beginning. This improves
* cache hit stats.
*
* We get this over with now because it may block
* and cause the state to change out from under us,
* rather then have to re-test the state both before
* and after this fragment.
*/
if ((error = pipelock(rpipe,1)) == 0) {
@ -414,15 +397,51 @@ pipe_read(fp, uio, cred)
rpipe->pipe_buffer.out = 0;
}
pipeunlock(rpipe);
} else {
/*
* If pipe filled up due to pipelock
* blocking, loop back up.
*/
if (rpipe->pipe_buffer.cnt > 0)
continue;
}
/*
* detect EOF condition
*/
if (rpipe->pipe_state & PIPE_EOF) {
/* XXX error = ? */
break;
}
/*
* If the "write-side" has been blocked, wake it up now.
*/
if (rpipe->pipe_state & PIPE_WANTW) {
rpipe->pipe_state &= ~PIPE_WANTW;
wakeup(rpipe);
}
/*
* break if error (signal via pipelock), or if some
* data was read
*/
if (error || nread > 0)
break;
/*
* Handle non-blocking mode operation
*/
if (fp->f_flag & FNONBLOCK) {
error = EAGAIN;
break;
}
/*
* Wait for more data
*/
rpipe->pipe_state |= PIPE_WANTR;
if ((error = tsleep(rpipe, PRIBIO|PCATCH, "piperd", 0)) != 0) {
break;