As Alexei suggested, the return value from get_perf_callchain() may be reused if another task preempts and requests the stack after BPF program switched to migrate disable. Reported-by: Alexei Starovoitov Signed-off-by: Tao Chen --- kernel/bpf/stackmap.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/kernel/bpf/stackmap.c b/kernel/bpf/stackmap.c index 2e182a3ac4c..07892320906 100644 --- a/kernel/bpf/stackmap.c +++ b/kernel/bpf/stackmap.c @@ -314,8 +314,10 @@ BPF_CALL_3(bpf_get_stackid, struct pt_regs *, regs, struct bpf_map *, map, if (max_depth > sysctl_perf_event_max_stack) max_depth = sysctl_perf_event_max_stack; + preempt_disable(); trace = get_perf_callchain(regs, 0, kernel, user, max_depth, false, false); + preempt_enable(); if (unlikely(!trace)) /* couldn't fetch the stack trace */ @@ -443,9 +445,7 @@ static long __bpf_get_stack(struct pt_regs *regs, struct task_struct *task, if (sysctl_perf_event_max_stack < max_depth) max_depth = sysctl_perf_event_max_stack; - if (may_fault) - rcu_read_lock(); /* need RCU for perf's callchain below */ - + preempt_disable(); if (trace_in) trace = trace_in; else if (kernel && task) @@ -455,8 +455,7 @@ static long __bpf_get_stack(struct pt_regs *regs, struct task_struct *task, crosstask, false); if (unlikely(!trace) || trace->nr < skip) { - if (may_fault) - rcu_read_unlock(); + preempt_enable(); goto err_fault; } @@ -474,10 +473,7 @@ static long __bpf_get_stack(struct pt_regs *regs, struct task_struct *task, } else { memcpy(buf, ips, copy_len); } - - /* trace/ips should not be dereferenced after this point */ - if (may_fault) - rcu_read_unlock(); + preempt_enable(); if (user_build_id) stack_map_get_build_id_offset(buf, trace_nr, user, may_fault); -- 2.48.1