* [PATCH bpf] bpf: check cpu_possible() in per-cpu helpers
@ 2026-10-27 3:07 bo cao
0 siblings, 0 replies; 4+ messages in thread
From: bo cao @ 2026-10-27 3:07 UTC (permalink / raw)
To: bpf; +Cc: ast, daniel, andrii, netdev, stable
Two BPF helpers take a target CPU id as a raw u32 (arg_type
ARG_ANYTHING, so the verifier does not constrain it) and only reject
cpu >= nr_cpu_ids before the value is used as a per_cpu_ptr() index.
bpf_map_lookup_percpu_elem() dispatches to the per-cpu map
implementations, and all three of them (percpu array, percpu hash,
percpu LRU hash) check nothing beyond that upper bound:
if (cpu >= nr_cpu_ids)
return NULL;
...
return per_cpu_ptr(..., cpu);
bpf_per_cpu_ptr() has the same weak check before returning
per_cpu_ptr(ptr, cpu) for a kernel percpu variable.
Per-cpu memory is only allocated for possible CPUs, and
__per_cpu_offset[] is only initialized for them (on arm64,
setup_per_cpu_areas() walks for_each_possible_cpu). On a system
with a sparse possible mask - e.g. an arm64 guest with a device-tree
CPU hole, possible mask 0,2-3 with nr_cpu_ids = 4 - a call with the
hole CPU id passes the weak check, and per_cpu_ptr() computes the
returned pointer from the uninitialized __per_cpu_offset[hole].
For bpf_map_lookup_percpu_elem() the helper then hands the BPF
program a pointer outside any valid per-cpu chunk of the map: reads
leak the stale bytes through the map value, and writes corrupt
whatever the offset happens to land on. For bpf_per_cpu_ptr() the
leak target is kernel memory instead of map values (the return type
is marked MEM_RDONLY, so reads only).
Reproduced on an arm64 guest with possible mask 0,2-3 (Ubuntu
7.3.0-9-generic): a socket filter program passes the pointer
returned by bpf_map_lookup_percpu_elem(map, key, 1) on as the value
argument of bpf_map_update_elem(), and the copy inside
array_map_update_elem() faults on it:
pc : __pi_memcpy_generic+0x3c/0x230
lr : array_map_update_elem+0x148/0x2a8
Call trace:
__pi_memcpy_generic+0x3c/0x230 (P)
bpf_prog_835a4ba2ea6755f0_poc+0x90/0xc0
bpf_test_run+0x2d0/0x460
bpf_prog_test_run_skb+0x4ac/0x920
__sys_bpf+0x614/0xbe8
__arm64_sys_bpf+0x38/0x78
The syscall side of the same problem (BPF_F_CPU map operation flags)
was fixed by ed54bf564ac5 ("bpf: Fix BPF_F_CPU validation for sparse
CPU IDs"), which added cpu_possible() validation to
bpf_map_check_op_flags(). The helper paths do not go through that
function, so the same class of hole CPU id needs to be rejected
there as well.
Fix the map helper centrally in its dispatch so all map
implementations are covered, and fix bpf_per_cpu_ptr() in place.
Fixes: 07343110b293 ("bpf: add bpf_map_lookup_percpu_elem for percpu map")
Fixes: eaa6bcb71ef6 ("bpf: Introduce bpf_per_cpu_ptr()")
Cc: stable@vger.kernel.org
Signed-off-by: bo cao <treebacker@foxmail.com>
---
kernel/bpf/helpers.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
index 648abe1ba63a..f9f2bd665038 100644
--- a/kernel/bpf/helpers.c
+++ b/kernel/bpf/helpers.c
@@ -133,6 +133,10 @@ const struct bpf_func_proto bpf_map_peek_elem_proto = {
BPF_CALL_3(bpf_map_lookup_percpu_elem, struct bpf_map *, map, void *, key, u32, cpu)
{
WARN_ON_ONCE(!bpf_rcu_lock_held());
+
+ if (cpu >= nr_cpu_ids || !cpu_possible(cpu))
+ return 0;
+
return (unsigned long) map->ops->map_lookup_percpu_elem(map, key, cpu);
}
@@ -753,7 +757,7 @@ const struct bpf_func_proto bpf_copy_from_user_task_proto = {
BPF_CALL_2(bpf_per_cpu_ptr, const void *, ptr, u32, cpu)
{
- if (cpu >= nr_cpu_ids)
+ if (cpu >= nr_cpu_ids || !cpu_possible(cpu))
return (unsigned long)NULL;
return (unsigned long)per_cpu_ptr((const void __percpu *)(const uintptr_t)ptr, cpu);
--
2.39.5 (Apple Git-154)
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH bpf] bpf: check cpu_possible() in per-cpu helpers
@ 2026-10-27 3:30 bo cao
2026-10-27 4:01 ` bot+bpf-ci
0 siblings, 1 reply; 4+ messages in thread
From: bo cao @ 2026-10-27 3:30 UTC (permalink / raw)
To: bpf; +Cc: ast, daniel, andrii, netdev, stable
Three paths take a target CPU id as a raw u32 and only reject
cpu >= nr_cpu_ids before the value is used as a per_cpu_ptr() index.
bpf_map_lookup_percpu_elem() dispatches to the per-cpu map
implementations, and all three of them (percpu array, percpu hash,
percpu LRU hash) check nothing beyond that upper bound:
if (cpu >= nr_cpu_ids)
return NULL;
...
return per_cpu_ptr(..., cpu);
bpf_per_cpu_ptr() has the same weak check before returning
per_cpu_ptr(ptr, cpu) for a kernel percpu variable.
Note that the verifier rewrites calls to bpf_map_lookup_percpu_elem()
with a known map into direct calls to the map callback
(kernel/bpf/fixups.c, BPF_FUNC_map_lookup_percpu_elem case), so a
check in the helpers.c wrapper alone does not cover JITed programs;
the callbacks themselves must reject the hole CPU id.
Per-cpu memory is only allocated for possible CPUs, and
__per_cpu_offset[] is only initialized for them (on arm64,
setup_per_cpu_areas() walks for_each_possible_cpu). On a system
with a sparse possible mask - e.g. an arm64 guest with a device-tree
CPU hole, possible mask 0,2-3 with nr_cpu_ids = 4 - a lookup with
the hole CPU id passes the weak checks, and per_cpu_ptr() computes
the returned pointer from the uninitialized __per_cpu_offset[hole].
For bpf_map_lookup_percpu_elem() the returned pointer lies outside
any valid per-cpu chunk of the map: reads leak the stale bytes
through the map value, and writes corrupt whatever the offset
happens to land on. For bpf_per_cpu_ptr() the leak target is kernel
memory instead of map values (the return type is marked MEM_RDONLY,
so reads only).
Reproduced on an arm64 guest with possible mask 0,2-3 (Ubuntu
7.3.0-9-generic): a socket filter program passes the pointer
returned by bpf_map_lookup_percpu_elem(map, key, 1) on as the value
argument of bpf_map_update_elem(), and the copy inside
array_map_update_elem() faults on it:
pc : __pi_memcpy_generic+0x3c/0x230
lr : array_map_update_elem+0x148/0x2a8
Call trace:
__pi_memcpy_generic+0x3c/0x230 (P)
bpf_prog_835a4ba2ea6755f0_poc+0x90/0xc0
bpf_test_run+0x2d0/0x460
bpf_prog_test_run_skb+0x4ac/0x920
__sys_bpf+0x614/0xbe8
__arm64_sys_bpf+0x38/0x78
the memcpy source (x1 = 0000b7737ec7dc88) being the out-of-bounds
pointer the lookup returned for the hole CPU.
The syscall side of the same problem (BPF_F_CPU map operation flags)
was fixed by ed54bf564ac5 ("bpf: Fix BPF_F_CPU validation for sparse
CPU IDs"), which added cpu_possible() validation to
bpf_map_check_op_flags(). These paths do not go through that
function, so the same class of hole CPU id needs to be rejected here
as well.
Add the check to the three map callbacks (so the verifier's direct
call path is covered), keep it in the helper dispatch as a central
fallback, and fix bpf_per_cpu_ptr() in place.
Suggested-by: sashiko-bot <sashiko-bot@kernel.org>
Fixes: 07343110b293 ("bpf: add bpf_map_lookup_percpu_elem for percpu map")
Fixes: eaa6bcb71ef6 ("bpf: Introduce bpf_per_cpu_ptr()")
Cc: stable@vger.kernel.org
Signed-off-by: 平之 <treebacker@foxmail.com>
---
Changes in v2: add the cpu_possible() check to the three per-cpu map
callbacks as well, because the verifier rewrites helper calls with a
known map into direct calls to the callbacks (kernel/bpf/fixups.c)
and thus bypasses the helpers.c wrapper, as pointed out by
sashiko-bot.
Link: https://lore.kernel.org/bpf/sashiko-outbox-186048@kernel.org/
---
kernel/bpf/arraymap.c | 2 +-
kernel/bpf/hashtab.c | 4 ++--
kernel/bpf/helpers.c | 6 +++++-
3 files changed, 8 insertions(+), 4 deletions(-)
diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c
index 133f189bf6d2..eab03f985280 100644
--- a/kernel/bpf/arraymap.c
+++ b/kernel/bpf/arraymap.c
@@ -327,7 +327,7 @@ static void *percpu_array_map_lookup_percpu_elem(struct bpf_map *map, void *key,
struct bpf_array *array = container_of(map, struct bpf_array, map);
u32 index = *(u32 *)key;
- if (cpu >= nr_cpu_ids)
+ if (cpu >= nr_cpu_ids || !cpu_possible(cpu))
return NULL;
if (unlikely(index >= array->map.max_entries))
diff --git a/kernel/bpf/hashtab.c b/kernel/bpf/hashtab.c
index bf78e09f4d51..007279c16b77 100644
--- a/kernel/bpf/hashtab.c
+++ b/kernel/bpf/hashtab.c
@@ -2670,7 +2670,7 @@ static void *htab_percpu_map_lookup_percpu_elem(struct bpf_map *map, void *key,
{
struct htab_elem *l;
- if (cpu >= nr_cpu_ids)
+ if (cpu >= nr_cpu_ids || !cpu_possible(cpu))
return NULL;
l = __htab_map_lookup_elem(map, key);
@@ -2696,7 +2696,7 @@ static void *htab_lru_percpu_map_lookup_percpu_elem(struct bpf_map *map, void *k
{
struct htab_elem *l;
- if (cpu >= nr_cpu_ids)
+ if (cpu >= nr_cpu_ids || !cpu_possible(cpu))
return NULL;
l = __htab_map_lookup_elem(map, key);
diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
index 648abe1ba63a..f9f2bd665038 100644
--- a/kernel/bpf/helpers.c
+++ b/kernel/bpf/helpers.c
@@ -133,6 +133,10 @@ const struct bpf_func_proto bpf_map_peek_elem_proto = {
BPF_CALL_3(bpf_map_lookup_percpu_elem, struct bpf_map *, map, void *, key, u32, cpu)
{
WARN_ON_ONCE(!bpf_rcu_lock_held());
+
+ if (cpu >= nr_cpu_ids || !cpu_possible(cpu))
+ return 0;
+
return (unsigned long) map->ops->map_lookup_percpu_elem(map, key, cpu);
}
@@ -753,7 +757,7 @@ const struct bpf_func_proto bpf_copy_from_user_task_proto = {
BPF_CALL_2(bpf_per_cpu_ptr, const void *, ptr, u32, cpu)
{
- if (cpu >= nr_cpu_ids)
+ if (cpu >= nr_cpu_ids || !cpu_possible(cpu))
return (unsigned long)NULL;
return (unsigned long)per_cpu_ptr((const void __percpu *)(const uintptr_t)ptr, cpu);
--
2.39.5 (Apple Git-154)
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH bpf] bpf: check cpu_possible() in per-cpu helpers
2026-10-27 3:30 bo cao
@ 2026-10-27 4:01 ` bot+bpf-ci
0 siblings, 0 replies; 4+ messages in thread
From: bot+bpf-ci @ 2026-10-27 4:01 UTC (permalink / raw)
To: treebacker, bpf
Cc: ast, daniel, andrii, netdev, stable, ast, andrii, daniel,
martin.lau, eddyz87, yonghong.song, mason, ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 3658 bytes --]
> bpf: check cpu_possible() in per-cpu helpers
TL;DR:
- bpf_per_cpu_ptr() on percpu kptrs may still crash with a NULL dereference:
the verifier doesn't mark the result maybe-NULL
- commit message wrongly says reads only
- From/Signed-off-by names differ
> bpf: check cpu_possible() in per-cpu helpers
>
> [ ... ]
>
> Fixes: 07343110b293 ("bpf: add bpf_map_lookup_percpu_elem for percpu map")
> Fixes: eaa6bcb71ef6 ("bpf: Introduce bpf_per_cpu_ptr()")
> Cc: stable@vger.kernel.org
> Signed-off-by: 平之 <treebacker@foxmail.com>
The From: name is "bo cao" while the Signed-off-by: name is "平之", with the
same email address. Could the two be made to match?
> diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
> index 648abe1ba63a..f9f2bd665038 100644
> --- a/kernel/bpf/helpers.c
> +++ b/kernel/bpf/helpers.c
[ ... ]
> @@ -753,7 +757,7 @@ const struct bpf_func_proto bpf_copy_from_user_task_proto = {
>
> BPF_CALL_2(bpf_per_cpu_ptr, const void *, ptr, u32, cpu)
> {
> - if (cpu >= nr_cpu_ids)
> + if (cpu >= nr_cpu_ids || !cpu_possible(cpu))
> return (unsigned long)NULL;
Does this fully handle the percpu kptr case? With this change a hole CPU
makes bpf_per_cpu_ptr() return NULL, which is only safe if the verifier
requires the program to test the result.
For a percpu kptr allocated with bpf_percpu_obj_new(), check_helper_call()
takes the returns_cpu_specific_alloc_ptr path and drops ret_flag, and with
it PTR_MAYBE_NULL:
kernel/bpf/verifier.c:check_helper_call() {
...
if (returns_cpu_specific_alloc_ptr) {
regs[BPF_REG_0].type = PTR_TO_BTF_ID | MEM_ALLOC | MEM_RCU;
} else {
...
regs[BPF_REG_0].type = PTR_TO_BTF_ID | ret_flag;
}
...
}
Since R0 is never marked maybe-NULL, a program like this passes the
verifier:
p = e->pc; /* __percpu_kptr from a map value */
if (!p) return 0;
v = bpf_per_cpu_ptr(p, cpu); /* cpu is a hole CPU or >= nr_cpu_ids */
v->c = 1; /* no NULL test */
check_ptr_to_btf_access() allows the write because type_is_ptr_alloc_obj()
is true, and bpf_may_fault_on_deref() returns false for this type, so the
store is not rewritten to BPF_PROBE_MEM. At run time this writes to
NULL + offsetof(c) and causes a kernel NULL pointer oops.
The cpu >= nr_cpu_ids case already reached this NULL dereference before
this patch (since 01cc55af9388, "bpf: Add bpf_this_cpu_ptr/bpf_per_cpu_ptr
support for allocated percpu obj"). A hole CPU id previously resulted in a
write through a bogus per-cpu address, and now takes the same NULL
dereference path.
Would keeping PTR_MAYBE_NULL on R0 for bpf_per_cpu_ptr() complete the fix,
for example:
PTR_TO_BTF_ID | MEM_ALLOC | MEM_RCU | (ret_flag & PTR_MAYBE_NULL)
If that is done, a Fixes: tag for 01cc55af9388 would also apply.
Separately, the commit message says:
> For bpf_per_cpu_ptr() the leak target is kernel
> memory instead of map values (the return type is marked MEM_RDONLY,
> so reads only).
Is that accurate for percpu kptrs? The MEM_RDONLY statement holds for ksym
arguments, but tools/testing/selftests/bpf/progs/percpu_alloc_array.c writes
through the result:
v = bpf_per_cpu_ptr(p, 0);
...
v->c = 1;
So before this patch a hole CPU let a program write through a bogus
address, not only read. Should the changelog describe the write case?
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/40445395328
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH bpf] bpf: check cpu_possible() in per-cpu helpers
@ 2026-10-27 4:59 bo cao
0 siblings, 0 replies; 4+ messages in thread
From: bo cao @ 2026-10-27 4:59 UTC (permalink / raw)
To: bpf; +Cc: ast, daniel, andrii, netdev, stable
Two BPF helpers take a target CPU id as a raw u32 (arg_type
ARG_ANYTHING, so the verifier does not constrain it) and only reject
cpu >= nr_cpu_ids before the value is used as a per_cpu_ptr() index.
bpf_map_lookup_percpu_elem() dispatches to the per-cpu map
implementations, and all three of them (percpu array, percpu hash,
percpu LRU hash) check nothing beyond that upper bound:
if (cpu >= nr_cpu_ids)
return NULL;
...
return per_cpu_ptr(..., cpu);
bpf_per_cpu_ptr() has the same weak check before returning
per_cpu_ptr(ptr, cpu).
Note that the verifier rewrites calls to bpf_map_lookup_percpu_elem()
with a known map into direct calls to the map callback
(kernel/bpf/fixups.c, BPF_FUNC_map_lookup_percpu_elem case), so a
check in the helpers.c wrapper alone does not cover JITed programs;
the callbacks themselves must reject the hole CPU id.
Per-cpu memory is only allocated for possible CPUs, and
__per_cpu_offset[] is only initialized for them (on arm64,
setup_per_cpu_areas() walks for_each_possible_cpu). On a system
with a sparse possible mask - e.g. an arm64 guest with a device-tree
CPU hole, possible mask 0,2-3 with nr_cpu_ids = 4 - a call with the
hole CPU id passes the weak checks, and per_cpu_ptr() computes the
returned pointer from the uninitialized __per_cpu_offset[hole].
For bpf_map_lookup_percpu_elem() the returned pointer lies outside
any valid per-cpu chunk of the map: reads leak the stale bytes
through the map value, and writes corrupt whatever the offset
happens to land on. For bpf_per_cpu_ptr() on a ksym the result is
read-only (the proto marks it MEM_RDONLY), but on a percpu kptr
allocated with bpf_percpu_obj_new() the result is writable, so a
hole CPU id let a program write through a bogus address as well.
Reproduced on an arm64 guest with possible mask 0,2-3 (Ubuntu
7.3.0-9-generic): a socket filter program passes the pointer
returned by bpf_map_lookup_percpu_elem(map, key, 1) on as the value
argument of bpf_map_update_elem(), and the copy inside
array_map_update_elem() faults on it:
pc : __pi_memcpy_generic+0x3c/0x230
lr : array_map_update_elem+0x148/0x2a8
Call trace:
__pi_memcpy_generic+0x3c/0x230 (P)
bpf_prog_835a4ba2ea6755f0_poc+0x90/0xc0
bpf_test_run+0x2d0/0x460
bpf_prog_test_run_skb+0x4ac/0x920
__sys_bpf+0x614/0xbe8
__arm64_sys_bpf+0x38/0x78
the memcpy source (x1 = 0000b7737ec7dc88) being the out-of-bounds
pointer the lookup returned for the hole CPU.
The syscall side of the same problem (BPF_F_CPU map operation flags)
was fixed by ed54bf564ac5 ("bpf: Fix BPF_F_CPU validation for sparse
CPU IDs"), which added cpu_possible() validation to
bpf_map_check_op_flags(). These paths do not go through that
function, so the same class of hole CPU id needs to be rejected here
as well.
bpf_per_cpu_ptr() has returned NULL for cpu >= nr_cpu_ids since its
introduction, but for a percpu obj the verifier drops PTR_MAYBE_NULL
from R0 (check_helper_call(), returns_cpu_specific_alloc_ptr path),
so a program that skips the NULL test and writes through the result
passes verification and oopses on NULL + offset at run time - a
problem on its own since 01cc55af9388, no sparse CPU mask required.
Keep PTR_MAYBE_NULL on R0 in that path so the verifier requires the
NULL test; the existing selftests already check, so they keep
passing. This also makes the new NULL return for a hole CPU safe.
Add the check to the three map callbacks (so the verifier's direct
call path is covered), keep it in the helper dispatch as a central
fallback, fix bpf_per_cpu_ptr() in place, and keep PTR_MAYBE_NULL
on R0 for percpu obj results.
Fixes: 07343110b293 ("bpf: add bpf_map_lookup_percpu_elem for percpu map")
Fixes: eaa6bcb71ef6 ("bpf: Introduce bpf_per_cpu_ptr()")
Fixes: 01cc55af9388 ("bpf: Add bpf_this_cpu_ptr/bpf_per_cpu_ptr support for allocated percpu obj")
Cc: stable@vger.kernel.org
Signed-off-by: bo cao <treebacker@foxmail.com>
---
Changes in v3:
- rename author and Signed-off-by to match the sender (bo cao).
- drop the incorrect "MEM_RDONLY, so reads only" claim for
bpf_per_cpu_ptr(): it only holds for ksym arguments; percpu kptr
results are writable.
- keep PTR_MAYBE_NULL on R0 for bpf_per_cpu_ptr() on percpu obj:
check_helper_call() drops the flag on the
returns_cpu_specific_alloc_ptr path, so the NULL return (for
cpu >= nr_cpu_ids since 01cc55af9388, and now also for a hole
CPU) let a non-checking program oops on NULL + offset. With the
flag kept, the verifier requires the NULL test; existing selftests
already check.
Changes in v2: add the cpu_possible() check to the three per-cpu map
callbacks as well, because the verifier rewrites helper calls with a
known map into direct calls to the callbacks (kernel/bpf/fixups.c)
and thus bypasses the helpers.c wrapper.
Link: https://lore.kernel.org/bpf/b3ddc3a43512b33fb503585d34dd75ef62420ea0e0d9f0ceecd6262f79fcb9d0@mail.kernel.org/
---
kernel/bpf/arraymap.c | 2 +-
kernel/bpf/hashtab.c | 4 ++--
kernel/bpf/helpers.c | 6 +++++-
kernel/bpf/verifier.c | 3 ++-
4 files changed, 10 insertions(+), 5 deletions(-)
diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c
index 133f189bf6d2..eab03f985280 100644
--- a/kernel/bpf/arraymap.c
+++ b/kernel/bpf/arraymap.c
@@ -327,7 +327,7 @@ static void *percpu_array_map_lookup_percpu_elem(struct bpf_map *map, void *key,
struct bpf_array *array = container_of(map, struct bpf_array, map);
u32 index = *(u32 *)key;
- if (cpu >= nr_cpu_ids)
+ if (cpu >= nr_cpu_ids || !cpu_possible(cpu))
return NULL;
if (unlikely(index >= array->map.max_entries))
diff --git a/kernel/bpf/hashtab.c b/kernel/bpf/hashtab.c
index bf78e09f4d51..007279c16b77 100644
--- a/kernel/bpf/hashtab.c
+++ b/kernel/bpf/hashtab.c
@@ -2670,7 +2670,7 @@ static void *htab_percpu_map_lookup_percpu_elem(struct bpf_map *map, void *key,
{
struct htab_elem *l;
- if (cpu >= nr_cpu_ids)
+ if (cpu >= nr_cpu_ids || !cpu_possible(cpu))
return NULL;
l = __htab_map_lookup_elem(map, key);
@@ -2696,7 +2696,7 @@ static void *htab_lru_percpu_map_lookup_percpu_elem(struct bpf_map *map, void *k
{
struct htab_elem *l;
- if (cpu >= nr_cpu_ids)
+ if (cpu >= nr_cpu_ids || !cpu_possible(cpu))
return NULL;
l = __htab_map_lookup_elem(map, key);
diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
index 648abe1ba63a..f9f2bd665038 100644
--- a/kernel/bpf/helpers.c
+++ b/kernel/bpf/helpers.c
@@ -133,6 +133,10 @@ const struct bpf_func_proto bpf_map_peek_elem_proto = {
BPF_CALL_3(bpf_map_lookup_percpu_elem, struct bpf_map *, map, void *, key, u32, cpu)
{
WARN_ON_ONCE(!bpf_rcu_lock_held());
+
+ if (cpu >= nr_cpu_ids || !cpu_possible(cpu))
+ return 0;
+
return (unsigned long) map->ops->map_lookup_percpu_elem(map, key, cpu);
}
@@ -753,7 +757,7 @@ const struct bpf_func_proto bpf_copy_from_user_task_proto = {
BPF_CALL_2(bpf_per_cpu_ptr, const void *, ptr, u32, cpu)
{
- if (cpu >= nr_cpu_ids)
+ if (cpu >= nr_cpu_ids || !cpu_possible(cpu))
return (unsigned long)NULL;
return (unsigned long)per_cpu_ptr((const void __percpu *)(const uintptr_t)ptr, cpu);
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 5601d2056218..0cedf39ee226 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -12957,7 +12957,8 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
regs[BPF_REG_0].mem_size = tsize;
} else {
if (returns_cpu_specific_alloc_ptr) {
- regs[BPF_REG_0].type = PTR_TO_BTF_ID | MEM_ALLOC | MEM_RCU;
+ regs[BPF_REG_0].type = PTR_TO_BTF_ID | MEM_ALLOC | MEM_RCU |
+ (ret_flag & PTR_MAYBE_NULL);
} else {
/* MEM_RDONLY may be carried from ret_flag, but it
* doesn't apply on PTR_TO_BTF_ID. Fold it, otherwise
--
2.39.5 (Apple Git-154)
^ permalink raw reply related [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-10-27 4:59 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-27 3:07 [PATCH bpf] bpf: check cpu_possible() in per-cpu helpers bo cao
-- strict thread matches above, loose matches on Subject: below --
2026-10-27 3:30 bo cao
2026-10-27 4:01 ` bot+bpf-ci
2026-10-27 4:59 bo cao
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox