diff options
author | tegge <tegge@FreeBSD.org> | 1999-12-13 02:55:47 +0000 |
---|---|---|
committer | tegge <tegge@FreeBSD.org> | 1999-12-13 02:55:47 +0000 |
commit | 77880079dd5ecc78686242d8a7540eba3109af3a (patch) | |
tree | 428354bc74f03bed3277b13aaad645cc1fbb6c5f | |
parent | 8a21e37554bb51157840405b76b020bfbd5b442b (diff) | |
download | FreeBSD-src-77880079dd5ecc78686242d8a7540eba3109af3a.zip FreeBSD-src-77880079dd5ecc78686242d8a7540eba3109af3a.tar.gz |
Fix two problems with pipe_write():
1. Data written beyond end of pipe buffer, causing kernel memory corruption.
- Check that space is still valid after obtaining the pipe lock.
- Defer the calculation of transfer size until the pipe
lock has been obtained.
- Update the pipe buffer pointers while holding the pipe lock.
2. Writes of size <= PIPE_BUF not always atomic.
- Allow an internal write to span two contiguous segments,
so writes of size <= PIPE_BUF can be kept atomic
when wrapping around from the end to the start of the
pipe buffer.
PR: 15235
Reviewed by: Matt Dillon <dillon@FreeBSD.org>
-rw-r--r-- | sys/kern/sys_pipe.c | 87 |
1 files changed, 64 insertions, 23 deletions
diff --git a/sys/kern/sys_pipe.c b/sys/kern/sys_pipe.c index 95f5140..de2399e 100644 --- a/sys/kern/sys_pipe.c +++ b/sys/kern/sys_pipe.c @@ -799,27 +799,13 @@ pipe_write(fp, uio, cred, flags, p) space = wpipe->pipe_buffer.size - wpipe->pipe_buffer.cnt; /* Writes of size <= PIPE_BUF must be atomic. */ - /* XXX perhaps they need to be contiguous to be atomic? */ if ((space < uio->uio_resid) && (orig_resid <= PIPE_BUF)) space = 0; if (space > 0 && (wpipe->pipe_buffer.cnt < PIPE_SIZE)) { - /* - * This set the maximum transfer as a segment of - * the buffer. - */ - int size = wpipe->pipe_buffer.size - wpipe->pipe_buffer.in; - /* - * space is the size left in the buffer - */ - if (size > space) - size = space; - /* - * now limit it to the size of the uio transfer - */ - if (size > uio->uio_resid) - size = uio->uio_resid; if ((error = pipelock(wpipe,1)) == 0) { + int size; /* Transfer size */ + int segsize; /* first segment to transfer */ /* * It is possible for a direct write to * slip in on us... handle it here... @@ -828,18 +814,73 @@ pipe_write(fp, uio, cred, flags, p) pipeunlock(wpipe); goto retrywrite; } - error = uiomove( &wpipe->pipe_buffer.buffer[wpipe->pipe_buffer.in], - size, uio); + /* + * If a process blocked in uiomove, our + * value for space might be bad. + */ + if (space > wpipe->pipe_buffer.size - + wpipe->pipe_buffer.cnt) { + pipeunlock(wpipe); + goto retrywrite; + } + + /* + * Transfer size is minimum of uio transfer + * and free space in pipe buffer. + */ + if (space > uio->uio_resid) + size = uio->uio_resid; + else + size = space; + /* + * First segment to transfer is minimum of + * transfer size and contiguous space in + * pipe buffer. If first segment to transfer + * is less than the transfer size, we've got + * a wraparound in the buffer. + */ + segsize = wpipe->pipe_buffer.size - + wpipe->pipe_buffer.in; + if (segsize > size) + segsize = size; + + /* Transfer first segment */ + + error = uiomove(&wpipe->pipe_buffer.buffer[wpipe->pipe_buffer.in], + segsize, uio); + + if (error == 0 && segsize < size) { + /* + * Transfer remaining part now, to + * support atomic writes. Wraparound + * happened. + */ + if (wpipe->pipe_buffer.in + segsize != + wpipe->pipe_buffer.size) + panic("Expected pipe buffer wraparound disappeared"); + + error = uiomove(&wpipe->pipe_buffer.buffer[0], + size - segsize, uio); + } + if (error == 0) { + wpipe->pipe_buffer.in += size; + if (wpipe->pipe_buffer.in >= + wpipe->pipe_buffer.size) { + if (wpipe->pipe_buffer.in != size - segsize + wpipe->pipe_buffer.size) + panic("Expected wraparound bad"); + wpipe->pipe_buffer.in = size - segsize; + } + + wpipe->pipe_buffer.cnt += size; + if (wpipe->pipe_buffer.cnt > wpipe->pipe_buffer.size) + panic("Pipe buffer overflow"); + + } pipeunlock(wpipe); } if (error) break; - wpipe->pipe_buffer.in += size; - if (wpipe->pipe_buffer.in >= wpipe->pipe_buffer.size) - wpipe->pipe_buffer.in = 0; - - wpipe->pipe_buffer.cnt += size; } else { /* * If the "read-side" has been blocked, wake it up now. |