bpf: reject direct access to nullable PTR_TO_BUF pointers
Subsystems (4)
v3 cover bpf: reject direct access to nullable PTR_TO_BUF pointers
check_mem_access() allows direct dereference of PTR_TO_BUF | PTR_MAYBE_NULL without a null check, causing kernel NULL dereference on map iterator stop callbacks.
Patch 1 adds the missing type_may_be_null() guard. Patch 2 adds a selftest.
Changes in v3:
- Drop stack trace from commit log
- Split kernel fix and selftest into separate patches
- Use [PATCH bpf] prefix
Changes in v2:
- Add selftest
Qi Tang (2):
bpf: reject direct access to nullable PTR_TO_BUF pointers selftests/bpf: add test for nullable PTR_TO_BUF access
kernel/bpf/verifier.c | 3 ++- tools/testing/selftests/bpf/progs/iter_buf_null_fail.c | 51 +++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/bpf/progs/iter_buf_null_fail.c
PATCH v3 1/2 bpf: reject direct access to nullable PTR_TO_BUF pointers
check_mem_access() matches PTR_TO_BUF via base_type() which strips PTR_MAYBE_NULL, allowing direct dereference without a null check.
Map iterator ctx->key and ctx->value are PTR_TO_BUF | PTR_MAYBE_NULL. On stop callbacks these are NULL, causing a kernel NULL dereference.
Add a type_may_be_null() guard to the PTR_TO_BUF branch, matching the existing PTR_TO_BTF_ID pattern.
--- kernel/bpf/verifier.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
kernel/bpf/verifier.c +2 -1
@@ -7905,7 +7905,8 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn }else if (reg->type ==CONST_PTR_TO_MAP ) { err =check_ptr_to_map_access (env, regs, regno, off, size, t, value_regno); - }else if (base_type (reg->type) ==PTR_TO_BUF ) { + }else if (base_type (reg->type) ==PTR_TO_BUF && + !type_may_be_null (reg->type)) {bool rdonly_mem =type_is_rdonly_mem (reg->type);u32 *max_access;
Signature · 1 line
2.43.0
Quoted 7 lines · Qi Tang
check_mem_access()matchesPTR_TO_BUFviabase_type()which stripsPTR_MAYBE_NULL, allowing direct dereference without a null check.Map iterator
ctx->keyandctx->valuearePTR_TO_BUF|PTR_MAYBE_NULL. On stop callbacks these are NULL, causing a kernel NULL dereference.
Quoted 6 lines
Add a
type_may_be_null()guard to thePTR_TO_BUFbranch, matching the existingPTR_TO_BTF_IDpattern.Fixes 20b2aff4bc15 ("bpf: Introduce MEM_RDONLY flag")Signed-off-by Qi Tang <[email protected]>---
Quoted 2 lines
[...]
PATCH v3 2/2 selftests/bpf: add test for nullable PTR_TO_BUF access
In reply to Qi TangAdd iter_buf_null_fail.c with two tests:
- iter_buf_null_deref: verifier must reject direct dereference of
ctx->key (PTR_TO_BUF | PTR_MAYBE_NULL) without a null check
- iter_buf_null_check_ok: verifier must accept dereference after an explicit null check
--- .../selftests/bpf/progs/iter_buf_null_fail.c | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 tools/testing/selftests/bpf/progs/iter_buf_null_fail.c
tools/testing/selftests/bpf/progs/iter_buf_null_fail.c new file +51 -0
@@ -0,0 +1,51 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2026 Qi Tang */ + +#include <vmlinux.h> +#include <bpf/bpf_helpers.h> +#include "bpf_misc.h" + +char _license[] SEC("license") = "GPL"; + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 1); + __type(key, __u32); + __type(value, __u64); +} hashmap SEC(".maps"); + +/* +* Verify that the verifier rejects direct access to nullable PTR_TO_BUF +* (ctx->key) without a null check. On the iterator stop callback, +* ctx->key is NULL, so unconditional access would be a NULL deref. +*/ +SEC("iter/bpf_map_elem") +__failure __msg("invalid mem access") +int iter_buf_null_deref(struct bpf_iter__bpf_map_elem *ctx) +{ +/* ctx->key is PTR_TO_BUF | PTR_MAYBE_NULL | MEM_RDONLY. +* Direct access without null check must be rejected. +*/ +volatile __u32 v = *(__u32 *)ctx->key; + + (void )v; +return 0 ; +} + +/* +* Verify that access after a null check is still accepted. +*/ +SEC("iter/bpf_map_elem") +__success +int iter_buf_null_check_ok (struct bpf_iter__bpf_map_elem *ctx) +{ +__u32 *key = ctx->key; + +if (!key) +return 0 ; + +volatile __u32 v = *key; + + (void )v; +return 0 ; +}
Signature · 1 line
2.43.0
Kumar Kartikeya Dwivedi
maintainerRe: [PATCH bpf v3 2/2] selftests/bpf: add test for nullable PTR_TO_BUF access
Quoted 9 lines · Qi Tang
Add
iter_buf_null_fail.cwith two tests:
-iter_buf_null_deref: verifier must reject direct dereference ofctx->key (PTR_TO_BUF | PTR_MAYBE_NULL) without a null check-
iter_buf_null_check_ok: verifier must accept dereference after an explicit null checkSigned-off-by Qi Tang <[email protected]>---
Test looks correct but you forgot to actually invoke it, so it never actually runs. I verified it manually by including the skeleton and adding RUN_TESTS(iter_buf_null_fail) in prog_tests/iters.c
pw-bot: cr