Skip to content

Commit c9bc175

Browse files
author
Peter Zijlstra
committed
perf: Fix __perf_event_overflow() vs perf_remove_from_context() race
Make sure that __perf_event_overflow() runs with IRQs disabled for all possible callchains. Specifically the software events can end up running it with only preemption disabled. This opens up a race vs perf_event_exit_event() and friends that will go and free various things the overflow path expects to be present, like the BPF program. Fixes: 592903c ("perf_counter: add an event_list") Reported-by: Simond Hu <cmdhh1767@gmail.com> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Tested-by: Simond Hu <cmdhh1767@gmail.com> Link: https://patch.msgid.link/20260224122909.GV1395416@noisy.programming.kicks-ass.net
1 parent 77de62a commit c9bc175

File tree

1 file changed

+41
-1
lines changed

1 file changed

+41
-1
lines changed

kernel/events/core.c

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10777,6 +10777,13 @@ int perf_event_overflow(struct perf_event *event,
1077710777
struct perf_sample_data *data,
1077810778
struct pt_regs *regs)
1077910779
{
10780+
/*
10781+
* Entry point from hardware PMI, interrupts should be disabled here.
10782+
* This serializes us against perf_event_remove_from_context() in
10783+
* things like perf_event_release_kernel().
10784+
*/
10785+
lockdep_assert_irqs_disabled();
10786+
1078010787
return __perf_event_overflow(event, 1, data, regs);
1078110788
}
1078210789

@@ -10853,6 +10860,19 @@ static void perf_swevent_event(struct perf_event *event, u64 nr,
1085310860
{
1085410861
struct hw_perf_event *hwc = &event->hw;
1085510862

10863+
/*
10864+
* This is:
10865+
* - software preempt
10866+
* - tracepoint preempt
10867+
* - tp_target_task irq (ctx->lock)
10868+
* - uprobes preempt/irq
10869+
* - kprobes preempt/irq
10870+
* - hw_breakpoint irq
10871+
*
10872+
* Any of these are sufficient to hold off RCU and thus ensure @event
10873+
* exists.
10874+
*/
10875+
lockdep_assert_preemption_disabled();
1085610876
local64_add(nr, &event->count);
1085710877

1085810878
if (!regs)
@@ -10861,6 +10881,16 @@ static void perf_swevent_event(struct perf_event *event, u64 nr,
1086110881
if (!is_sampling_event(event))
1086210882
return;
1086310883

10884+
/*
10885+
* Serialize against event_function_call() IPIs like normal overflow
10886+
* event handling. Specifically, must not allow
10887+
* perf_event_release_kernel() -> perf_remove_from_context() to make
10888+
* progress and 'release' the event from under us.
10889+
*/
10890+
guard(irqsave)();
10891+
if (event->state != PERF_EVENT_STATE_ACTIVE)
10892+
return;
10893+
1086410894
if ((event->attr.sample_type & PERF_SAMPLE_PERIOD) && !event->attr.freq) {
1086510895
data->period = nr;
1086610896
return perf_swevent_overflow(event, 1, data, regs);
@@ -11359,6 +11389,11 @@ void perf_tp_event(u16 event_type, u64 count, void *record, int entry_size,
1135911389
struct perf_sample_data data;
1136011390
struct perf_event *event;
1136111391

11392+
/*
11393+
* Per being a tracepoint, this runs with preemption disabled.
11394+
*/
11395+
lockdep_assert_preemption_disabled();
11396+
1136211397
struct perf_raw_record raw = {
1136311398
.frag = {
1136411399
.size = entry_size,
@@ -11691,6 +11726,11 @@ void perf_bp_event(struct perf_event *bp, void *data)
1169111726
struct perf_sample_data sample;
1169211727
struct pt_regs *regs = data;
1169311728

11729+
/*
11730+
* Exception context, will have interrupts disabled.
11731+
*/
11732+
lockdep_assert_irqs_disabled();
11733+
1169411734
perf_sample_data_init(&sample, bp->attr.bp_addr, 0);
1169511735

1169611736
if (!bp->hw.state && !perf_exclude_event(bp, regs))
@@ -12155,7 +12195,7 @@ static enum hrtimer_restart perf_swevent_hrtimer(struct hrtimer *hrtimer)
1215512195

1215612196
if (regs && !perf_exclude_event(event, regs)) {
1215712197
if (!(event->attr.exclude_idle && is_idle_task(current)))
12158-
if (__perf_event_overflow(event, 1, &data, regs))
12198+
if (perf_event_overflow(event, &data, regs))
1215912199
ret = HRTIMER_NORESTART;
1216012200
}
1216112201

0 commit comments

Comments
 (0)