From 2aaf67f0516fde29620d0edfc29c01b9ea7ad430 Mon Sep 17 00:00:00 2001 From: Nicholas Dudar Date: Thu, 9 Jul 2026 11:58:36 -0400 Subject: [PATCH 1/2] bpf: Reject rdonly/rdwr_buf_size kfunc arguments that exceed u32 max check_kfunc_args() detects a kfunc argument named rdonly_buf_size or rdwr_buf_size and stores reg->var_off.value into meta->r0_size, a u64, and does not bound it. check_kfunc_call() later copies that value into the returned register's mem_size field: meta->r0_size = reg->var_off.value; ... regs[BPF_REG_0].mem_size = meta.r0_size; regs[BPF_REG_0].mem_size is u32. A constant whose upper 32 bits are set gets truncated instead of causing a load-time rejection, so the verifier records a PTR_TO_MEM register with an approximately 4 GiB mem_size for whatever allocation the kfunc returned. A later access check against that register uses the truncated, wrong bound. Reject rdonly_buf_size/rdwr_buf_size values that exceed U32_MAX at the point meta->r0_size is set. Fixes: eb1f7f71c126 ("bpf/verifier: allow kfunc to return an allocated mem") Signed-off-by: Nicholas Dudar Acked-by: Eduard Zingerman Link: https://lore.kernel.org/bpf/20260709155837.1879230-2-main.kalliope@gmail.com Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Kumar Kartikeya Dwivedi --- kernel/bpf/verifier.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index a0830ad6bebb..03e2202cca13 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -12109,6 +12109,11 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_ } meta->r0_size = reg->var_off.value; + if (meta->r0_size > U32_MAX) { + verbose(env, "%s rdonly/rdwr_buf_size exceeds u32 max\n", + reg_arg_name(env, argno)); + return -EINVAL; + } if (regno >= 0) ret = mark_chain_precision(env, regno); else From 12556c3198328df38b3444978391141ad6f7092b Mon Sep 17 00:00:00 2001 From: Nicholas Dudar Date: Thu, 9 Jul 2026 11:58:37 -0400 Subject: [PATCH 2/2] selftests/bpf: Add test for oversized rdonly/rdwr_buf_size kfunc argument Add a load-failure test to the kfunc_call suite using the existing bpf_kfunc_call_test_get_rdwr_mem() test kfunc. Its rdwr_buf_size argument is a const int, so the test uses a 64-bit immediate load in inline asm to place 2^64 - 192 (0xffffffffffffff40) in the argument register. The verifier records r0_size from the full 64-bit register value, and the test asserts that BPF_PROG_LOAD rejects it with "rdonly/rdwr_buf_size exceeds u32 max". Signed-off-by: Nicholas Dudar Acked-by: Eduard Zingerman Link: https://lore.kernel.org/bpf/20260709155837.1879230-3-main.kalliope@gmail.com Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Kumar Kartikeya Dwivedi --- .../selftests/bpf/prog_tests/kfunc_call.c | 1 + .../selftests/bpf/progs/kfunc_call_fail.c | 33 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/tools/testing/selftests/bpf/prog_tests/kfunc_call.c b/tools/testing/selftests/bpf/prog_tests/kfunc_call.c index 3df07680f9e0..67a30bf69509 100644 --- a/tools/testing/selftests/bpf/prog_tests/kfunc_call.c +++ b/tools/testing/selftests/bpf/prog_tests/kfunc_call.c @@ -66,6 +66,7 @@ static struct kfunc_test_params kfunc_tests[] = { TC_FAIL(kfunc_call_test_get_mem_fail_rdonly, 0, "R0 cannot write into rdonly_mem"), TC_FAIL(kfunc_call_test_get_mem_fail_use_after_free, 0, "invalid mem access 'scalar'"), TC_FAIL(kfunc_call_test_get_mem_fail_oob, 0, "min value is outside of the allowed memory range"), + TC_FAIL(kfunc_call_test_get_mem_fail_oversized, 0, "rdonly/rdwr_buf_size exceeds u32 max"), TC_FAIL(kfunc_call_test_get_mem_fail_not_const, 0, "is not a const"), TC_FAIL(kfunc_call_test_mem_acquire_fail, 0, "acquire kernel function does not return PTR_TO_BTF_ID"), TC_FAIL(kfunc_call_test_pointer_arg_type_mismatch, 0, "R1 expected pointer to ctx, but got scalar"), diff --git a/tools/testing/selftests/bpf/progs/kfunc_call_fail.c b/tools/testing/selftests/bpf/progs/kfunc_call_fail.c index a1963497f0bf..6144ce3ff0b2 100644 --- a/tools/testing/selftests/bpf/progs/kfunc_call_fail.c +++ b/tools/testing/selftests/bpf/progs/kfunc_call_fail.c @@ -103,6 +103,39 @@ int kfunc_call_test_get_mem_fail_oob(struct __sk_buff *skb) return ret; } +SEC("?tc") +int kfunc_call_test_get_mem_fail_oversized(struct __sk_buff *skb) +{ + struct prog_test_ref_kfunc *pt; + unsigned long s = 0; + int *p = NULL; + int ret = 0; + + pt = bpf_kfunc_call_test_acquire(&s); + if (pt) { + /* + * rdwr_buf_size is a const int, so a C literal is narrowed to + * 32 bits before the call. Force the full 64-bit value 2^64 - 192 + * (0xffffffffffffff40, > U32_MAX) into the argument register with + * a 64-bit immediate load. The verifier records r0_size from the + * full register value and must reject it before that value is + * truncated into R0's u32 mem_size. + */ + asm volatile ( + "r1 = %[pt];" + "r2 = %[oversized] ll;" + "call %[get_rdwr_mem];" + "%[p] = r0;" + : [p] "=r"(p) + : [pt] "r"(pt), + [oversized] "i"(0xffffffffffffff40LL), + [get_rdwr_mem] "i"(bpf_kfunc_call_test_get_rdwr_mem) + : "r0", "r1", "r2", "r3", "r4", "r5"); + bpf_kfunc_call_test_release(pt); + } + return ret; +} + int not_const_size = 2 * sizeof(int); SEC("?tc")