]>
Commit | Line | Data |
---|---|---|
2705b78f GKH |
1 | From a0ce2f0aa6ad97c3d4927bf2ca54bcebdf062d55 Mon Sep 17 00:00:00 2001 |
2 | From: Jann Horn <jannh@google.com> | |
3 | Date: Wed, 23 Jan 2019 15:19:17 +0100 | |
4 | Subject: splice: don't merge into linked buffers | |
5 | ||
6 | From: Jann Horn <jannh@google.com> | |
7 | ||
8 | commit a0ce2f0aa6ad97c3d4927bf2ca54bcebdf062d55 upstream. | |
9 | ||
10 | Before this patch, it was possible for two pipes to affect each other after | |
11 | data had been transferred between them with tee(): | |
12 | ||
13 | ============ | |
14 | $ cat tee_test.c | |
15 | ||
16 | int main(void) { | |
17 | int pipe_a[2]; | |
18 | if (pipe(pipe_a)) err(1, "pipe"); | |
19 | int pipe_b[2]; | |
20 | if (pipe(pipe_b)) err(1, "pipe"); | |
21 | if (write(pipe_a[1], "abcd", 4) != 4) err(1, "write"); | |
22 | if (tee(pipe_a[0], pipe_b[1], 2, 0) != 2) err(1, "tee"); | |
23 | if (write(pipe_b[1], "xx", 2) != 2) err(1, "write"); | |
24 | ||
25 | char buf[5]; | |
26 | if (read(pipe_a[0], buf, 4) != 4) err(1, "read"); | |
27 | buf[4] = 0; | |
28 | printf("got back: '%s'\n", buf); | |
29 | } | |
30 | $ gcc -o tee_test tee_test.c | |
31 | $ ./tee_test | |
32 | got back: 'abxx' | |
33 | $ | |
34 | ============ | |
35 | ||
36 | As suggested by Al Viro, fix it by creating a separate type for | |
37 | non-mergeable pipe buffers, then changing the types of buffers in | |
38 | splice_pipe_to_pipe() and link_pipe(). | |
39 | ||
40 | Cc: <stable@vger.kernel.org> | |
41 | Fixes: 7c77f0b3f920 ("splice: implement pipe to pipe splicing") | |
42 | Fixes: 70524490ee2e ("[PATCH] splice: add support for sys_tee()") | |
43 | Suggested-by: Al Viro <viro@zeniv.linux.org.uk> | |
44 | Signed-off-by: Jann Horn <jannh@google.com> | |
45 | Signed-off-by: Al Viro <viro@zeniv.linux.org.uk> | |
46 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | |
47 | ||
48 | --- | |
49 | fs/pipe.c | 14 ++++++++++++++ | |
50 | fs/splice.c | 4 ++++ | |
51 | include/linux/pipe_fs_i.h | 1 + | |
52 | 3 files changed, 19 insertions(+) | |
53 | ||
54 | --- a/fs/pipe.c | |
55 | +++ b/fs/pipe.c | |
56 | @@ -239,6 +239,14 @@ static const struct pipe_buf_operations | |
57 | .get = generic_pipe_buf_get, | |
58 | }; | |
59 | ||
60 | +static const struct pipe_buf_operations anon_pipe_buf_nomerge_ops = { | |
61 | + .can_merge = 0, | |
62 | + .confirm = generic_pipe_buf_confirm, | |
63 | + .release = anon_pipe_buf_release, | |
64 | + .steal = anon_pipe_buf_steal, | |
65 | + .get = generic_pipe_buf_get, | |
66 | +}; | |
67 | + | |
68 | static const struct pipe_buf_operations packet_pipe_buf_ops = { | |
69 | .can_merge = 0, | |
70 | .confirm = generic_pipe_buf_confirm, | |
71 | @@ -247,6 +255,12 @@ static const struct pipe_buf_operations | |
72 | .get = generic_pipe_buf_get, | |
73 | }; | |
74 | ||
75 | +void pipe_buf_mark_unmergeable(struct pipe_buffer *buf) | |
76 | +{ | |
77 | + if (buf->ops == &anon_pipe_buf_ops) | |
78 | + buf->ops = &anon_pipe_buf_nomerge_ops; | |
79 | +} | |
80 | + | |
81 | static ssize_t | |
82 | pipe_read(struct kiocb *iocb, struct iov_iter *to) | |
83 | { | |
84 | --- a/fs/splice.c | |
85 | +++ b/fs/splice.c | |
86 | @@ -1580,6 +1580,8 @@ retry: | |
87 | */ | |
88 | obuf->flags &= ~PIPE_BUF_FLAG_GIFT; | |
89 | ||
90 | + pipe_buf_mark_unmergeable(obuf); | |
91 | + | |
92 | obuf->len = len; | |
93 | opipe->nrbufs++; | |
94 | ibuf->offset += obuf->len; | |
95 | @@ -1654,6 +1656,8 @@ static int link_pipe(struct pipe_inode_i | |
96 | */ | |
97 | obuf->flags &= ~PIPE_BUF_FLAG_GIFT; | |
98 | ||
99 | + pipe_buf_mark_unmergeable(obuf); | |
100 | + | |
101 | if (obuf->len > len) | |
102 | obuf->len = len; | |
103 | ||
104 | --- a/include/linux/pipe_fs_i.h | |
105 | +++ b/include/linux/pipe_fs_i.h | |
106 | @@ -183,6 +183,7 @@ void generic_pipe_buf_get(struct pipe_in | |
107 | int generic_pipe_buf_confirm(struct pipe_inode_info *, struct pipe_buffer *); | |
108 | int generic_pipe_buf_steal(struct pipe_inode_info *, struct pipe_buffer *); | |
109 | void generic_pipe_buf_release(struct pipe_inode_info *, struct pipe_buffer *); | |
110 | +void pipe_buf_mark_unmergeable(struct pipe_buffer *buf); | |
111 | ||
112 | extern const struct pipe_buf_operations nosteal_pipe_buf_ops; | |
113 |