From 12556c3198328df38b3444978391141ad6f7092b Mon Sep 17 00:00:00 2001 From: Nicholas Dudar Date: Thu, 9 Jul 2026 11:58:37 -0400 Subject: [PATCH] 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")