From 1cbfe73da57072f86d633d65f8821651afa936e9 Mon Sep 17 00:00:00 2001 From: Mark Johnston Date: Mon, 11 Nov 2019 20:44:30 +0000 Subject: [PATCH] Fix handling of PIPE_EOF in the direct write path. Suppose a writing thread has pinned its pages and gone to sleep with pipe_map.cnt > 0. Suppose that the thread is woken up by a signal (so error != 0) and the other end of the pipe has simultaneously been closed. In this case, to satisfy the assertion about pipe_map.cnt in pipe_destroy_write_buffer(), we must mark the buffer as empty. Reported by: syzbot+5cce271bf2cb1b1e1876@syzkaller.appspotmail.com Reviewed by: kib Tested by: pho MFC after: 1 week Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D22261 --- sys/kern/sys_pipe.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/sys/kern/sys_pipe.c b/sys/kern/sys_pipe.c index df0d027502c9..5ef0a96635e6 100644 --- a/sys/kern/sys_pipe.c +++ b/sys/kern/sys_pipe.c @@ -970,15 +970,8 @@ pipe_direct_write(struct pipe *wpipe, struct uio *uio) goto error1; } - while (wpipe->pipe_map.cnt != 0) { - if (wpipe->pipe_state & PIPE_EOF) { - wpipe->pipe_map.cnt = 0; - pipe_destroy_write_buffer(wpipe); - pipeselwakeup(wpipe); - pipeunlock(wpipe); - error = EPIPE; - goto error1; - } + while (wpipe->pipe_map.cnt != 0 && + (wpipe->pipe_state & PIPE_EOF) == 0) { if (wpipe->pipe_state & PIPE_WANTR) { wpipe->pipe_state &= ~PIPE_WANTR; wakeup(wpipe); @@ -993,12 +986,16 @@ pipe_direct_write(struct pipe *wpipe, struct uio *uio) break; } - if (wpipe->pipe_state & PIPE_EOF) - error = EPIPE; - if (error == EINTR || error == ERESTART) - pipe_clone_write_buffer(wpipe); - else + if ((wpipe->pipe_state & PIPE_EOF) != 0) { + wpipe->pipe_map.cnt = 0; pipe_destroy_write_buffer(wpipe); + pipeselwakeup(wpipe); + error = EPIPE; + } else if (error == EINTR || error == ERESTART) { + pipe_clone_write_buffer(wpipe); + } else { + pipe_destroy_write_buffer(wpipe); + } pipeunlock(wpipe); KASSERT((wpipe->pipe_state & PIPE_DIRECTW) == 0, ("pipe %p leaked PIPE_DIRECTW", wpipe));