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)
store_release_8(void)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)
store_release_16(void)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)
store_release_32(void)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)
store_release_64(void)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")
store_release_with_uninitialized_src_reg(void)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")
store_release_with_uninitialized_dst_reg(void)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'")
store_release_with_non_pointer_dst_reg(void)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")
__flag(BPF_F_ANY_ALIGNMENT)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")
store_release_to_ctx_pointer(void)139 __naked void store_release_to_ctx_pointer(void)
140 {
141 	asm volatile (
142 	"w0 = 0;"
143 	// store_release((u8 *)(r1 + offsetof(struct __sk_buff, cb[0])), w0);
144 	".8byte %[store_release_insn];"
145 	"exit;"
146 	:
147 	: __imm_insn(store_release_insn,
148 		     BPF_ATOMIC_OP(BPF_B, BPF_STORE_REL, BPF_REG_1, BPF_REG_0,
149 				   offsetof(struct __sk_buff, cb[0])))
150 	: __clobber_all);
151 }
152 
153 SEC("xdp")
154 __description("store-release to pkt pointer")
155 __failure __msg("BPF_ATOMIC stores into R2 pkt is not allowed")
store_release_to_pkt_pointer(void)156 __naked void store_release_to_pkt_pointer(void)
157 {
158 	asm volatile (
159 	"w0 = 0;"
160 	"r2 = *(u32 *)(r1 + %[xdp_md_data]);"
161 	"r3 = *(u32 *)(r1 + %[xdp_md_data_end]);"
162 	"r1 = r2;"
163 	"r1 += 8;"
164 	"if r1 >= r3 goto l0_%=;"
165 	".8byte %[store_release_insn];" // store_release((u8 *)(r2 + 0), w0);
166 "l0_%=:  r0 = 0;"
167 	"exit;"
168 	:
169 	: __imm_const(xdp_md_data, offsetof(struct xdp_md, data)),
170 	  __imm_const(xdp_md_data_end, offsetof(struct xdp_md, data_end)),
171 	  __imm_insn(store_release_insn,
172 		     BPF_ATOMIC_OP(BPF_B, BPF_STORE_REL, BPF_REG_2, BPF_REG_0, 0))
173 	: __clobber_all);
174 }
175 
176 SEC("flow_dissector")
177 __description("store-release to flow_keys pointer")
178 __failure __msg("BPF_ATOMIC stores into R2 flow_keys is not allowed")
store_release_to_flow_keys_pointer(void)179 __naked void store_release_to_flow_keys_pointer(void)
180 {
181 	asm volatile (
182 	"w0 = 0;"
183 	"r2 = *(u64 *)(r1 + %[__sk_buff_flow_keys]);"
184 	".8byte %[store_release_insn];" // store_release((u8 *)(r2 + 0), w0);
185 	"exit;"
186 	:
187 	: __imm_const(__sk_buff_flow_keys,
188 		      offsetof(struct __sk_buff, flow_keys)),
189 	  __imm_insn(store_release_insn,
190 		     BPF_ATOMIC_OP(BPF_B, BPF_STORE_REL, BPF_REG_2, BPF_REG_0, 0))
191 	: __clobber_all);
192 }
193 
194 SEC("sk_reuseport")
195 __description("store-release to sock pointer")
196 __failure __msg("R2 cannot write into sock")
store_release_to_sock_pointer(void)197 __naked void store_release_to_sock_pointer(void)
198 {
199 	asm volatile (
200 	"w0 = 0;"
201 	"r2 = *(u64 *)(r1 + %[sk_reuseport_md_sk]);"
202 	".8byte %[store_release_insn];" // store_release((u8 *)(r2 + 0), w0);
203 	"exit;"
204 	:
205 	: __imm_const(sk_reuseport_md_sk, offsetof(struct sk_reuseport_md, sk)),
206 	  __imm_insn(store_release_insn,
207 		     BPF_ATOMIC_OP(BPF_B, BPF_STORE_REL, BPF_REG_2, BPF_REG_0, 0))
208 	: __clobber_all);
209 }
210 
211 SEC("socket")
212 __description("store-release, leak pointer to stack")
213 __success __success_unpriv __retval(0)
store_release_leak_pointer_to_stack(void)214 __naked void store_release_leak_pointer_to_stack(void)
215 {
216 	asm volatile (
217 	".8byte %[store_release_insn];" // store_release((u64 *)(r10 - 8), r1);
218 	"r0 = 0;"
219 	"exit;"
220 	:
221 	: __imm_insn(store_release_insn,
222 		     BPF_ATOMIC_OP(BPF_DW, BPF_STORE_REL, BPF_REG_10, BPF_REG_1, -8))
223 	: __clobber_all);
224 }
225 
226 struct {
227 	__uint(type, BPF_MAP_TYPE_HASH);
228 	__uint(max_entries, 1);
229 	__type(key, long long);
230 	__type(value, long long);
231 } map_hash_8b SEC(".maps");
232 
233 SEC("socket")
234 __description("store-release, leak pointer to map")
235 __success __retval(0)
236 __failure_unpriv __msg_unpriv("R6 leaks addr into map")
store_release_leak_pointer_to_map(void)237 __naked void store_release_leak_pointer_to_map(void)
238 {
239 	asm volatile (
240 	"r6 = r1;"
241 	"r1 = %[map_hash_8b] ll;"
242 	"r2 = 0;"
243 	"*(u64 *)(r10 - 8) = r2;"
244 	"r2 = r10;"
245 	"r2 += -8;"
246 	"call %[bpf_map_lookup_elem];"
247 	"if r0 == 0 goto l0_%=;"
248 	".8byte %[store_release_insn];" // store_release((u64 *)(r0 + 0), r6);
249 "l0_%=:"
250 	"r0 = 0;"
251 	"exit;"
252 	:
253 	: __imm_addr(map_hash_8b),
254 	  __imm(bpf_map_lookup_elem),
255 	  __imm_insn(store_release_insn,
256 		     BPF_ATOMIC_OP(BPF_DW, BPF_STORE_REL, BPF_REG_0, BPF_REG_6, 0))
257 	: __clobber_all);
258 }
259 
260 SEC("socket")
261 __description("store-release with invalid register R15")
262 __failure __failure_unpriv __msg("R15 is invalid")
store_release_with_invalid_reg(void)263 __naked void store_release_with_invalid_reg(void)
264 {
265 	asm volatile (
266 	".8byte %[store_release_insn];" // store_release((u64 *)(r15 + 0), r1);
267 	"exit;"
268 	:
269 	: __imm_insn(store_release_insn,
270 		     BPF_ATOMIC_OP(BPF_DW, BPF_STORE_REL, 15 /* invalid reg */, BPF_REG_1, 0))
271 	: __clobber_all);
272 }
273 
274 #else
275 
276 SEC("socket")
277 __description("Clang version < 18, ENABLE_ATOMICS_TESTS not defined, and/or JIT doesn't support store-release, use a dummy test")
278 __success
dummy_test(void)279 int dummy_test(void)
280 {
281 	return 0;
282 }
283 
284 #endif
285 
286 char _license[] SEC("license") = "GPL";
287