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
This commit is contained in:
Mark Johnston 2019-11-11 20:44:30 +00:00
parent 7aff07d914
commit 1cbfe73da5
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=354629

View File

@ -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));