Re: [PATCHv2 bpf-next 01/11] bpf: Factor stackid_init function from __bpf_get_stackid

Jiri Olsa <[email protected]> Thu, 30 Jul 2026 14:04:28 +0200
Newsgroups dev.linux.lists.sashiko-reviews,org.kernel.vger.bpf
Message-ID <ams9zOlhHI72-Kfs@krava>
On Wed, Jul 29, 2026 at 08:51:36AM +0000, [email protected] wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> 
> Pre-existing issues:
> - [High] A TOCTOU race with `sysctl_perf_event_max_stack` causes an integer underflow in the `max_depth - skip` calculation, leading to a massive heap buffer overflow when copying stack traces.
> --
> 
> --- Patch [1]: [PATCHv2 bpf-next 01/11] bpf: Factor stackid_init function from __bpf_get_stackid ---
> commit 94626e7c900d701c2d2326e06f48ebc28e1f6e93
> Author: Jiri Olsa <[email protected]>
> 
> bpf: Factor stackid_init function from __bpf_get_stackid
> 
> The new stackid_init function stores all the necessary bits for stackid
> trace and it will be used by other functions in following changes.
> 
> > diff --git a/kernel/bpf/stackmap.c b/kernel/bpf/stackmap.c
> > index 41fe87d7302f2..0eafe55b18289 100644
> > --- a/kernel/bpf/stackmap.c
> > +++ b/kernel/bpf/stackmap.c
> > @@ -504,33 +504,54 @@ get_callchain_entry_for_task(struct task_struct *task, u32 max_depth)
> [ ... ]
> > +static int stackid_init(struct stackid *stackid, struct bpf_map *map,
> > +			struct perf_callchain_entry *trace, u64 flags)
> >  {
> >  	struct bpf_stack_map *smap = container_of(map, struct bpf_stack_map, map);
> > -	struct stack_map_bucket *bucket, *new_bucket, *old_bucket;
> > -	u32 hash, id, trace_nr, trace_len, i, max_depth;
> >  	u32 skip = flags & BPF_F_SKIP_FIELD_MASK;
> > -	bool user = flags & BPF_F_USER_STACK;
> > -	u64 *ips;
> > -	bool hash_matches;
> > +	u32 max_depth;
> >  
> >  	if (trace->nr <= skip)
> >  		/* skipping more than usable stack trace */
> >  		return -EFAULT;
> >  
> >  	max_depth = stack_map_calculate_max_depth(map->value_size, stack_map_data_size(map), flags);
> > -	trace_nr = min_t(u32, trace->nr - skip, max_depth - skip);
> > -	trace_len = trace_nr * sizeof(u64);
> > -	ips = trace->ip + skip;
> > -	hash = jhash2((u32 *)ips, trace_len / sizeof(u32), 0);
> > -	id = hash & (smap->n_buckets - 1);
> > -	bucket = READ_ONCE(smap->buckets[id]);
> > +	stackid->nr = min_t(u32, trace->nr - skip, max_depth - skip);
> 
> [Severity: High]
> This is a pre-existing issue, but can this max_depth - skip calculation
> underflow?
> 
> If a BPF program is attached to a perf event using bpf_get_stackid_pe(),
> the callchain is generated using the current sysctl_perf_event_max_stack
> limit. If an administrator concurrently reduces sysctl_perf_event_max_stack to
> a value smaller than the skip parameter, stack_map_calculate_max_depth()
> clamps max_depth to this new smaller sysctl value:
> 
> kernel/bpf/stackmap.c:stack_map_calculate_max_depth() {
>     ...
>     u32 curr_sysctl_max_stack = READ_ONCE(sysctl_perf_event_max_stack);
>     ...
>     if (max_depth > curr_sysctl_max_stack)
>         return curr_sysctl_max_stack;
>     return max_depth;
> }
> 
> This would cause max_depth - skip to underflow into a huge u32 value. Since
> min_t() is used with u32, a large trace->nr - skip could be selected, which
> might exceed the stackmap bucket capacity and lead to a heap buffer overflow
> when copying the stack trace later in __bpf_get_stackid():
> 
>     memcpy(new_bucket->data, stackid->ips, stackid->len);
> 
> Does this sequence need to be protected against sysctl changes between trace
> gathering and max_depth calculation?

not possible, replied in previous version

jirka