From: Jiri Olsa <olsajiri@gmail.com>
To: Ihor Solodrai <ihor.solodrai@linux.dev>
Cc: Alexei Starovoitov <ast@kernel.org>,
Daniel Borkmann <daniel@iogearbox.net>,
Andrii Nakryiko <andrii@kernel.org>,
stable@vger.kernel.org, Tao Chen <chen.dylane@linux.dev>,
STAR Labs SG <info@starlabs.sg>,
bpf@vger.kernel.org, Martin KaFai Lau <martin.lau@linux.dev>,
Eduard Zingerman <eddyz87@gmail.com>,
Song Liu <songliubraving@fb.com>, Yonghong Song <yhs@fb.com>,
Quentin Monnet <qmo@kernel.org>
Subject: Re: [PATCH bpf-next 9/9] bpf: Disable preemption in __bpf_get_stack
Date: Mon, 27 Jul 2026 12:10:47 +0200 [thread overview]
Message-ID: <amcup1unyLB6rXZ3@krava> (raw)
In-Reply-To: <4aa21cf3-0ed6-424c-9c96-425e2e5ed586@linux.dev>
On Fri, Jul 24, 2026 at 01:23:21PM -0700, Ihor Solodrai wrote:
> On 2026-07-20 1:53 a.m., Jiri Olsa wrote:
> > From: Daniel Borkmann <borkmann@iogearbox.net>
> >
> > get_perf_callchain() returns a per-CPU perf_callchain_entry buffer and
> > releases its recursion slot via put_callchain_entry() before returning,
> > so nothing keeps the entry reserved while __bpf_get_stack() consumes
> > it below.
> >
> > A preemptible BPF program (e.g. a non-sleepable raw tracepoint program
> > on a PREEMPT kernel, which runs under migrate_disable() but not
> > preempt_disable()) can be scheduled out between obtaining the entry
> > and the copy. Another task scheduled on the same CPU then reuses the
> > same per-CPU buffer and overwrites trace->nr with a larger value.
> > copy_len is then computed from the inflated trace->nr and can exceed
> > the caller's buffer, causing an out-of-bounds write in the memcpy()
> > and in the build_id path.
> >
> > The rcu_read_lock() previously taken here does not prevent this. It is
> > only taken on the may_fault path, and under CONFIG_PREEMPT_RCU it does
> > not disable preemption; it merely keeps perf's callchain buffer array
> > alive (freed via call_rcu()) and does nothing to stop another task
> > from reusing the entry.
> >
> > Disable preemption around obtaining the callchain entry and copying
> > it into the caller's buffer, so the entry cannot be reused underneath
> > us and trace->nr stays bounded by max_depth. Build ID resolution may
> > fault and is therefore deferred until after preemption is re-enabled;
> > by then the instruction pointers have already been copied into buf,
> > so it operates only on that private copy. Note, preempt_disable() also
> > subsumes the buffer-lifetime guarantee the rcu_read_lock() provided,
> > since a preempt-disabled section is an RCU read-side critical section
> > for the callchain buffers' call_rcu() reclaim.
> >
> > Cc: stable@vger.kernel.org
> > Fixes: c195651e565a ("bpf: add bpf_get_stack helper")
> > Reported-by: Tao Chen <chen.dylane@linux.dev>
> > Closes: https://lore.kernel.org/bpf/20260206090653.1336687-1-chen.dylane@linux.dev/
> > Reported-by: STAR Labs SG <info@starlabs.sg>
> > Signed-off-by: Daniel Borkmann <borkmann@iogearbox.net>
> > [ changed Fixes: commit ]
> > Signed-off-by: Jiri Olsa <jolsa@kernel.org>
> > ---
> > kernel/bpf/stackmap.c | 9 +++------
> > 1 file changed, 3 insertions(+), 6 deletions(-)
> >
> > diff --git a/kernel/bpf/stackmap.c b/kernel/bpf/stackmap.c
> > index 57cd4c33403b..37f8e46319b3 100644
> > --- a/kernel/bpf/stackmap.c
> > +++ b/kernel/bpf/stackmap.c
> > @@ -819,8 +819,7 @@ static long __bpf_get_stack(struct pt_regs *regs, struct task_struct *task,
> > max_depth = stack_map_calculate_max_depth(size, elem_size, flags);
> > - if (may_fault)
> > - rcu_read_lock(); /* need RCU for perf's callchain below */
> > + preempt_disable();
>
> With the series applied on bpf-ci-like kconfig I get a "suspicious RCU
> usage" splat on BPF selftests, pasted at the bottom.
>
> AFAIU, the rcu_read_lock() that's removed here was not only
> controlling the lifetime (which is now covered by preempt_disable),
> but also putting the RCU read-side annotation for the
>
> entries = rcu_dereference(callchain_cpus_entries);
>
> in get_callchain_entry() (callchain.c:163).
>
> preempt_disable() never takes rcu_lock_map, and a sleepable program
> holds only rcu_read_lock_trace() (rcu_tasks_trace_srcu_struct), which
> is a different lockmap. So on PREEMPT_RCU + PROVE_RCU the
> rcu_dereference_check() there now fails (see the splat).
>
> The non-sleepable path is fine because it enters with rcu_read_lock()
> held via rcu_read_lock_dont_migrate().
>
> I think to fix this we have to keep if (may_fault) rcu_read_lock();
> alongside preempt_disable(). Something like this:
>
> diff --git a/kernel/bpf/stackmap.c b/kernel/bpf/stackmap.c
> index 37f8e46319b3..6be413072ef6 100644
> --- a/kernel/bpf/stackmap.c
> +++ b/kernel/bpf/stackmap.c
> @@ -820,6 +820,8 @@ static long __bpf_get_stack(struct pt_regs *regs, struct
> task_struct *task,
> max_depth = stack_map_calculate_max_depth(size, elem_size, flags);
>
> preempt_disable();
> + if (may_fault)
> + rcu_read_lock();
>
> if (kernel && task) {
> trace = get_callchain_entry_for_task(task, max_depth);
> @@ -829,6 +831,8 @@ static long __bpf_get_stack(struct pt_regs *regs, struct
> task_struct *task,
> }
>
> if (unlikely(!trace) || trace->nr < skip) {
> + if (may_fault)
> + rcu_read_unlock();
> preempt_enable();
> goto err_fault;
> }
> @@ -836,6 +840,8 @@ static long __bpf_get_stack(struct pt_regs *regs, struct
> task_struct *task,
> trace_nr = callchain_store(trace, buf, size, elem_size, flags);
>
> /* trace should not be dereferenced after this point */
> + if (may_fault)
> + rcu_read_unlock();
> preempt_enable();
>
> return callchain_finalize(buf, size, trace_nr, elem_size,
> user_build_id, user, may_fault);
>
>
> I confirmed this diff fixes the splat below.
yes, I managed to reproduce it as well.. basically just to add
the preemption_disable/enable hunks and keey rcu locks in place
>
> I guess teaching the perf side to accept rcu_read_lock_sched_held()
> would also work, but that's out of scope for a bpf fix and may be no
> less tricky.
will check, but you might be right it'd be tricky
thanks,
jirka
next prev parent reply other threads:[~2026-07-27 10:10 UTC|newest]
Thread overview: 26+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-20 8:53 [PATCH bpf-next 0/9] bpf: Disable preemption in stack map code Jiri Olsa
2026-07-20 8:53 ` [PATCH bpf-next 1/9] bpf: Factor stackid_init function from __bpf_get_stackid Jiri Olsa
2026-07-20 8:53 ` [PATCH bpf-next 2/9] bpf: Factor stackid_fastpath " Jiri Olsa
2026-07-20 9:09 ` sashiko-bot
2026-07-20 20:31 ` Jiri Olsa
2026-07-20 8:53 ` [PATCH bpf-next 3/9] bpf: Factor stackid_new_bucket " Jiri Olsa
2026-07-20 9:01 ` sashiko-bot
2026-07-20 20:31 ` Jiri Olsa
2026-07-20 8:53 ` [PATCH bpf-next 4/9] bpf: Use stack id functions instead of __bpf_get_stackid Jiri Olsa
2026-07-20 8:53 ` [PATCH bpf-next 5/9] bpf: Disable preemption in bpf_get_stackid Jiri Olsa
2026-07-20 9:04 ` sashiko-bot
2026-07-20 20:31 ` Jiri Olsa
2026-07-20 8:53 ` [PATCH bpf-next 6/9] bpf: Factor callchain_store function from __bpf_get_stack Jiri Olsa
2026-07-20 9:07 ` sashiko-bot
2026-07-20 20:31 ` Jiri Olsa
2026-07-20 8:53 ` [PATCH bpf-next 7/9] bpf: Factor callchain_finalize " Jiri Olsa
2026-07-20 9:08 ` sashiko-bot
2026-07-20 20:31 ` Jiri Olsa
2026-07-20 8:53 ` [PATCH bpf-next 8/9] bpf: Remove trace_in argument " Jiri Olsa
2026-07-20 9:09 ` sashiko-bot
2026-07-20 20:31 ` Jiri Olsa
2026-07-20 8:53 ` [PATCH bpf-next 9/9] bpf: Disable preemption in __bpf_get_stack Jiri Olsa
2026-07-20 9:18 ` sashiko-bot
2026-07-24 20:23 ` Ihor Solodrai
2026-07-27 10:10 ` Jiri Olsa [this message]
2026-07-24 20:20 ` [PATCH bpf-next 0/9] bpf: Disable preemption in stack map code Ihor Solodrai
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=amcup1unyLB6rXZ3@krava \
--to=olsajiri@gmail.com \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=chen.dylane@linux.dev \
--cc=daniel@iogearbox.net \
--cc=eddyz87@gmail.com \
--cc=ihor.solodrai@linux.dev \
--cc=info@starlabs.sg \
--cc=martin.lau@linux.dev \
--cc=qmo@kernel.org \
--cc=songliubraving@fb.com \
--cc=stable@vger.kernel.org \
--cc=yhs@fb.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox