diff options
author | Peter Zijlstra <peterz@infradead.org> | 2015-12-15 07:49:05 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2016-01-06 04:52:39 -0500 |
commit | 12ca6ad2e3a896256f086497a7c7406a547ee373 (patch) | |
tree | 96f15b0a7c6622da00161b2b66ac6fb78019cfbe | |
parent | c127449944659543e5e2423002f08f0af98dba5c (diff) |
perf: Fix race in swevent hash
There's a race on CPU unplug where we free the swevent hash array
while it can still have events on. This will result in a
use-after-free which is BAD.
Simply do not free the hash array on unplug. This leaves the thing
around and no use-after-free takes place.
When the last swevent dies, we do a for_each_possible_cpu() iteration
anyway to clean these up, at which time we'll free it, so no leakage
will occur.
Reported-by: Sasha Levin <sasha.levin@oracle.com>
Tested-by: Sasha Levin <sasha.levin@oracle.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Vince Weaver <vincent.weaver@maine.edu>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r-- | kernel/events/core.c | 20 |
1 files changed, 1 insertions, 19 deletions
diff --git a/kernel/events/core.c b/kernel/events/core.c index fd7de0418fbe..0a791a2203dc 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c | |||
@@ -6488,9 +6488,6 @@ struct swevent_htable { | |||
6488 | 6488 | ||
6489 | /* Recursion avoidance in each contexts */ | 6489 | /* Recursion avoidance in each contexts */ |
6490 | int recursion[PERF_NR_CONTEXTS]; | 6490 | int recursion[PERF_NR_CONTEXTS]; |
6491 | |||
6492 | /* Keeps track of cpu being initialized/exited */ | ||
6493 | bool online; | ||
6494 | }; | 6491 | }; |
6495 | 6492 | ||
6496 | static DEFINE_PER_CPU(struct swevent_htable, swevent_htable); | 6493 | static DEFINE_PER_CPU(struct swevent_htable, swevent_htable); |
@@ -6748,14 +6745,8 @@ static int perf_swevent_add(struct perf_event *event, int flags) | |||
6748 | hwc->state = !(flags & PERF_EF_START); | 6745 | hwc->state = !(flags & PERF_EF_START); |
6749 | 6746 | ||
6750 | head = find_swevent_head(swhash, event); | 6747 | head = find_swevent_head(swhash, event); |
6751 | if (!head) { | 6748 | if (WARN_ON_ONCE(!head)) |
6752 | /* | ||
6753 | * We can race with cpu hotplug code. Do not | ||
6754 | * WARN if the cpu just got unplugged. | ||
6755 | */ | ||
6756 | WARN_ON_ONCE(swhash->online); | ||
6757 | return -EINVAL; | 6749 | return -EINVAL; |
6758 | } | ||
6759 | 6750 | ||
6760 | hlist_add_head_rcu(&event->hlist_entry, head); | 6751 | hlist_add_head_rcu(&event->hlist_entry, head); |
6761 | perf_event_update_userpage(event); | 6752 | perf_event_update_userpage(event); |
@@ -6823,7 +6814,6 @@ static int swevent_hlist_get_cpu(struct perf_event *event, int cpu) | |||
6823 | int err = 0; | 6814 | int err = 0; |
6824 | 6815 | ||
6825 | mutex_lock(&swhash->hlist_mutex); | 6816 | mutex_lock(&swhash->hlist_mutex); |
6826 | |||
6827 | if (!swevent_hlist_deref(swhash) && cpu_online(cpu)) { | 6817 | if (!swevent_hlist_deref(swhash) && cpu_online(cpu)) { |
6828 | struct swevent_hlist *hlist; | 6818 | struct swevent_hlist *hlist; |
6829 | 6819 | ||
@@ -9286,7 +9276,6 @@ static void perf_event_init_cpu(int cpu) | |||
9286 | struct swevent_htable *swhash = &per_cpu(swevent_htable, cpu); | 9276 | struct swevent_htable *swhash = &per_cpu(swevent_htable, cpu); |
9287 | 9277 | ||
9288 | mutex_lock(&swhash->hlist_mutex); | 9278 | mutex_lock(&swhash->hlist_mutex); |
9289 | swhash->online = true; | ||
9290 | if (swhash->hlist_refcount > 0) { | 9279 | if (swhash->hlist_refcount > 0) { |
9291 | struct swevent_hlist *hlist; | 9280 | struct swevent_hlist *hlist; |
9292 | 9281 | ||
@@ -9328,14 +9317,7 @@ static void perf_event_exit_cpu_context(int cpu) | |||
9328 | 9317 | ||
9329 | static void perf_event_exit_cpu(int cpu) | 9318 | static void perf_event_exit_cpu(int cpu) |
9330 | { | 9319 | { |
9331 | struct swevent_htable *swhash = &per_cpu(swevent_htable, cpu); | ||
9332 | |||
9333 | perf_event_exit_cpu_context(cpu); | 9320 | perf_event_exit_cpu_context(cpu); |
9334 | |||
9335 | mutex_lock(&swhash->hlist_mutex); | ||
9336 | swhash->online = false; | ||
9337 | swevent_hlist_release(swhash); | ||
9338 | mutex_unlock(&swhash->hlist_mutex); | ||
9339 | } | 9321 | } |
9340 | #else | 9322 | #else |
9341 | static inline void perf_event_exit_cpu(int cpu) { } | 9323 | static inline void perf_event_exit_cpu(int cpu) { } |