]>
Commit | Line | Data |
---|---|---|
ea78042b SL |
1 | From d15cf81e82c6abf559bec243d5de7ae8f6ebf9f4 Mon Sep 17 00:00:00 2001 |
2 | From: Sasha Levin <sashal@kernel.org> | |
3 | Date: Fri, 24 Nov 2023 16:08:22 +0100 | |
4 | Subject: fs/pipe: Fix lockdep false-positive in watchqueue pipe_write() | |
5 | ||
6 | From: Jann Horn <jannh@google.com> | |
7 | ||
8 | [ Upstream commit 055ca83559912f2cfd91c9441427bac4caf3c74e ] | |
9 | ||
10 | When you try to splice between a normal pipe and a notification pipe, | |
11 | get_pipe_info(..., true) fails, so splice() falls back to treating the | |
12 | notification pipe like a normal pipe - so we end up in | |
13 | iter_file_splice_write(), which first locks the input pipe, then calls | |
14 | vfs_iter_write(), which locks the output pipe. | |
15 | ||
16 | Lockdep complains about that, because we're taking a pipe lock while | |
17 | already holding another pipe lock. | |
18 | ||
19 | I think this probably (?) can't actually lead to deadlocks, since you'd | |
20 | need another way to nest locking a normal pipe into locking a | |
21 | watch_queue pipe, but the lockdep annotations don't make that clear. | |
22 | ||
23 | Bail out earlier in pipe_write() for notification pipes, before taking | |
24 | the pipe lock. | |
25 | ||
26 | Reported-and-tested-by: <syzbot+011e4ea1da6692cf881c@syzkaller.appspotmail.com> | |
27 | Closes: https://syzkaller.appspot.com/bug?extid=011e4ea1da6692cf881c | |
28 | Fixes: c73be61cede5 ("pipe: Add general notification queue support") | |
29 | Signed-off-by: Jann Horn <jannh@google.com> | |
30 | Link: https://lore.kernel.org/r/20231124150822.2121798-1-jannh@google.com | |
31 | Signed-off-by: Christian Brauner <brauner@kernel.org> | |
32 | Signed-off-by: Sasha Levin <sashal@kernel.org> | |
33 | --- | |
34 | fs/pipe.c | 17 ++++++++++++----- | |
35 | 1 file changed, 12 insertions(+), 5 deletions(-) | |
36 | ||
37 | diff --git a/fs/pipe.c b/fs/pipe.c | |
38 | index a234035cc375d..ba4376341ddd2 100644 | |
39 | --- a/fs/pipe.c | |
40 | +++ b/fs/pipe.c | |
41 | @@ -425,6 +425,18 @@ pipe_write(struct kiocb *iocb, struct iov_iter *from) | |
42 | bool was_empty = false; | |
43 | bool wake_next_writer = false; | |
44 | ||
45 | + /* | |
46 | + * Reject writing to watch queue pipes before the point where we lock | |
47 | + * the pipe. | |
48 | + * Otherwise, lockdep would be unhappy if the caller already has another | |
49 | + * pipe locked. | |
50 | + * If we had to support locking a normal pipe and a notification pipe at | |
51 | + * the same time, we could set up lockdep annotations for that, but | |
52 | + * since we don't actually need that, it's simpler to just bail here. | |
53 | + */ | |
54 | + if (pipe_has_watch_queue(pipe)) | |
55 | + return -EXDEV; | |
56 | + | |
57 | /* Null write succeeds. */ | |
58 | if (unlikely(total_len == 0)) | |
59 | return 0; | |
60 | @@ -437,11 +449,6 @@ pipe_write(struct kiocb *iocb, struct iov_iter *from) | |
61 | goto out; | |
62 | } | |
63 | ||
64 | - if (pipe_has_watch_queue(pipe)) { | |
65 | - ret = -EXDEV; | |
66 | - goto out; | |
67 | - } | |
68 | - | |
69 | /* | |
70 | * If it wasn't empty we try to merge new data into | |
71 | * the last buffer. | |
72 | -- | |
73 | 2.43.0 | |
74 |