[RESEND PATCH v2 2/2] kernel/watch_queue: NULL the dangling *pipe, and use it for clear check

From: Siddh Raman Pant
Date: Wed Sep 21 2022 - 05:29:18 EST


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.

Signed-off-by: Siddh Raman Pant <code@xxxxxxxx>
---
include/linux/watch_queue.h | 4 +---
kernel/watch_queue.c | 12 ++++++------
2 files changed, 7 insertions(+), 9 deletions(-)

diff --git a/include/linux/watch_queue.h b/include/linux/watch_queue.h
index 7f8b1f15634b..d72ad82a4435 100644
--- a/include/linux/watch_queue.h
+++ b/include/linux/watch_queue.h
@@ -55,7 +55,7 @@ struct watch_filter {
*
* @rcu: RCU head
* @filter: Filter to use on watches
- * @pipe: The pipe we're using as a buffer
+ * @pipe: The pipe we're using as a buffer, NULL when queue is cleared/closed
* @watches: Contributory watches
* @notes: Preallocated notifications
* @notes_bitmap: Allocation bitmap for notes
@@ -63,7 +63,6 @@ struct watch_filter {
* @lock: To serialize accesses and removes
* @nr_notes: Number of notes
* @nr_pages: Number of pages in notes[]
- * @defunct: True when queues closed
*/
struct watch_queue {
struct rcu_head rcu;
@@ -76,7 +75,6 @@ struct watch_queue {
spinlock_t lock;
unsigned int nr_notes;
unsigned int nr_pages;
- bool defunct;
};

/**
diff --git a/kernel/watch_queue.c b/kernel/watch_queue.c
index a6f9bdd956c3..a70ddfd622ee 100644
--- a/kernel/watch_queue.c
+++ b/kernel/watch_queue.c
@@ -43,7 +43,7 @@ MODULE_LICENSE("GPL");
static inline bool lock_wqueue(struct watch_queue *wqueue)
{
spin_lock_bh(&wqueue->lock);
- if (unlikely(wqueue->defunct)) {
+ if (unlikely(!wqueue->pipe)) {
spin_unlock_bh(&wqueue->lock);
return false;
}
@@ -105,9 +105,6 @@ static bool post_one_notification(struct watch_queue *wqueue,
unsigned int head, tail, mask, note, offset, len;
bool done = false;

- if (!pipe)
- return false;
-
spin_lock_irq(&pipe->rd_wait.lock);

mask = pipe->ring_size - 1;
@@ -603,8 +600,11 @@ void watch_queue_clear(struct watch_queue *wqueue)
rcu_read_lock();
spin_lock_bh(&wqueue->lock);

- /* Prevent new notifications from being stored. */
- wqueue->defunct = true;
+ /*
+ * This pipe will get freed by the caller free_pipe_info().
+ * Removing this reference also prevents new notifications.
+ */
+ wqueue->pipe = NULL;

while (!hlist_empty(&wqueue->watches)) {
watch = hlist_entry(wqueue->watches.first, struct watch, queue_node);
--
2.35.1