perf_event_detach_bpf_prog() writes event->prog = NULL under bpf_event_mutex, while __perf_event_overflow() reads event->prog concurrently without any lock protection. This causes a data race detected by KCSAN. __perf_event_overflow() can be called from interrupt/NMI context, so it cannot acquire bpf_event_mutex. Fix the race by using WRITE_ONCE() in perf_event_detach_bpf_prog() and READ_ONCE() in __perf_event_overflow(). Also store the result of READ_ONCE() in a local variable to avoid reading event->prog twice which could result in a NULL pointer dereference if the pointer becomes NULL between the two reads. Reported-by: syzbot+651d2774bd1d8395595f@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=651d2774bd1d8395595f Fixes: f11f10bfa1ca ("perf/bpf: Call BPF handler directly, not through overflow machinery") Signed-off-by: Deepanshu Kartikey --- kernel/events/core.c | 4 +++- kernel/trace/bpf_trace.c | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/kernel/events/core.c b/kernel/events/core.c index ba5bd6a78fe7..9d9c5477772e 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -10753,6 +10753,7 @@ static int __perf_event_overflow(struct perf_event *event, { int events = atomic_read(&event->event_limit); int ret = 0; + struct bpf_prog *prog; /* * Non-sampling counters might still use the PMI to fold short @@ -10766,7 +10767,8 @@ static int __perf_event_overflow(struct perf_event *event, if (event->attr.aux_pause) perf_event_aux_pause(event->aux_event, true); - if (event->prog && event->prog->type == BPF_PROG_TYPE_PERF_EVENT && + prog = READ_ONCE(event->prog); + if (prog && prog->type == BPF_PROG_TYPE_PERF_EVENT && !bpf_overflow_handler(event, data, regs)) goto out; diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index 75495a5c3507..ee61dc760045 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -2018,7 +2018,7 @@ void perf_event_detach_bpf_prog(struct perf_event *event) put: prog = event->prog; - event->prog = NULL; + WRITE_ONCE(event->prog, NULL); unlock: mutex_unlock(&bpf_event_mutex); -- 2.43.0