Skip to content

bpf: make reg_not_null() true for CONST_PTR_TO_MAP #9042

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion kernel/bpf/verifier.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
77 changes: 77 additions & 0 deletions tools/testing/selftests/bpf/progs/verifier_map_in_map.c
Original file line number Diff line number Diff line change
Expand Up @@ -139,4 +139,81 @@ __naked void on_the_inner_map_pointer(void)
: __clobber_all);
}

SEC("socket")
int map_ptr_is_never_null(void *ctx)
{
struct bpf_map *maps[2] = { 0 };
struct bpf_map *map = NULL;
int __attribute__((aligned(8))) key = 0;

for (key = 0; key < 2; key++) {
map = bpf_map_lookup_elem(&map_in_map, &key);
if (map)
maps[key] = map;
else
return 0;
}

/* After the loop every element of maps is CONST_PTR_TO_MAP so
* the invalid branch should not be explored by the verifier.
*/
if (!maps[0])
asm volatile ("r10 = 0;");

return 0;
}

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, 4096);
});
} 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";
19 changes: 18 additions & 1 deletion tools/testing/selftests/bpf/progs/verifier_unpriv.c
Original file line number Diff line number Diff line change
Expand Up @@ -619,7 +619,7 @@ __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)
{
Expand All @@ -634,6 +634,23 @@ 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 = 0; \
r1 = %[map_hash_8b] ll; \
if r1 == 0xdeadbeef 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")
Expand Down
Loading