diff options
author | Jann Horn <jannh@google.com> | 2019-01-23 09:19:17 -0500 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2019-02-01 02:01:45 -0500 |
commit | a0ce2f0aa6ad97c3d4927bf2ca54bcebdf062d55 (patch) | |
tree | 69fa826572391147bebfa35abc51dd3a326b3e6a /fs/pipe.c | |
parent | 801e523796004a3aaeae7dfc83fe81888f055287 (diff) |
splice: don't merge into linked buffers
Before this patch, it was possible for two pipes to affect each other after
data had been transferred between them with tee():
============
$ cat tee_test.c
int main(void) {
int pipe_a[2];
if (pipe(pipe_a)) err(1, "pipe");
int pipe_b[2];
if (pipe(pipe_b)) err(1, "pipe");
if (write(pipe_a[1], "abcd", 4) != 4) err(1, "write");
if (tee(pipe_a[0], pipe_b[1], 2, 0) != 2) err(1, "tee");
if (write(pipe_b[1], "xx", 2) != 2) err(1, "write");
char buf[5];
if (read(pipe_a[0], buf, 4) != 4) err(1, "read");
buf[4] = 0;
printf("got back: '%s'\n", buf);
}
$ gcc -o tee_test tee_test.c
$ ./tee_test
got back: 'abxx'
$
============
As suggested by Al Viro, fix it by creating a separate type for
non-mergeable pipe buffers, then changing the types of buffers in
splice_pipe_to_pipe() and link_pipe().
Cc: <stable@vger.kernel.org>
Fixes: 7c77f0b3f920 ("splice: implement pipe to pipe splicing")
Fixes: 70524490ee2e ("[PATCH] splice: add support for sys_tee()")
Suggested-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Jann Horn <jannh@google.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/pipe.c')
-rw-r--r-- | fs/pipe.c | 14 |
1 files changed, 14 insertions, 0 deletions
@@ -234,6 +234,14 @@ static const struct pipe_buf_operations anon_pipe_buf_ops = { | |||
234 | .get = generic_pipe_buf_get, | 234 | .get = generic_pipe_buf_get, |
235 | }; | 235 | }; |
236 | 236 | ||
237 | static const struct pipe_buf_operations anon_pipe_buf_nomerge_ops = { | ||
238 | .can_merge = 0, | ||
239 | .confirm = generic_pipe_buf_confirm, | ||
240 | .release = anon_pipe_buf_release, | ||
241 | .steal = anon_pipe_buf_steal, | ||
242 | .get = generic_pipe_buf_get, | ||
243 | }; | ||
244 | |||
237 | static const struct pipe_buf_operations packet_pipe_buf_ops = { | 245 | static const struct pipe_buf_operations packet_pipe_buf_ops = { |
238 | .can_merge = 0, | 246 | .can_merge = 0, |
239 | .confirm = generic_pipe_buf_confirm, | 247 | .confirm = generic_pipe_buf_confirm, |
@@ -242,6 +250,12 @@ static const struct pipe_buf_operations packet_pipe_buf_ops = { | |||
242 | .get = generic_pipe_buf_get, | 250 | .get = generic_pipe_buf_get, |
243 | }; | 251 | }; |
244 | 252 | ||
253 | void pipe_buf_mark_unmergeable(struct pipe_buffer *buf) | ||
254 | { | ||
255 | if (buf->ops == &anon_pipe_buf_ops) | ||
256 | buf->ops = &anon_pipe_buf_nomerge_ops; | ||
257 | } | ||
258 | |||
245 | static ssize_t | 259 | static ssize_t |
246 | pipe_read(struct kiocb *iocb, struct iov_iter *to) | 260 | pipe_read(struct kiocb *iocb, struct iov_iter *to) |
247 | { | 261 | { |