Merge branch 'bpf-make-reg_not_null-true-for-const_ptr_to_map'
Ihor Solodrai says: ==================== bpf: make reg_not_null() true for CONST_PTR_TO_MAP Handle CONST_PTR_TO_MAP null checks in the BPF verifier. Add appropriate test cases. v3->v4: more test cases v2->v3: change constant in unpriv test v1->v2: add a test case with ringbufs v3: https://lore.kernel.org/bpf/20250604222729.3351946-1-isolodrai@meta.com/ v2: https://lore.kernel.org/bpf/20250604003759.1020745-1-isolodrai@meta.com/ v1: https://lore.kernel.org/bpf/20250523232503.1086319-1-isolodrai@meta.com/ ==================== Link: https://patch.msgid.link/20250609183024.359974-1-isolodrai@meta.com Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
This commit is contained in:
@@ -405,7 +405,8 @@ static bool reg_not_null(const struct bpf_reg_state *reg)
|
||||
type == PTR_TO_MAP_KEY ||
|
||||
type == PTR_TO_SOCK_COMMON ||
|
||||
(type == PTR_TO_BTF_ID && is_trusted_reg(reg)) ||
|
||||
type == PTR_TO_MEM;
|
||||
type == PTR_TO_MEM ||
|
||||
type == CONST_PTR_TO_MAP;
|
||||
}
|
||||
|
||||
static struct btf_record *reg_btf_record(const struct bpf_reg_state *reg)
|
||||
|
||||
@@ -139,4 +139,122 @@ __naked void on_the_inner_map_pointer(void)
|
||||
: __clobber_all);
|
||||
}
|
||||
|
||||
SEC("socket")
|
||||
__description("map_ptr is never null")
|
||||
__success
|
||||
__naked void map_ptr_is_never_null(void)
|
||||
{
|
||||
asm volatile (" \
|
||||
r0 = 0; \
|
||||
r1 = %[map_in_map] ll; \
|
||||
if r1 != 0 goto l0_%=; \
|
||||
r10 = 42; \
|
||||
l0_%=: exit; \
|
||||
" :
|
||||
: __imm(bpf_map_lookup_elem),
|
||||
__imm_addr(map_in_map)
|
||||
: __clobber_all);
|
||||
}
|
||||
|
||||
SEC("socket")
|
||||
__description("map_ptr is never null inner")
|
||||
__success
|
||||
__naked void map_ptr_is_never_null_inner(void)
|
||||
{
|
||||
asm volatile (" \
|
||||
r1 = 0; \
|
||||
*(u32*)(r10 - 4) = r1; \
|
||||
r2 = r10; \
|
||||
r2 += -4; \
|
||||
r1 = %[map_in_map] ll; \
|
||||
call %[bpf_map_lookup_elem]; \
|
||||
if r0 == 0 goto l0_%=; \
|
||||
if r0 != 0 goto l0_%=; \
|
||||
r10 = 42; \
|
||||
l0_%=: exit; \
|
||||
" :
|
||||
: __imm(bpf_map_lookup_elem),
|
||||
__imm_addr(map_in_map)
|
||||
: __clobber_all);
|
||||
}
|
||||
|
||||
SEC("socket")
|
||||
__description("map_ptr is never null inner spill fill")
|
||||
__success
|
||||
__naked void map_ptr_is_never_null_inner_spill_fill(void)
|
||||
{
|
||||
asm volatile (" \
|
||||
r1 = 0; \
|
||||
*(u32*)(r10 - 4) = r1; \
|
||||
r2 = r10; \
|
||||
r2 += -4; \
|
||||
r1 = %[map_in_map] ll; \
|
||||
call %[bpf_map_lookup_elem]; \
|
||||
if r0 != 0 goto l0_%=; \
|
||||
exit; \
|
||||
l0_%=: *(u64 *)(r10 -16) = r0; \
|
||||
r1 = *(u64 *)(r10 -16); \
|
||||
if r1 == 0 goto l1_%=; \
|
||||
exit; \
|
||||
l1_%=: r10 = 42; \
|
||||
exit; \
|
||||
" :
|
||||
: __imm(bpf_map_lookup_elem),
|
||||
__imm_addr(map_in_map)
|
||||
: __clobber_all);
|
||||
}
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);
|
||||
__uint(max_entries, 1);
|
||||
__type(key, int);
|
||||
__type(value, int);
|
||||
__array(values, struct {
|
||||
__uint(type, BPF_MAP_TYPE_RINGBUF);
|
||||
__uint(max_entries, 64 * 1024);
|
||||
});
|
||||
} rb_in_map SEC(".maps");
|
||||
|
||||
struct rb_ctx {
|
||||
void *rb;
|
||||
struct bpf_dynptr dptr;
|
||||
};
|
||||
|
||||
static __always_inline struct rb_ctx __rb_event_reserve(__u32 sz)
|
||||
{
|
||||
struct rb_ctx rb_ctx = {};
|
||||
void *rb;
|
||||
__u32 cpu = bpf_get_smp_processor_id();
|
||||
__u32 rb_slot = cpu & 1;
|
||||
|
||||
rb = bpf_map_lookup_elem(&rb_in_map, &rb_slot);
|
||||
if (!rb)
|
||||
return rb_ctx;
|
||||
|
||||
rb_ctx.rb = rb;
|
||||
bpf_ringbuf_reserve_dynptr(rb, sz, 0, &rb_ctx.dptr);
|
||||
|
||||
return rb_ctx;
|
||||
}
|
||||
|
||||
static __noinline void __rb_event_submit(struct rb_ctx *ctx)
|
||||
{
|
||||
if (!ctx->rb)
|
||||
return;
|
||||
|
||||
/* If the verifier (incorrectly) concludes that ctx->rb can be
|
||||
* NULL at this point, we'll get "BPF_EXIT instruction in main
|
||||
* prog would lead to reference leak" error
|
||||
*/
|
||||
bpf_ringbuf_submit_dynptr(&ctx->dptr, 0);
|
||||
}
|
||||
|
||||
SEC("socket")
|
||||
int map_ptr_is_never_null_rb(void *ctx)
|
||||
{
|
||||
struct rb_ctx event_ctx = __rb_event_reserve(256);
|
||||
__rb_event_submit(&event_ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
@@ -619,12 +619,11 @@ __naked void pass_pointer_to_tail_call(void)
|
||||
|
||||
SEC("socket")
|
||||
__description("unpriv: cmp map pointer with zero")
|
||||
__success __failure_unpriv __msg_unpriv("R1 pointer comparison")
|
||||
__success __success_unpriv
|
||||
__retval(0)
|
||||
__naked void cmp_map_pointer_with_zero(void)
|
||||
{
|
||||
asm volatile (" \
|
||||
r1 = 0; \
|
||||
r1 = %[map_hash_8b] ll; \
|
||||
if r1 == 0 goto l0_%=; \
|
||||
l0_%=: r0 = 0; \
|
||||
@@ -634,6 +633,22 @@ l0_%=: r0 = 0; \
|
||||
: __clobber_all);
|
||||
}
|
||||
|
||||
SEC("socket")
|
||||
__description("unpriv: cmp map pointer with const")
|
||||
__success __failure_unpriv __msg_unpriv("R1 pointer comparison prohibited")
|
||||
__retval(0)
|
||||
__naked void cmp_map_pointer_with_const(void)
|
||||
{
|
||||
asm volatile (" \
|
||||
r1 = %[map_hash_8b] ll; \
|
||||
if r1 == 0x0000beef goto l0_%=; \
|
||||
l0_%=: r0 = 0; \
|
||||
exit; \
|
||||
" :
|
||||
: __imm_addr(map_hash_8b)
|
||||
: __clobber_all);
|
||||
}
|
||||
|
||||
SEC("socket")
|
||||
__description("unpriv: write into frame pointer")
|
||||
__failure __msg("frame pointer is read only")
|
||||
|
||||
Reference in New Issue
Block a user