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 | |
| 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>
| -rw-r--r-- | fs/pipe.c | 14 | ||||
| -rw-r--r-- | fs/splice.c | 4 | ||||
| -rw-r--r-- | include/linux/pipe_fs_i.h | 1 |
3 files changed, 19 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 | { |
diff --git a/fs/splice.c b/fs/splice.c index de2ede048473..90c29675d573 100644 --- a/fs/splice.c +++ b/fs/splice.c | |||
| @@ -1597,6 +1597,8 @@ retry: | |||
| 1597 | */ | 1597 | */ |
| 1598 | obuf->flags &= ~PIPE_BUF_FLAG_GIFT; | 1598 | obuf->flags &= ~PIPE_BUF_FLAG_GIFT; |
| 1599 | 1599 | ||
| 1600 | pipe_buf_mark_unmergeable(obuf); | ||
| 1601 | |||
| 1600 | obuf->len = len; | 1602 | obuf->len = len; |
| 1601 | opipe->nrbufs++; | 1603 | opipe->nrbufs++; |
| 1602 | ibuf->offset += obuf->len; | 1604 | ibuf->offset += obuf->len; |
| @@ -1671,6 +1673,8 @@ static int link_pipe(struct pipe_inode_info *ipipe, | |||
| 1671 | */ | 1673 | */ |
| 1672 | obuf->flags &= ~PIPE_BUF_FLAG_GIFT; | 1674 | obuf->flags &= ~PIPE_BUF_FLAG_GIFT; |
| 1673 | 1675 | ||
| 1676 | pipe_buf_mark_unmergeable(obuf); | ||
| 1677 | |||
| 1674 | if (obuf->len > len) | 1678 | if (obuf->len > len) |
| 1675 | obuf->len = len; | 1679 | obuf->len = len; |
| 1676 | 1680 | ||
diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h index 5a3bb3b7c9ad..3ecd7ea212ae 100644 --- a/include/linux/pipe_fs_i.h +++ b/include/linux/pipe_fs_i.h | |||
| @@ -182,6 +182,7 @@ void generic_pipe_buf_get(struct pipe_inode_info *, struct pipe_buffer *); | |||
| 182 | int generic_pipe_buf_confirm(struct pipe_inode_info *, struct pipe_buffer *); | 182 | int generic_pipe_buf_confirm(struct pipe_inode_info *, struct pipe_buffer *); |
| 183 | int generic_pipe_buf_steal(struct pipe_inode_info *, struct pipe_buffer *); | 183 | int generic_pipe_buf_steal(struct pipe_inode_info *, struct pipe_buffer *); |
| 184 | void generic_pipe_buf_release(struct pipe_inode_info *, struct pipe_buffer *); | 184 | void generic_pipe_buf_release(struct pipe_inode_info *, struct pipe_buffer *); |
| 185 | void pipe_buf_mark_unmergeable(struct pipe_buffer *buf); | ||
| 185 | 186 | ||
| 186 | extern const struct pipe_buf_operations nosteal_pipe_buf_ops; | 187 | extern const struct pipe_buf_operations nosteal_pipe_buf_ops; |
| 187 | 188 | ||
