]>
Commit | Line | Data |
---|---|---|
e7340404 GKH |
1 | From 971316f0503a5c50633d07b83b6db2f15a3a5b00 Mon Sep 17 00:00:00 2001 |
2 | From: Oleg Nesterov <oleg@redhat.com> | |
3 | Date: Fri, 24 Feb 2012 20:07:29 +0100 | |
4 | Subject: epoll: ep_unregister_pollwait() can use the freed pwq->whead | |
5 | ||
6 | From: Oleg Nesterov <oleg@redhat.com> | |
7 | ||
8 | commit 971316f0503a5c50633d07b83b6db2f15a3a5b00 upstream. | |
9 | ||
10 | signalfd_cleanup() ensures that ->signalfd_wqh is not used, but | |
11 | this is not enough. eppoll_entry->whead still points to the memory | |
12 | we are going to free, ep_unregister_pollwait()->remove_wait_queue() | |
13 | is obviously unsafe. | |
14 | ||
15 | Change ep_poll_callback(POLLFREE) to set eppoll_entry->whead = NULL, | |
16 | change ep_unregister_pollwait() to check pwq->whead != NULL under | |
17 | rcu_read_lock() before remove_wait_queue(). We add the new helper, | |
18 | ep_remove_wait_queue(), for this. | |
19 | ||
20 | This works because sighand_cachep is SLAB_DESTROY_BY_RCU and because | |
21 | ->signalfd_wqh is initialized in sighand_ctor(), not in copy_sighand. | |
22 | ep_unregister_pollwait()->remove_wait_queue() can play with already | |
23 | freed and potentially reused ->sighand, but this is fine. This memory | |
24 | must have the valid ->signalfd_wqh until rcu_read_unlock(). | |
25 | ||
26 | Reported-by: Maxime Bizon <mbizon@freebox.fr> | |
27 | Signed-off-by: Oleg Nesterov <oleg@redhat.com> | |
28 | Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> | |
29 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | |
30 | ||
31 | --- | |
32 | fs/eventpoll.c | 30 +++++++++++++++++++++++++++--- | |
33 | fs/signalfd.c | 6 +++++- | |
34 | 2 files changed, 32 insertions(+), 4 deletions(-) | |
35 | ||
36 | --- a/fs/eventpoll.c | |
37 | +++ b/fs/eventpoll.c | |
38 | @@ -299,6 +299,11 @@ static inline int ep_is_linked(struct li | |
39 | return !list_empty(p); | |
40 | } | |
41 | ||
42 | +static inline struct eppoll_entry *ep_pwq_from_wait(wait_queue_t *p) | |
43 | +{ | |
44 | + return container_of(p, struct eppoll_entry, wait); | |
45 | +} | |
46 | + | |
47 | /* Get the "struct epitem" from a wait queue pointer */ | |
48 | static inline struct epitem *ep_item_from_wait(wait_queue_t *p) | |
49 | { | |
50 | @@ -446,6 +451,18 @@ static void ep_poll_safewake(wait_queue_ | |
51 | put_cpu(); | |
52 | } | |
53 | ||
54 | +static void ep_remove_wait_queue(struct eppoll_entry *pwq) | |
55 | +{ | |
56 | + wait_queue_head_t *whead; | |
57 | + | |
58 | + rcu_read_lock(); | |
59 | + /* If it is cleared by POLLFREE, it should be rcu-safe */ | |
60 | + whead = rcu_dereference(pwq->whead); | |
61 | + if (whead) | |
62 | + remove_wait_queue(whead, &pwq->wait); | |
63 | + rcu_read_unlock(); | |
64 | +} | |
65 | + | |
66 | /* | |
67 | * This function unregisters poll callbacks from the associated file | |
68 | * descriptor. Must be called with "mtx" held (or "epmutex" if called from | |
69 | @@ -460,7 +477,7 @@ static void ep_unregister_pollwait(struc | |
70 | pwq = list_first_entry(lsthead, struct eppoll_entry, llink); | |
71 | ||
72 | list_del(&pwq->llink); | |
73 | - remove_wait_queue(pwq->whead, &pwq->wait); | |
74 | + ep_remove_wait_queue(pwq); | |
75 | kmem_cache_free(pwq_cache, pwq); | |
76 | } | |
77 | } | |
78 | @@ -827,9 +844,16 @@ static int ep_poll_callback(wait_queue_t | |
79 | struct epitem *epi = ep_item_from_wait(wait); | |
80 | struct eventpoll *ep = epi->ep; | |
81 | ||
82 | - /* the caller holds eppoll_entry->whead->lock */ | |
83 | - if ((unsigned long)key & POLLFREE) | |
84 | + if ((unsigned long)key & POLLFREE) { | |
85 | + ep_pwq_from_wait(wait)->whead = NULL; | |
86 | + /* | |
87 | + * whead = NULL above can race with ep_remove_wait_queue() | |
88 | + * which can do another remove_wait_queue() after us, so we | |
89 | + * can't use __remove_wait_queue(). whead->lock is held by | |
90 | + * the caller. | |
91 | + */ | |
92 | list_del_init(&wait->task_list); | |
93 | + } | |
94 | ||
95 | spin_lock_irqsave(&ep->lock, flags); | |
96 | ||
97 | --- a/fs/signalfd.c | |
98 | +++ b/fs/signalfd.c | |
99 | @@ -33,7 +33,11 @@ | |
100 | void signalfd_cleanup(struct sighand_struct *sighand) | |
101 | { | |
102 | wait_queue_head_t *wqh = &sighand->signalfd_wqh; | |
103 | - | |
104 | + /* | |
105 | + * The lockless check can race with remove_wait_queue() in progress, | |
106 | + * but in this case its caller should run under rcu_read_lock() and | |
107 | + * sighand_cachep is SLAB_DESTROY_BY_RCU, we can safely return. | |
108 | + */ | |
109 | if (likely(!waitqueue_active(wqh))) | |
110 | return; | |
111 |