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