diff options
author | Mark Johnston <markj@FreeBSD.org> | 2019-02-19 15:46:43 +0000 |
---|---|---|
committer | Mark Johnston <markj@FreeBSD.org> | 2019-02-19 15:46:43 +0000 |
commit | 18a7de663bae743c421c0fac8939e1fc99873b4b (patch) | |
tree | 0914d52498d780a8f0f146c563ed31d3d6ca0af2 /sys/kern/sys_pipe.c | |
parent | c9172fb4f18aa1d5a5b370f70eb3f01a90ff47e6 (diff) | |
download | src-18a7de663bae743c421c0fac8939e1fc99873b4b.tar.gz src-18a7de663bae743c421c0fac8939e1fc99873b4b.zip |
Move a racy assertion in filt_pipewrite().
EVFILT_WRITE knotes for pipes live on the knlist for the other end of the
pipe. Since they do not hold a reference on the corresponding file
structure, they may be removed from the knlist by pipeclose() while still
remaining active. In this case, there is no knlist lock acquired before
filt_pipewrite() is called, so the assertion fails.
Fix the problem by first checking whether that end of the pipe has been
closed. These checks are memory safe since the knote holds a reference
on one end of the pipe, and the pipe structure is not freed until both
ends are closed. The checks are not racy since PIPE_EOF is never cleared
after being set, and pipe_present is never set back to PIPE_ACTIVE after
pipeclose() has been called.
PR: 235640
Reported and tested by: pho
Reviewed by: kib
MFC after: 2 weeks
Sponsored by: The FreeBSD Foundation
Differential Revision: https://reviews.freebsd.org/D19224
Notes
Notes:
svn path=/head/; revision=344278
Diffstat (limited to 'sys/kern/sys_pipe.c')
-rw-r--r-- | sys/kern/sys_pipe.c | 8 |
1 files changed, 6 insertions, 2 deletions
diff --git a/sys/kern/sys_pipe.c b/sys/kern/sys_pipe.c index db5dfc68ae24..eafb902601d7 100644 --- a/sys/kern/sys_pipe.c +++ b/sys/kern/sys_pipe.c @@ -1741,15 +1741,19 @@ static int filt_pipewrite(struct knote *kn, long hint) { struct pipe *wpipe; - + + /* + * If this end of the pipe is closed, the knote was removed from the + * knlist and the list lock (i.e., the pipe lock) is therefore not held. + */ wpipe = kn->kn_hook; - PIPE_LOCK_ASSERT(wpipe, MA_OWNED); if (wpipe->pipe_present != PIPE_ACTIVE || (wpipe->pipe_state & PIPE_EOF)) { kn->kn_data = 0; kn->kn_flags |= EV_EOF; return (1); } + PIPE_LOCK_ASSERT(wpipe, MA_OWNED); kn->kn_data = (wpipe->pipe_buffer.size > 0) ? (wpipe->pipe_buffer.size - wpipe->pipe_buffer.cnt) : PIPE_BUF; if (wpipe->pipe_state & PIPE_DIRECTW) |