Skip to content

Commit f3effef

Browse files
committed
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/[email protected]/ v2: https://lore.kernel.org/bpf/[email protected]/ v1: https://lore.kernel.org/bpf/[email protected]/ ==================== Link: https://patch.msgid.link/[email protected] Signed-off-by: Andrii Nakryiko <[email protected]>
2 parents 97ebac5 + 260b862 commit f3effef

File tree

3 files changed

+137
-3
lines changed

3 files changed

+137
-3
lines changed

kernel/bpf/verifier.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,8 @@ static bool reg_not_null(const struct bpf_reg_state *reg)
405405
type == PTR_TO_MAP_KEY ||
406406
type == PTR_TO_SOCK_COMMON ||
407407
(type == PTR_TO_BTF_ID && is_trusted_reg(reg)) ||
408-
type == PTR_TO_MEM;
408+
type == PTR_TO_MEM ||
409+
type == CONST_PTR_TO_MAP;
409410
}
410411

411412
static struct btf_record *reg_btf_record(const struct bpf_reg_state *reg)

tools/testing/selftests/bpf/progs/verifier_map_in_map.c

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,4 +139,122 @@ __naked void on_the_inner_map_pointer(void)
139139
: __clobber_all);
140140
}
141141

142+
SEC("socket")
143+
__description("map_ptr is never null")
144+
__success
145+
__naked void map_ptr_is_never_null(void)
146+
{
147+
asm volatile (" \
148+
r0 = 0; \
149+
r1 = %[map_in_map] ll; \
150+
if r1 != 0 goto l0_%=; \
151+
r10 = 42; \
152+
l0_%=: exit; \
153+
" :
154+
: __imm(bpf_map_lookup_elem),
155+
__imm_addr(map_in_map)
156+
: __clobber_all);
157+
}
158+
159+
SEC("socket")
160+
__description("map_ptr is never null inner")
161+
__success
162+
__naked void map_ptr_is_never_null_inner(void)
163+
{
164+
asm volatile (" \
165+
r1 = 0; \
166+
*(u32*)(r10 - 4) = r1; \
167+
r2 = r10; \
168+
r2 += -4; \
169+
r1 = %[map_in_map] ll; \
170+
call %[bpf_map_lookup_elem]; \
171+
if r0 == 0 goto l0_%=; \
172+
if r0 != 0 goto l0_%=; \
173+
r10 = 42; \
174+
l0_%=: exit; \
175+
" :
176+
: __imm(bpf_map_lookup_elem),
177+
__imm_addr(map_in_map)
178+
: __clobber_all);
179+
}
180+
181+
SEC("socket")
182+
__description("map_ptr is never null inner spill fill")
183+
__success
184+
__naked void map_ptr_is_never_null_inner_spill_fill(void)
185+
{
186+
asm volatile (" \
187+
r1 = 0; \
188+
*(u32*)(r10 - 4) = r1; \
189+
r2 = r10; \
190+
r2 += -4; \
191+
r1 = %[map_in_map] ll; \
192+
call %[bpf_map_lookup_elem]; \
193+
if r0 != 0 goto l0_%=; \
194+
exit; \
195+
l0_%=: *(u64 *)(r10 -16) = r0; \
196+
r1 = *(u64 *)(r10 -16); \
197+
if r1 == 0 goto l1_%=; \
198+
exit; \
199+
l1_%=: r10 = 42; \
200+
exit; \
201+
" :
202+
: __imm(bpf_map_lookup_elem),
203+
__imm_addr(map_in_map)
204+
: __clobber_all);
205+
}
206+
207+
struct {
208+
__uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);
209+
__uint(max_entries, 1);
210+
__type(key, int);
211+
__type(value, int);
212+
__array(values, struct {
213+
__uint(type, BPF_MAP_TYPE_RINGBUF);
214+
__uint(max_entries, 64 * 1024);
215+
});
216+
} rb_in_map SEC(".maps");
217+
218+
struct rb_ctx {
219+
void *rb;
220+
struct bpf_dynptr dptr;
221+
};
222+
223+
static __always_inline struct rb_ctx __rb_event_reserve(__u32 sz)
224+
{
225+
struct rb_ctx rb_ctx = {};
226+
void *rb;
227+
__u32 cpu = bpf_get_smp_processor_id();
228+
__u32 rb_slot = cpu & 1;
229+
230+
rb = bpf_map_lookup_elem(&rb_in_map, &rb_slot);
231+
if (!rb)
232+
return rb_ctx;
233+
234+
rb_ctx.rb = rb;
235+
bpf_ringbuf_reserve_dynptr(rb, sz, 0, &rb_ctx.dptr);
236+
237+
return rb_ctx;
238+
}
239+
240+
static __noinline void __rb_event_submit(struct rb_ctx *ctx)
241+
{
242+
if (!ctx->rb)
243+
return;
244+
245+
/* If the verifier (incorrectly) concludes that ctx->rb can be
246+
* NULL at this point, we'll get "BPF_EXIT instruction in main
247+
* prog would lead to reference leak" error
248+
*/
249+
bpf_ringbuf_submit_dynptr(&ctx->dptr, 0);
250+
}
251+
252+
SEC("socket")
253+
int map_ptr_is_never_null_rb(void *ctx)
254+
{
255+
struct rb_ctx event_ctx = __rb_event_reserve(256);
256+
__rb_event_submit(&event_ctx);
257+
return 0;
258+
}
259+
142260
char _license[] SEC("license") = "GPL";

tools/testing/selftests/bpf/progs/verifier_unpriv.c

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -619,12 +619,11 @@ __naked void pass_pointer_to_tail_call(void)
619619

620620
SEC("socket")
621621
__description("unpriv: cmp map pointer with zero")
622-
__success __failure_unpriv __msg_unpriv("R1 pointer comparison")
622+
__success __success_unpriv
623623
__retval(0)
624624
__naked void cmp_map_pointer_with_zero(void)
625625
{
626626
asm volatile (" \
627-
r1 = 0; \
628627
r1 = %[map_hash_8b] ll; \
629628
if r1 == 0 goto l0_%=; \
630629
l0_%=: r0 = 0; \
@@ -634,6 +633,22 @@ l0_%=: r0 = 0; \
634633
: __clobber_all);
635634
}
636635

636+
SEC("socket")
637+
__description("unpriv: cmp map pointer with const")
638+
__success __failure_unpriv __msg_unpriv("R1 pointer comparison prohibited")
639+
__retval(0)
640+
__naked void cmp_map_pointer_with_const(void)
641+
{
642+
asm volatile (" \
643+
r1 = %[map_hash_8b] ll; \
644+
if r1 == 0x0000beef goto l0_%=; \
645+
l0_%=: r0 = 0; \
646+
exit; \
647+
" :
648+
: __imm_addr(map_hash_8b)
649+
: __clobber_all);
650+
}
651+
637652
SEC("socket")
638653
__description("unpriv: write into frame pointer")
639654
__failure __msg("frame pointer is read only")

0 commit comments

Comments
 (0)