Skip to content

Commit 95c5ea2

Browse files
theihorKernel Patches Daemon
authored and
Kernel Patches Daemon
committed
selftests/bpf: add test cases with CONST_PTR_TO_MAP null checks
A test requires the following to happen: * CONST_PTR_TO_MAP value is put on the stack * then this value is checked for null * the code in the null branch fails verification I was able to achieve this by using a stack allocated array of maps, populated with values from a global map. This is the first test case: map_ptr_is_never_null. The second test case (map_ptr_is_never_null_rb) involves an array of ringbufs and attempts to recreate a common coding pattern [1]. [1] https://lore.kernel.org/bpf/CAEf4BzZNU0gX_sQ8k8JaLe1e+Veth3Rk=4x7MDhv=hQxvO8EDw@mail.gmail.com/ Suggested-by: Andrii Nakryiko <[email protected]> Signed-off-by: Ihor Solodrai <[email protected]>
1 parent 6e1046a commit 95c5ea2

File tree

1 file changed

+77
-0
lines changed

1 file changed

+77
-0
lines changed

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

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

142+
SEC("socket")
143+
int map_ptr_is_never_null(void *ctx)
144+
{
145+
struct bpf_map *maps[2] = { 0 };
146+
struct bpf_map *map = NULL;
147+
int __attribute__((aligned(8))) key = 0;
148+
149+
for (key = 0; key < 2; key++) {
150+
map = bpf_map_lookup_elem(&map_in_map, &key);
151+
if (map)
152+
maps[key] = map;
153+
else
154+
return 0;
155+
}
156+
157+
/* After the loop every element of maps is CONST_PTR_TO_MAP so
158+
* the invalid branch should not be explored by the verifier.
159+
*/
160+
if (!maps[0])
161+
asm volatile ("r10 = 0;");
162+
163+
return 0;
164+
}
165+
166+
struct {
167+
__uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);
168+
__uint(max_entries, 1);
169+
__type(key, int);
170+
__type(value, int);
171+
__array(values, struct {
172+
__uint(type, BPF_MAP_TYPE_RINGBUF);
173+
__uint(max_entries, 4096);
174+
});
175+
} rb_in_map SEC(".maps");
176+
177+
struct rb_ctx {
178+
void *rb;
179+
struct bpf_dynptr dptr;
180+
};
181+
182+
static __always_inline struct rb_ctx __rb_event_reserve(__u32 sz)
183+
{
184+
struct rb_ctx rb_ctx = {};
185+
void *rb;
186+
__u32 cpu = bpf_get_smp_processor_id();
187+
__u32 rb_slot = cpu & 1;
188+
189+
rb = bpf_map_lookup_elem(&rb_in_map, &rb_slot);
190+
if (!rb)
191+
return rb_ctx;
192+
193+
rb_ctx.rb = rb;
194+
bpf_ringbuf_reserve_dynptr(rb, sz, 0, &rb_ctx.dptr);
195+
196+
return rb_ctx;
197+
}
198+
199+
static __noinline void __rb_event_submit(struct rb_ctx *ctx)
200+
{
201+
if (!ctx->rb)
202+
return;
203+
204+
/* If the verifier (incorrectly) concludes that ctx->rb can be
205+
* NULL at this point, we'll get "BPF_EXIT instruction in main
206+
* prog would lead to reference leak" error
207+
*/
208+
bpf_ringbuf_submit_dynptr(&ctx->dptr, 0);
209+
}
210+
211+
SEC("socket")
212+
int map_ptr_is_never_null_rb(void *ctx)
213+
{
214+
struct rb_ctx event_ctx = __rb_event_reserve(256);
215+
__rb_event_submit(&event_ctx);
216+
return 0;
217+
}
218+
142219
char _license[] SEC("license") = "GPL";

0 commit comments

Comments
 (0)