@@ -28,6 +28,20 @@ struct {
__uint(map_flags, BPF_F_WRONLY_PROG);
} map_array_wo SEC(".maps");
+struct {
+ __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
+ __uint(max_entries, 2);
+ __type(key, int);
+ __type(value, struct test_val);
+} map_array_pcpu SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(max_entries, 2);
+ __type(key, int);
+ __type(value, struct test_val);
+} map_array SEC(".maps");
+
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 1);
@@ -526,4 +540,133 @@ l0_%=: exit; \
: __clobber_all);
}
+SEC("socket")
+__description("valid map access into an array using constant without nullness")
+__success
+__retval(4)
+__naked void an_array_with_a_constant_no_nullness(void)
+{
+ asm volatile (" \
+ r1 = 1; \
+ *(u64*)(r10 - 8) = r1; \
+ r2 = r10; \
+ r2 += -8; \
+ r1 = %[map_array] ll; \
+ call %[bpf_map_lookup_elem]; \
+ r1 = %[test_val_foo]; \
+ *(u64*)(r0 + 0) = r1; \
+ r0 = *(u64*)(r0 + 0); \
+ exit; \
+" :
+ : __imm(bpf_map_lookup_elem),
+ __imm_addr(map_array),
+ __imm_const(test_val_foo, offsetof(struct test_val, foo))
+ : __clobber_all);
+}
+
+SEC("socket")
+__description("valid multiple map access into an array using constant without nullness")
+__success
+__retval(8)
+__naked void multiple_array_with_a_constant_no_nullness(void)
+{
+ asm volatile (" \
+ r1 = 1; \
+ *(u64*)(r10 - 8) = r1; \
+ r2 = r10; \
+ r2 += -8; \
+ r1 = %[map_array] ll; \
+ call %[bpf_map_lookup_elem]; \
+ r6 = %[test_val_foo]; \
+ *(u64*)(r0 + 0) = r6; \
+ r7 = *(u64*)(r0 + 0); \
+ r1 = 0; \
+ *(u64*)(r10 - 16) = r1; \
+ r2 = r10; \
+ r2 += -16; \
+ r1 = %[map_array] ll; \
+ call %[bpf_map_lookup_elem]; \
+ *(u64*)(r0 + 0) = r6; \
+ r1 = *(u64*)(r0 + 0); \
+ r7 += r1; \
+ r0 = r7; \
+ exit; \
+" :
+ : __imm(bpf_map_lookup_elem),
+ __imm_addr(map_array),
+ __imm_const(test_val_foo, offsetof(struct test_val, foo))
+ : __clobber_all);
+}
+
+SEC("socket")
+__description("valid map access into an array using 32-bit constant without nullness")
+__success
+__retval(4)
+__naked void an_array_with_a_32bit_constant_no_nullness(void)
+{
+ asm volatile (" \
+ r1 = 1; \
+ *(u32*)(r10 - 4) = r1; \
+ r2 = r10; \
+ r2 += -4; \
+ r1 = %[map_array] ll; \
+ call %[bpf_map_lookup_elem]; \
+ r1 = %[test_val_foo]; \
+ *(u64*)(r0 + 0) = r1; \
+ r0 = *(u64*)(r0 + 0); \
+ exit; \
+" :
+ : __imm(bpf_map_lookup_elem),
+ __imm_addr(map_array),
+ __imm_const(test_val_foo, offsetof(struct test_val, foo))
+ : __clobber_all);
+}
+
+SEC("socket")
+__description("valid map access into a pcpu array using constant without nullness")
+__success
+__retval(4)
+__naked void a_pcpu_array_with_a_constant_no_nullness(void)
+{
+ asm volatile (" \
+ r1 = 1; \
+ *(u64*)(r10 - 8) = r1; \
+ r2 = r10; \
+ r2 += -8; \
+ r1 = %[map_array_pcpu] ll; \
+ call %[bpf_map_lookup_elem]; \
+ r1 = %[test_val_foo]; \
+ *(u64*)(r0 + 0) = r1; \
+ r0 = *(u64*)(r0 + 0); \
+ exit; \
+" :
+ : __imm(bpf_map_lookup_elem),
+ __imm_addr(map_array_pcpu),
+ __imm_const(test_val_foo, offsetof(struct test_val, foo))
+ : __clobber_all);
+}
+
+SEC("socket")
+__description("invalid map access into an array using constant without nullness")
+__failure __msg("R0 invalid mem access 'map_value_or_null'")
+__naked void an_array_with_a_constant_no_nullness_out_of_bounds(void)
+{
+ asm volatile (" \
+ r1 = 3; \
+ *(u64*)(r10 - 8) = r1; \
+ r2 = r10; \
+ r2 += -8; \
+ r1 = %[map_array] ll; \
+ call %[bpf_map_lookup_elem]; \
+ r1 = %[test_val_foo]; \
+ *(u64*)(r0 + 0) = r1; \
+ r0 = *(u64*)(r0 + 0); \
+ exit; \
+" :
+ : __imm(bpf_map_lookup_elem),
+ __imm_addr(map_array),
+ __imm_const(test_val_foo, offsetof(struct test_val, foo))
+ : __clobber_all);
+}
+
char _license[] SEC("license") = "GPL";
Test that nullness elision works for common use cases. For example, we want to check that both full and subreg stack slots recognized. As well as multiple lookups. And obviously some bound checks. Signed-off-by: Daniel Xu <dxu@dxuuu.xyz> --- .../bpf/progs/verifier_array_access.c | 143 ++++++++++++++++++ 1 file changed, 143 insertions(+)