diff options
author | David Howells <dhowells@redhat.com> | 2022-03-11 14:24:36 +0100 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2022-03-11 19:17:13 +0100 |
commit | 2ed147f015af2b48f41c6f0b6746aa9ea85c19f3 (patch) | |
tree | 9274d9f2ec7e074fa4a2fb9ab4696ee82f9bd853 | |
parent | watch_queue: Free the alloc bitmap when the watch_queue is torn down (diff) | |
download | linux-2ed147f015af2b48f41c6f0b6746aa9ea85c19f3.tar.xz linux-2ed147f015af2b48f41c6f0b6746aa9ea85c19f3.zip |
watch_queue: Fix lack of barrier/sync/lock between post and read
There's nothing to synchronise post_one_notification() versus
pipe_read(). Whilst posting is done under pipe->rd_wait.lock, the
reader only takes pipe->mutex which cannot bar notification posting as
that may need to be made from contexts that cannot sleep.
Fix this by setting pipe->head with a barrier in post_one_notification()
and reading pipe->head with a barrier in pipe_read().
If that's not sufficient, the rd_wait.lock will need to be taken,
possibly in a ->confirm() op so that it only applies to notifications.
The lock would, however, have to be dropped before copy_page_to_iter()
is invoked.
Fixes: c73be61cede5 ("pipe: Add general notification queue support")
Reported-by: Jann Horn <jannh@google.com>
Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | fs/pipe.c | 3 | ||||
-rw-r--r-- | kernel/watch_queue.c | 2 |
2 files changed, 3 insertions, 2 deletions
diff --git a/fs/pipe.c b/fs/pipe.c index 4eb88bc138bb..2667db9506e2 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -253,7 +253,8 @@ pipe_read(struct kiocb *iocb, struct iov_iter *to) */ was_full = pipe_full(pipe->head, pipe->tail, pipe->max_usage); for (;;) { - unsigned int head = pipe->head; + /* Read ->head with a barrier vs post_one_notification() */ + unsigned int head = smp_load_acquire(&pipe->head); unsigned int tail = pipe->tail; unsigned int mask = pipe->ring_size - 1; diff --git a/kernel/watch_queue.c b/kernel/watch_queue.c index c12267ccc70e..37bcd900fd77 100644 --- a/kernel/watch_queue.c +++ b/kernel/watch_queue.c @@ -113,7 +113,7 @@ static bool post_one_notification(struct watch_queue *wqueue, buf->offset = offset; buf->len = len; buf->flags = PIPE_BUF_FLAG_WHOLE; - pipe->head = head + 1; + smp_store_release(&pipe->head, head + 1); /* vs pipe_read() */ if (!test_and_clear_bit(note, wqueue->notes_bitmap)) { spin_unlock_irq(&pipe->rd_wait.lock); |