aboutsummaryrefslogtreecommitdiff
path: root/sys/kern/sys_pipe.c
diff options
context:
space:
mode:
authorMark Johnston <markj@FreeBSD.org>2019-02-19 15:46:43 +0000
committerMark Johnston <markj@FreeBSD.org>2019-02-19 15:46:43 +0000
commit18a7de663bae743c421c0fac8939e1fc99873b4b (patch)
tree0914d52498d780a8f0f146c563ed31d3d6ca0af2 /sys/kern/sys_pipe.c
parentc9172fb4f18aa1d5a5b370f70eb3f01a90ff47e6 (diff)
downloadsrc-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.c8
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)