BPF List
 help / color / mirror / Atom feed
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

  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