Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

watch_queue: prevent dangling pipe pointer

NULL the dangling pipe reference while clearing watch_queue.

If not done, a reference to a freed pipe remains in the watch_queue,
as this function is called before freeing a pipe in free_pipe_info()
(see line 834 of fs/pipe.c).

The sole use of wqueue->defunct is for checking if the watch queue has
been cleared, but wqueue->pipe is also NULLed while clearing.

Thus, wqueue->defunct is superfluous, as wqueue->pipe can be checked
for NULL. Hence, the former can be removed.

Tested with keyutils testsuite.

Cc: stable@vger.kernel.org # 6.1
Signed-off-by: Siddh Raman Pant <code@siddh.me>
Acked-by: David Howells <dhowells@redhat.com>
Message-Id: <20230605143616.640517-1-code@siddh.me>
Signed-off-by: Christian Brauner <brauner@kernel.org>

authored by

Siddh Raman Pant and committed by
Christian Brauner
943211c8 a7bc2e8d

+7 -8
+1 -2
include/linux/watch_queue.h
··· 38 38 struct watch_queue { 39 39 struct rcu_head rcu; 40 40 struct watch_filter __rcu *filter; 41 - struct pipe_inode_info *pipe; /* The pipe we're using as a buffer */ 41 + struct pipe_inode_info *pipe; /* Pipe we use as a buffer, NULL if queue closed */ 42 42 struct hlist_head watches; /* Contributory watches */ 43 43 struct page **notes; /* Preallocated notifications */ 44 44 unsigned long *notes_bitmap; /* Allocation bitmap for notes */ ··· 46 46 spinlock_t lock; 47 47 unsigned int nr_notes; /* Number of notes */ 48 48 unsigned int nr_pages; /* Number of pages in notes[] */ 49 - bool defunct; /* T when queues closed */ 50 49 }; 51 50 52 51 /*
+6 -6
kernel/watch_queue.c
··· 42 42 static inline bool lock_wqueue(struct watch_queue *wqueue) 43 43 { 44 44 spin_lock_bh(&wqueue->lock); 45 - if (unlikely(wqueue->defunct)) { 45 + if (unlikely(!wqueue->pipe)) { 46 46 spin_unlock_bh(&wqueue->lock); 47 47 return false; 48 48 } ··· 103 103 struct page *page; 104 104 unsigned int head, tail, mask, note, offset, len; 105 105 bool done = false; 106 - 107 - if (!pipe) 108 - return false; 109 106 110 107 spin_lock_irq(&pipe->rd_wait.lock); 111 108 ··· 600 603 rcu_read_lock(); 601 604 spin_lock_bh(&wqueue->lock); 602 605 603 - /* Prevent new notifications from being stored. */ 604 - wqueue->defunct = true; 606 + /* 607 + * This pipe can be freed by callers like free_pipe_info(). 608 + * Removing this reference also prevents new notifications. 609 + */ 610 + wqueue->pipe = NULL; 605 611 606 612 while (!hlist_empty(&wqueue->watches)) { 607 613 watch = hlist_entry(wqueue->watches.first, struct watch, queue_node);