1 // SPDX-License-Identifier: GPL-2.0
2 /* Converted from tools/testing/selftests/bpf/verifier/jeq_infer_not_null.c */
3
4 #include <linux/bpf.h>
5 #include <bpf/bpf_helpers.h>
6 #include "bpf_misc.h"
7
8 struct {
9 __uint(type, BPF_MAP_TYPE_XSKMAP);
10 __uint(max_entries, 1);
11 __type(key, int);
12 __type(value, int);
13 } map_xskmap SEC(".maps");
14
15 /* This is equivalent to the following program:
16 *
17 * r6 = skb->sk;
18 * r7 = sk_fullsock(r6);
19 * r0 = sk_fullsock(r6);
20 * if (r0 == 0) return 0; (a)
21 * if (r0 != r7) return 0; (b)
22 * *r7->type; (c)
23 * return 0;
24 *
25 * It is safe to dereference r7 at point (c), because of (a) and (b).
26 * The test verifies that relation r0 == r7 is propagated from (b) to (c).
27 */
28 SEC("cgroup/skb")
29 __description("jne/jeq infer not null, PTR_TO_SOCKET_OR_NULL -> PTR_TO_SOCKET for JNE false branch")
30 __success __failure_unpriv __msg_unpriv("R7 pointer comparison")
31 __retval(0)
socket_for_jne_false_branch(void)32 __naked void socket_for_jne_false_branch(void)
33 {
34 asm volatile (" \
35 /* r6 = skb->sk; */ \
36 r6 = *(u64*)(r1 + %[__sk_buff_sk]); \
37 /* if (r6 == 0) return 0; */ \
38 if r6 == 0 goto l0_%=; \
39 /* r7 = sk_fullsock(skb); */ \
40 r1 = r6; \
41 call %[bpf_sk_fullsock]; \
42 r7 = r0; \
43 /* r0 = sk_fullsock(skb); */ \
44 r1 = r6; \
45 call %[bpf_sk_fullsock]; \
46 /* if (r0 == null) return 0; */ \
47 if r0 == 0 goto l0_%=; \
48 /* if (r0 == r7) r0 = *(r7->type); */ \
49 if r0 != r7 goto l0_%=; /* Use ! JNE ! */\
50 r0 = *(u32*)(r7 + %[bpf_sock_type]); \
51 l0_%=: /* return 0 */ \
52 r0 = 0; \
53 exit; \
54 " :
55 : __imm(bpf_sk_fullsock),
56 __imm_const(__sk_buff_sk, offsetof(struct __sk_buff, sk)),
57 __imm_const(bpf_sock_type, offsetof(struct bpf_sock, type))
58 : __clobber_all);
59 }
60
61 /* Same as above, but verify that another branch of JNE still
62 * prohibits access to PTR_MAYBE_NULL.
63 */
64 SEC("cgroup/skb")
65 __description("jne/jeq infer not null, PTR_TO_SOCKET_OR_NULL unchanged for JNE true branch")
66 __failure __msg("R7 invalid mem access 'sock_or_null'")
67 __failure_unpriv __msg_unpriv("R7 pointer comparison")
unchanged_for_jne_true_branch(void)68 __naked void unchanged_for_jne_true_branch(void)
69 {
70 asm volatile (" \
71 /* r6 = skb->sk */ \
72 r6 = *(u64*)(r1 + %[__sk_buff_sk]); \
73 /* if (r6 == 0) return 0; */ \
74 if r6 == 0 goto l0_%=; \
75 /* r7 = sk_fullsock(skb); */ \
76 r1 = r6; \
77 call %[bpf_sk_fullsock]; \
78 r7 = r0; \
79 /* r0 = sk_fullsock(skb); */ \
80 r1 = r6; \
81 call %[bpf_sk_fullsock]; \
82 /* if (r0 == null) return 0; */ \
83 if r0 != 0 goto l0_%=; \
84 /* if (r0 == r7) return 0; */ \
85 if r0 != r7 goto l1_%=; /* Use ! JNE ! */\
86 goto l0_%=; \
87 l1_%=: /* r0 = *(r7->type); */ \
88 r0 = *(u32*)(r7 + %[bpf_sock_type]); \
89 l0_%=: /* return 0 */ \
90 r0 = 0; \
91 exit; \
92 " :
93 : __imm(bpf_sk_fullsock),
94 __imm_const(__sk_buff_sk, offsetof(struct __sk_buff, sk)),
95 __imm_const(bpf_sock_type, offsetof(struct bpf_sock, type))
96 : __clobber_all);
97 }
98
99 /* Same as a first test, but not null should be inferred for JEQ branch */
100 SEC("cgroup/skb")
101 __description("jne/jeq infer not null, PTR_TO_SOCKET_OR_NULL -> PTR_TO_SOCKET for JEQ true branch")
102 __success __failure_unpriv __msg_unpriv("R7 pointer comparison")
103 __retval(0)
socket_for_jeq_true_branch(void)104 __naked void socket_for_jeq_true_branch(void)
105 {
106 asm volatile (" \
107 /* r6 = skb->sk; */ \
108 r6 = *(u64*)(r1 + %[__sk_buff_sk]); \
109 /* if (r6 == null) return 0; */ \
110 if r6 == 0 goto l0_%=; \
111 /* r7 = sk_fullsock(skb); */ \
112 r1 = r6; \
113 call %[bpf_sk_fullsock]; \
114 r7 = r0; \
115 /* r0 = sk_fullsock(skb); */ \
116 r1 = r6; \
117 call %[bpf_sk_fullsock]; \
118 /* if (r0 == null) return 0; */ \
119 if r0 == 0 goto l0_%=; \
120 /* if (r0 != r7) return 0; */ \
121 if r0 == r7 goto l1_%=; /* Use ! JEQ ! */\
122 goto l0_%=; \
123 l1_%=: /* r0 = *(r7->type); */ \
124 r0 = *(u32*)(r7 + %[bpf_sock_type]); \
125 l0_%=: /* return 0; */ \
126 r0 = 0; \
127 exit; \
128 " :
129 : __imm(bpf_sk_fullsock),
130 __imm_const(__sk_buff_sk, offsetof(struct __sk_buff, sk)),
131 __imm_const(bpf_sock_type, offsetof(struct bpf_sock, type))
132 : __clobber_all);
133 }
134
135 /* Same as above, but verify that another branch of JNE still
136 * prohibits access to PTR_MAYBE_NULL.
137 */
138 SEC("cgroup/skb")
139 __description("jne/jeq infer not null, PTR_TO_SOCKET_OR_NULL unchanged for JEQ false branch")
140 __failure __msg("R7 invalid mem access 'sock_or_null'")
141 __failure_unpriv __msg_unpriv("R7 pointer comparison")
unchanged_for_jeq_false_branch(void)142 __naked void unchanged_for_jeq_false_branch(void)
143 {
144 asm volatile (" \
145 /* r6 = skb->sk; */ \
146 r6 = *(u64*)(r1 + %[__sk_buff_sk]); \
147 /* if (r6 == null) return 0; */ \
148 if r6 == 0 goto l0_%=; \
149 /* r7 = sk_fullsock(skb); */ \
150 r1 = r6; \
151 call %[bpf_sk_fullsock]; \
152 r7 = r0; \
153 /* r0 = sk_fullsock(skb); */ \
154 r1 = r6; \
155 call %[bpf_sk_fullsock]; \
156 /* if (r0 == null) return 0; */ \
157 if r0 == 0 goto l0_%=; \
158 /* if (r0 != r7) r0 = *(r7->type); */ \
159 if r0 == r7 goto l0_%=; /* Use ! JEQ ! */\
160 r0 = *(u32*)(r7 + %[bpf_sock_type]); \
161 l0_%=: /* return 0; */ \
162 r0 = 0; \
163 exit; \
164 " :
165 : __imm(bpf_sk_fullsock),
166 __imm_const(__sk_buff_sk, offsetof(struct __sk_buff, sk)),
167 __imm_const(bpf_sock_type, offsetof(struct bpf_sock, type))
168 : __clobber_all);
169 }
170
171 /* Maps are treated in a different branch of `mark_ptr_not_null_reg`,
172 * so separate test for maps case.
173 */
174 SEC("xdp")
175 __description("jne/jeq infer not null, PTR_TO_MAP_VALUE_OR_NULL -> PTR_TO_MAP_VALUE")
176 __success __retval(0)
null_ptr_to_map_value(void)177 __naked void null_ptr_to_map_value(void)
178 {
179 asm volatile (" \
180 /* r9 = &some stack to use as key */ \
181 r1 = 0; \
182 *(u32*)(r10 - 8) = r1; \
183 r9 = r10; \
184 r9 += -8; \
185 /* r8 = process local map */ \
186 r8 = %[map_xskmap] ll; \
187 /* r6 = map_lookup_elem(r8, r9); */ \
188 r1 = r8; \
189 r2 = r9; \
190 call %[bpf_map_lookup_elem]; \
191 r6 = r0; \
192 /* r7 = map_lookup_elem(r8, r9); */ \
193 r1 = r8; \
194 r2 = r9; \
195 call %[bpf_map_lookup_elem]; \
196 r7 = r0; \
197 /* if (r6 == 0) return 0; */ \
198 if r6 == 0 goto l0_%=; \
199 /* if (r6 != r7) return 0; */ \
200 if r6 != r7 goto l0_%=; \
201 /* read *r7; */ \
202 r0 = *(u32*)(r7 + %[bpf_xdp_sock_queue_id]); \
203 l0_%=: /* return 0; */ \
204 r0 = 0; \
205 exit; \
206 " :
207 : __imm(bpf_map_lookup_elem),
208 __imm_addr(map_xskmap),
209 __imm_const(bpf_xdp_sock_queue_id, offsetof(struct bpf_xdp_sock, queue_id))
210 : __clobber_all);
211 }
212
213 char _license[] SEC("license") = "GPL";
214