Patch "epoll: fix race between ep_poll_callback(POLLFREE) and ep_free()/ep_remove()" has been added to the 4.4-stable tree
Subsystems (2)
This is a note to let you know that I've just added the patch titled
epoll: fix race between ep_poll_callback(POLLFREE) and ep_free()/ep_remove()
to the 4.4-stable tree which can be found at:
http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary
The filename of the patch is:
epoll-fix-race-between-ep_poll_callback-pollfree-and-ep_free-ep_remove.patch
and it can be found in the queue-4.4 subdirectory.
If you, or anyone else, feels it should not be added to the stable tree, please let <[email protected]> know about it.
Quoted 1 line
From
138e4ad67afd5c6c318b056b4d17c17f2c0ca5c0Mon Sep 17 00:00:00 2001
From: Oleg Nesterov <[email protected]>
Date: Fri, 1 Sep 2017 18:55:33 +0200
Subject: epoll: fix race between ep_poll_callback(POLLFREE) and ep_free()/ep_remove()
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
From: Oleg Nesterov <[email protected]>
commit 138e4ad67afd5c6c318b056b4d17c17f2c0ca5c0 upstream.
The race was introduced by me in commit 971316f0503a ("epoll:ep_unregister_pollwait() can use the freed pwq->whead"). I did not realize that nothing can protect eventpoll after ep_poll_callback() sets ->whead = NULL, only whead->lock can save us from the race with ep_free() or ep_remove().
Move ->whead = NULL to the end of ep_poll_callback() and add the necessary barriers.
TODO: cleanup the ewake/EPOLLEXCLUSIVE logic, it was confusing even before this patch.
Hopefully this explains use-after-free reported by syzcaller:
BUG: KASAN: use-after-free in debug_spin_lock_before ... _raw_spin_lock_irqsave+0x4a/0x60 kernel/locking/spinlock.c:159 ep_poll_callback+0x29f/0xff0 fs/eventpoll.c:1148
this is spin_lock(eventpoll->lock),
... Freed by task 17774: ... kfree+0xe8/0x2c0 mm/slub.c:3883 ep_free+0x22c/0x2a0 fs/eventpoll.c:865
--- fs/eventpoll.c | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-)
fs/eventpoll.c +24 -13
@@ -518,8 +518,13 @@ static void ep_remove_wait_queue(structwait_queue_head_t *whead;rcu_read_lock (); -/* If it is cleared by POLLFREE, it should be rcu-safe */ - whead =rcu_dereference (pwq->whead); +/* +* If it is cleared by POLLFREE, it should be rcu-safe. +* If we read NULL we need a barrier paired with +* smp_store_release() in ep_poll_callback(), otherwise +* we rely on whead->lock. +*/ + whead =smp_load_acquire (&pwq->whead);if (whead)remove_wait_queue (whead, &pwq->wait);rcu_read_unlock ();
@@ -1003,17 +1008,6 @@ static int ep_poll_callback(wait_queue_tstruct epitem *epi =ep_item_from_wait (wait);struct eventpoll *ep = epi->ep; -if ((unsigned long )key &POLLFREE ) { -ep_pwq_from_wait (wait)->whead =NULL ; -/* -* whead = NULL above can race with ep_remove_wait_queue() -* which can do another remove_wait_queue() after us, so we -* can't use __remove_wait_queue(). whead->lock is held by -* the caller. -*/ -list_del_init (&wait->task_list); - } -spin_lock_irqsave (&ep->lock, flags);/*
@@ -1078,6 +1072,23 @@ out_unlock:if (pwake)ep_poll_safewake (&ep->poll_wait); + +if ((unsigned long )key &POLLFREE ) { +/* +* If we race with ep_remove_wait_queue() it can miss +* ->whead = NULL and do another remove_wait_queue() after +* us, so we can't use __remove_wait_queue(). +*/ +list_del_init (&wait->task_list); +/* +* ->whead != NULL protects us from the race with ep_free() +* or ep_remove(), ep_remove_wait_queue() takes whead->lock +* held by the caller. Once we nullify it, nothing protects +* ep/epi or even wait. +*/ +smp_store_release (&ep_pwq_from_wait (wait)->whead,NULL ); + } +return 1 ; }
Patches currently in stable-queue which might be from [email protected] are
queue-4.4/epoll-fix-race-between-ep_poll_callback-pollfree-and-ep_free-ep_remove.patch