History

perf/core: Fix sideband list-iteration vs. event ordering NULL pointer deference crash

Vegard Nossum reported that perf fuzzing generates a NULL pointer dereference crash: > Digging a bit deeper into this, it seems the event itself is getting > created by perf_event_open() and it gets added to the pmu_event_list > through: > > perf_event_open() > - perf_event_alloc() > - account_event() > - account_pmu_sb_event() > - attach_sb_event() > > so at this point the event is being attached but its ->ctx is still > NULL. It seems like ->ctx is set just a bit later in > perf_event_open(), though. > > But before that, __schedule() comes along and creates a stack trace > similar to the one above: > > __schedule() > - __perf_event_task_sched_out() > - perf_iterate_sb() > - perf_iterate_sb_cpu() > - event_filter_match() > - perf_cgroup_match() > - __get_cpu_context() > - (dereference ctx which is NULL) > > So I guess the question is... should the event be attached (= put on > the list) before ->ctx gets set? Or should the cgroup code check for a > NULL ->ctx? The latter seems like the simplest solution. Moving the list-add later creates a bit of a mess.

Peter Zijlstra committed 10 years ago

Commit 0b8f1e21 parent 69766c41 file · +18 −5Browse files

Unified · Split

kernel/events/core.c+18−5
@@ -1716,8 +1716,8 @@ static inline int pmu_filter_match(struct perf_event *event) · 1715 unmodified lines
17161716 static inline int
17171717 event_filter_match(struct perf_event *event)
17181718 {
1719− return (event->cpu == -1 || event->cpu == smp_processor_id())
1720− && perf_cgroup_match(event) && pmu_filter_match(event);
1719+ return (event->cpu == -1 || event->cpu == smp_processor_id()) &&
1720+ perf_cgroup_match(event) && pmu_filter_match(event);
17211721 }
17221722
17231723 static void
@@ -1737,8 +1737,8 @@ event_sched_out(struct perf_event *event, · 13 unmodified lines
17371737 * maintained, otherwise bogus information is return
17381738 * via read() for time_enabled, time_running:
17391739 */
1740− if (event->state == PERF_EVENT_STATE_INACTIVE
1741− && !event_filter_match(event)) {
1740+ if (event->state == PERF_EVENT_STATE_INACTIVE &&
1741+ !event_filter_match(event)) {
17421742 delta = tstamp - event->tstamp_stopped;
17431743 event->tstamp_running += delta;
17441744 event->tstamp_stopped = tstamp;
@@ -2236,10 +2236,15 @@ perf_install_in_context(struct perf_event_context *ctx, · 491 unmodified lines
22362236
22372237 lockdep_assert_held(&ctx->mutex);
22382238
2239− event->ctx = ctx;
22402239 if (event->cpu != -1)
22412240 event->cpu = cpu;
22422241
2242+ /*
2243+ * Ensures that if we can observe event->ctx, both the event and ctx
2244+ * will be 'complete'. See perf_iterate_sb_cpu().
2245+ */
2246+ smp_store_release(&event->ctx, ctx);
2247+
22432248 if (!task) {
22442249 cpu_function_call(cpu, __perf_install_in_context, event);
22452250 return;
@@ -5969,6 +5974,14 @@ static void perf_iterate_sb_cpu(perf_iterate_f output, void *data) · 3723 unmodified lines
59695974 struct perf_event *event;
59705975
59715976 list_for_each_entry_rcu(event, &pel->list, sb_list) {
5977+ /*
5978+ * Skip events that are not fully formed yet; ensure that
5979+ * if we observe event->ctx, both event and ctx will be
5980+ * complete enough. See perf_install_in_context().
5981+ */
5982+ if (!smp_load_acquire(&event->ctx))
5983+ continue;
5984+
59725985 if (event->state < PERF_EVENT_STATE_INACTIVE)
59735986 continue;
59745987 if (!event_filter_match(event))

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t