xref: /linux/tools/testing/selftests/bpf/progs/verifier_store_release.c (revision 38c6104e0bc7c8af20ab4897cb0504e3339e4fe4)
1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2025 Google LLC. */
3 
4 #include <linux/bpf.h>
5 #include <bpf/bpf_helpers.h>
6 #include "../../../include/linux/filter.h"
7 #include "bpf_misc.h"
8 
9 #if __clang_major__ >= 18 && defined(ENABLE_ATOMICS_TESTS) && \
10 	(defined(__TARGET_ARCH_arm64) || defined(__TARGET_ARCH_x86))
11 
12 SEC("socket")
13 __description("store-release, 8-bit")
14 __success __success_unpriv __retval(0x12)
15 __naked void store_release_8(void)
16 {
17 	asm volatile (
18 	"w1 = 0x12;"
19 	".8byte %[store_release_insn];" // store_release((u8 *)(r10 - 1), w1);
20 	"w0 = *(u8 *)(r10 - 1);"
21 	"exit;"
22 	:
23 	: __imm_insn(store_release_insn,
24 		     BPF_ATOMIC_OP(BPF_B, BPF_STORE_REL, BPF_REG_10, BPF_REG_1, -1))
25 	: __clobber_all);
26 }
27 
28 SEC("socket")
29 __description("store-release, 16-bit")
30 __success __success_unpriv __retval(0x1234)
31 __naked void store_release_16(void)
32 {
33 	asm volatile (
34 	"w1 = 0x1234;"
35 	".8byte %[store_release_insn];" // store_release((u16 *)(r10 - 2), w1);
36 	"w0 = *(u16 *)(r10 - 2);"
37 	"exit;"
38 	:
39 	: __imm_insn(store_release_insn,
40 		     BPF_ATOMIC_OP(BPF_H, BPF_STORE_REL, BPF_REG_10, BPF_REG_1, -2))
41 	: __clobber_all);
42 }
43 
44 SEC("socket")
45 __description("store-release, 32-bit")
46 __success __success_unpriv __retval(0x12345678)
47 __naked void store_release_32(void)
48 {
49 	asm volatile (
50 	"w1 = 0x12345678;"
51 	".8byte %[store_release_insn];" // store_release((u32 *)(r10 - 4), w1);
52 	"w0 = *(u32 *)(r10 - 4);"
53 	"exit;"
54 	:
55 	: __imm_insn(store_release_insn,
56 		     BPF_ATOMIC_OP(BPF_W, BPF_STORE_REL, BPF_REG_10, BPF_REG_1, -4))
57 	: __clobber_all);
58 }
59 
60 SEC("socket")
61 __description("store-release, 64-bit")
62 __success __success_unpriv __retval(0x1234567890abcdef)
63 __naked void store_release_64(void)
64 {
65 	asm volatile (
66 	"r1 = 0x1234567890abcdef ll;"
67 	".8byte %[store_release_insn];" // store_release((u64 *)(r10 - 8), r1);
68 	"r0 = *(u64 *)(r10 - 8);"
69 	"exit;"
70 	:
71 	: __imm_insn(store_release_insn,
72 		     BPF_ATOMIC_OP(BPF_DW, BPF_STORE_REL, BPF_REG_10, BPF_REG_1, -8))
73 	: __clobber_all);
74 }
75 
76 SEC("socket")
77 __description("store-release with uninitialized src_reg")
78 __failure __failure_unpriv __msg("R2 !read_ok")
79 __naked void store_release_with_uninitialized_src_reg(void)
80 {
81 	asm volatile (
82 	".8byte %[store_release_insn];" // store_release((u64 *)(r10 - 8), r2);
83 	"exit;"
84 	:
85 	: __imm_insn(store_release_insn,
86 		     BPF_ATOMIC_OP(BPF_DW, BPF_STORE_REL, BPF_REG_10, BPF_REG_2, -8))
87 	: __clobber_all);
88 }
89 
90 SEC("socket")
91 __description("store-release with uninitialized dst_reg")
92 __failure __failure_unpriv __msg("R2 !read_ok")
93 __naked void store_release_with_uninitialized_dst_reg(void)
94 {
95 	asm volatile (
96 	"r1 = 0;"
97 	".8byte %[store_release_insn];" // store_release((u64 *)(r2 - 8), r1);
98 	"exit;"
99 	:
100 	: __imm_insn(store_release_insn,
101 		     BPF_ATOMIC_OP(BPF_DW, BPF_STORE_REL, BPF_REG_2, BPF_REG_1, -8))
102 	: __clobber_all);
103 }
104 
105 SEC("socket")
106 __description("store-release with non-pointer dst_reg")
107 __failure __failure_unpriv __msg("R1 invalid mem access 'scalar'")
108 __naked void store_release_with_non_pointer_dst_reg(void)
109 {
110 	asm volatile (
111 	"r1 = 0;"
112 	".8byte %[store_release_insn];" // store_release((u64 *)(r1 + 0), r1);
113 	"exit;"
114 	:
115 	: __imm_insn(store_release_insn,
116 		     BPF_ATOMIC_OP(BPF_DW, BPF_STORE_REL, BPF_REG_1, BPF_REG_1, 0))
117 	: __clobber_all);
118 }
119 
120 SEC("socket")
121 __description("misaligned store-release")
122 __failure __failure_unpriv __msg("misaligned stack access off")
123 __flag(BPF_F_ANY_ALIGNMENT)
124 __naked void store_release_misaligned(void)
125 {
126 	asm volatile (
127 	"w0 = 0;"
128 	".8byte %[store_release_insn];" // store_release((u32 *)(r10 - 5), w0);
129 	"exit;"
130 	:
131 	: __imm_insn(store_release_insn,
132 		     BPF_ATOMIC_OP(BPF_W, BPF_STORE_REL, BPF_REG_10, BPF_REG_0, -5))
133 	: __clobber_all);
134 }
135 
136 SEC("socket")
137 __description("store-release to ctx pointer")
138 __failure __failure_unpriv __msg("BPF_ATOMIC stores into R1 ctx is not allowed")
139 __naked void store_release_to_ctx_pointer(void)
140 {
141 	asm volatile (
142 	"w0 = 0;"
143 	".8byte %[store_release_insn];" // store_release((u8 *)(r1 + 0), w0);
144 	"exit;"
145 	:
146 	: __imm_insn(store_release_insn,
147 		     BPF_ATOMIC_OP(BPF_B, BPF_STORE_REL, BPF_REG_1, BPF_REG_0, 0))
148 	: __clobber_all);
149 }
150 
151 SEC("xdp")
152 __description("store-release to pkt pointer")
153 __failure __msg("BPF_ATOMIC stores into R2 pkt is not allowed")
154 __naked void store_release_to_pkt_pointer(void)
155 {
156 	asm volatile (
157 	"w0 = 0;"
158 	"r2 = *(u32 *)(r1 + %[xdp_md_data]);"
159 	".8byte %[store_release_insn];" // store_release((u8 *)(r2 + 0), w0);
160 	"exit;"
161 	:
162 	: __imm_const(xdp_md_data, offsetof(struct xdp_md, data)),
163 	  __imm_insn(store_release_insn,
164 		     BPF_ATOMIC_OP(BPF_B, BPF_STORE_REL, BPF_REG_2, BPF_REG_0, 0))
165 	: __clobber_all);
166 }
167 
168 SEC("flow_dissector")
169 __description("store-release to flow_keys pointer")
170 __failure __msg("BPF_ATOMIC stores into R2 flow_keys is not allowed")
171 __naked void store_release_to_flow_keys_pointer(void)
172 {
173 	asm volatile (
174 	"w0 = 0;"
175 	"r2 = *(u64 *)(r1 + %[__sk_buff_flow_keys]);"
176 	".8byte %[store_release_insn];" // store_release((u8 *)(r2 + 0), w0);
177 	"exit;"
178 	:
179 	: __imm_const(__sk_buff_flow_keys,
180 		      offsetof(struct __sk_buff, flow_keys)),
181 	  __imm_insn(store_release_insn,
182 		     BPF_ATOMIC_OP(BPF_B, BPF_STORE_REL, BPF_REG_2, BPF_REG_0, 0))
183 	: __clobber_all);
184 }
185 
186 SEC("sk_reuseport")
187 __description("store-release to sock pointer")
188 __failure __msg("BPF_ATOMIC stores into R2 sock is not allowed")
189 __naked void store_release_to_sock_pointer(void)
190 {
191 	asm volatile (
192 	"w0 = 0;"
193 	"r2 = *(u64 *)(r1 + %[sk_reuseport_md_sk]);"
194 	".8byte %[store_release_insn];" // store_release((u8 *)(r2 + 0), w0);
195 	"exit;"
196 	:
197 	: __imm_const(sk_reuseport_md_sk, offsetof(struct sk_reuseport_md, sk)),
198 	  __imm_insn(store_release_insn,
199 		     BPF_ATOMIC_OP(BPF_B, BPF_STORE_REL, BPF_REG_2, BPF_REG_0, 0))
200 	: __clobber_all);
201 }
202 
203 SEC("socket")
204 __description("store-release, leak pointer to stack")
205 __success __success_unpriv __retval(0)
206 __naked void store_release_leak_pointer_to_stack(void)
207 {
208 	asm volatile (
209 	".8byte %[store_release_insn];" // store_release((u64 *)(r10 - 8), r1);
210 	"r0 = 0;"
211 	"exit;"
212 	:
213 	: __imm_insn(store_release_insn,
214 		     BPF_ATOMIC_OP(BPF_DW, BPF_STORE_REL, BPF_REG_10, BPF_REG_1, -8))
215 	: __clobber_all);
216 }
217 
218 struct {
219 	__uint(type, BPF_MAP_TYPE_HASH);
220 	__uint(max_entries, 1);
221 	__type(key, long long);
222 	__type(value, long long);
223 } map_hash_8b SEC(".maps");
224 
225 SEC("socket")
226 __description("store-release, leak pointer to map")
227 __success __retval(0)
228 __failure_unpriv __msg_unpriv("R6 leaks addr into map")
229 __naked void store_release_leak_pointer_to_map(void)
230 {
231 	asm volatile (
232 	"r6 = r1;"
233 	"r1 = %[map_hash_8b] ll;"
234 	"r2 = 0;"
235 	"*(u64 *)(r10 - 8) = r2;"
236 	"r2 = r10;"
237 	"r2 += -8;"
238 	"call %[bpf_map_lookup_elem];"
239 	"if r0 == 0 goto l0_%=;"
240 	".8byte %[store_release_insn];" // store_release((u64 *)(r0 + 0), r6);
241 "l0_%=:"
242 	"r0 = 0;"
243 	"exit;"
244 	:
245 	: __imm_addr(map_hash_8b),
246 	  __imm(bpf_map_lookup_elem),
247 	  __imm_insn(store_release_insn,
248 		     BPF_ATOMIC_OP(BPF_DW, BPF_STORE_REL, BPF_REG_0, BPF_REG_6, 0))
249 	: __clobber_all);
250 }
251 
252 #else
253 
254 SEC("socket")
255 __description("Clang version < 18, ENABLE_ATOMICS_TESTS not defined, and/or JIT doesn't support store-release, use a dummy test")
256 __success
257 int dummy_test(void)
258 {
259 	return 0;
260 }
261 
262 #endif
263 
264 char _license[] SEC("license") = "GPL";
265