Merge branch 'bpf-bound-rdonly-rdwr_buf_size-kfunc-return-size'

Nicholas Dudar says:

====================
bpf: bound rdonly/rdwr_buf_size kfunc return size

check_kfunc_args() stores a kfunc's rdonly_buf_size/rdwr_buf_size
argument into a u64 that check_kfunc_call() later narrows into the
returned register's u32 mem_size, so a value above U32_MAX truncates
instead of being rejected. Fix it and add a selftest for coverage.

Changelog:
----------
v1 -> v2 (v1 was a private report to security@kernel.org)

 * Split the fix and selftest into separate patches.
 * Target bpf-next instead of bpf.
====================

Link: https://patch.msgid.link/20260709155837.1879230-1-main.kalliope@gmail.com
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
This commit is contained in:
Kumar Kartikeya Dwivedi 2026-07-09 22:17:56 +02:00
commit a4553044d1
No known key found for this signature in database
GPG Key ID: 472D377B63542F83
3 changed files with 39 additions and 0 deletions

View File

@ -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

View File

@ -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"),

View File

@ -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")