Skip to content

Commit ff3afe5

Browse files
peilin-yeAlexei Starovoitov
authored and
Alexei Starovoitov
committed
selftests/bpf: Add selftests for load-acquire and store-release instructions
Add several ./test_progs tests: - arena_atomics/load_acquire - arena_atomics/store_release - verifier_load_acquire/* - verifier_store_release/* - verifier_precision/bpf_load_acquire - verifier_precision/bpf_store_release The last two tests are added to check if backtrack_insn() handles the new instructions correctly. Additionally, the last test also makes sure that the verifier "remembers" the value (in src_reg) we store-release into e.g. a stack slot. For example, if we take a look at the test program: #0: r1 = 8; /* store_release((u64 *)(r10 - 8), r1); */ #1: .8byte %[store_release]; #2: r1 = *(u64 *)(r10 - 8); #3: r2 = r10; #4: r2 += r1; #5: r0 = 0; #6: exit; At #1, if the verifier doesn't remember that we wrote 8 to the stack, then later at #4 we would be adding an unbounded scalar value to the stack pointer, which would cause the program to be rejected: VERIFIER LOG: ============= ... math between fp pointer and register with unbounded min value is not allowed For easier CI integration, instead of using built-ins like __atomic_{load,store}_n() which depend on the new __BPF_FEATURE_LOAD_ACQ_STORE_REL pre-defined macro, manually craft load-acquire/store-release instructions using __imm_insn(), as suggested by Eduard. All new tests depend on: (1) Clang major version >= 18, and (2) ENABLE_ATOMICS_TESTS is defined (currently implies -mcpu=v3 or v4), and (3) JIT supports load-acquire/store-release (currently arm64 and x86-64) In .../progs/arena_atomics.c: /* 8-byte-aligned */ __u8 __arena_global load_acquire8_value = 0x12; /* 1-byte hole */ __u16 __arena_global load_acquire16_value = 0x1234; That 1-byte hole in the .addr_space.1 ELF section caused clang-17 to crash: fatal error: error in backend: unable to write nop sequence of 1 bytes To work around such llvm-17 CI job failures, conditionally define __arena_global variables as 64-bit if __clang_major__ < 18, to make sure .addr_space.1 has no holes. Ideally we should avoid compiling this file using clang-17 at all (arena tests depend on __BPF_FEATURE_ADDR_SPACE_CAST, and are skipped for llvm-17 anyway), but that is a separate topic. Acked-by: Eduard Zingerman <eddyz87@gmail.com> Signed-off-by: Peilin Ye <yepeilin@google.com> Link: https://lore.kernel.org/r/1b46c6feaf0f1b6984d9ec80e500cc7383e9da1a.1741049567.git.yepeilin@google.com Signed-off-by: Alexei Starovoitov <ast@kernel.org>
1 parent 5341c9a commit ff3afe5

File tree

6 files changed

+698
-3
lines changed

6 files changed

+698
-3
lines changed

tools/testing/selftests/bpf/prog_tests/arena_atomics.c

+65-1
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,66 @@ static void test_uaf(struct arena_atomics *skel)
162162
ASSERT_EQ(skel->arena->uaf_recovery_fails, 0, "uaf_recovery_fails");
163163
}
164164

165+
static void test_load_acquire(struct arena_atomics *skel)
166+
{
167+
LIBBPF_OPTS(bpf_test_run_opts, topts);
168+
int err, prog_fd;
169+
170+
if (skel->data->skip_lacq_srel_tests) {
171+
printf("%s:SKIP: ENABLE_ATOMICS_TESTS not defined, Clang doesn't support addr_space_cast, and/or JIT doesn't support load-acquire\n",
172+
__func__);
173+
test__skip();
174+
return;
175+
}
176+
177+
/* No need to attach it, just run it directly */
178+
prog_fd = bpf_program__fd(skel->progs.load_acquire);
179+
err = bpf_prog_test_run_opts(prog_fd, &topts);
180+
if (!ASSERT_OK(err, "test_run_opts err"))
181+
return;
182+
if (!ASSERT_OK(topts.retval, "test_run_opts retval"))
183+
return;
184+
185+
ASSERT_EQ(skel->arena->load_acquire8_result, 0x12,
186+
"load_acquire8_result");
187+
ASSERT_EQ(skel->arena->load_acquire16_result, 0x1234,
188+
"load_acquire16_result");
189+
ASSERT_EQ(skel->arena->load_acquire32_result, 0x12345678,
190+
"load_acquire32_result");
191+
ASSERT_EQ(skel->arena->load_acquire64_result, 0x1234567890abcdef,
192+
"load_acquire64_result");
193+
}
194+
195+
static void test_store_release(struct arena_atomics *skel)
196+
{
197+
LIBBPF_OPTS(bpf_test_run_opts, topts);
198+
int err, prog_fd;
199+
200+
if (skel->data->skip_lacq_srel_tests) {
201+
printf("%s:SKIP: ENABLE_ATOMICS_TESTS not defined, Clang doesn't support addr_space_cast, and/or JIT doesn't support store-release\n",
202+
__func__);
203+
test__skip();
204+
return;
205+
}
206+
207+
/* No need to attach it, just run it directly */
208+
prog_fd = bpf_program__fd(skel->progs.store_release);
209+
err = bpf_prog_test_run_opts(prog_fd, &topts);
210+
if (!ASSERT_OK(err, "test_run_opts err"))
211+
return;
212+
if (!ASSERT_OK(topts.retval, "test_run_opts retval"))
213+
return;
214+
215+
ASSERT_EQ(skel->arena->store_release8_result, 0x12,
216+
"store_release8_result");
217+
ASSERT_EQ(skel->arena->store_release16_result, 0x1234,
218+
"store_release16_result");
219+
ASSERT_EQ(skel->arena->store_release32_result, 0x12345678,
220+
"store_release32_result");
221+
ASSERT_EQ(skel->arena->store_release64_result, 0x1234567890abcdef,
222+
"store_release64_result");
223+
}
224+
165225
void test_arena_atomics(void)
166226
{
167227
struct arena_atomics *skel;
@@ -171,7 +231,7 @@ void test_arena_atomics(void)
171231
if (!ASSERT_OK_PTR(skel, "arena atomics skeleton open"))
172232
return;
173233

174-
if (skel->data->skip_tests) {
234+
if (skel->data->skip_all_tests) {
175235
printf("%s:SKIP:no ENABLE_ATOMICS_TESTS or no addr_space_cast support in clang",
176236
__func__);
177237
test__skip();
@@ -198,6 +258,10 @@ void test_arena_atomics(void)
198258
test_xchg(skel);
199259
if (test__start_subtest("uaf"))
200260
test_uaf(skel);
261+
if (test__start_subtest("load_acquire"))
262+
test_load_acquire(skel);
263+
if (test__start_subtest("store_release"))
264+
test_store_release(skel);
201265

202266
cleanup:
203267
arena_atomics__destroy(skel);

tools/testing/selftests/bpf/prog_tests/verifier.c

+4
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
#include "verifier_ldsx.skel.h"
4646
#include "verifier_leak_ptr.skel.h"
4747
#include "verifier_linked_scalars.skel.h"
48+
#include "verifier_load_acquire.skel.h"
4849
#include "verifier_loops1.skel.h"
4950
#include "verifier_lwt.skel.h"
5051
#include "verifier_map_in_map.skel.h"
@@ -80,6 +81,7 @@
8081
#include "verifier_spill_fill.skel.h"
8182
#include "verifier_spin_lock.skel.h"
8283
#include "verifier_stack_ptr.skel.h"
84+
#include "verifier_store_release.skel.h"
8385
#include "verifier_subprog_precision.skel.h"
8486
#include "verifier_subreg.skel.h"
8587
#include "verifier_tailcall_jit.skel.h"
@@ -173,6 +175,7 @@ void test_verifier_int_ptr(void) { RUN(verifier_int_ptr); }
173175
void test_verifier_iterating_callbacks(void) { RUN(verifier_iterating_callbacks); }
174176
void test_verifier_jeq_infer_not_null(void) { RUN(verifier_jeq_infer_not_null); }
175177
void test_verifier_jit_convergence(void) { RUN(verifier_jit_convergence); }
178+
void test_verifier_load_acquire(void) { RUN(verifier_load_acquire); }
176179
void test_verifier_ld_ind(void) { RUN(verifier_ld_ind); }
177180
void test_verifier_ldsx(void) { RUN(verifier_ldsx); }
178181
void test_verifier_leak_ptr(void) { RUN(verifier_leak_ptr); }
@@ -211,6 +214,7 @@ void test_verifier_sockmap_mutate(void) { RUN(verifier_sockmap_mutate); }
211214
void test_verifier_spill_fill(void) { RUN(verifier_spill_fill); }
212215
void test_verifier_spin_lock(void) { RUN(verifier_spin_lock); }
213216
void test_verifier_stack_ptr(void) { RUN(verifier_stack_ptr); }
217+
void test_verifier_store_release(void) { RUN(verifier_store_release); }
214218
void test_verifier_subprog_precision(void) { RUN(verifier_subprog_precision); }
215219
void test_verifier_subreg(void) { RUN(verifier_subreg); }
216220
void test_verifier_tailcall_jit(void) { RUN(verifier_tailcall_jit); }

tools/testing/selftests/bpf/progs/arena_atomics.c

+119-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
#include <stdbool.h>
77
#include <stdatomic.h>
88
#include "bpf_arena_common.h"
9+
#include "../../../include/linux/filter.h"
10+
#include "bpf_misc.h"
911

1012
struct {
1113
__uint(type, BPF_MAP_TYPE_ARENA);
@@ -19,9 +21,17 @@ struct {
1921
} arena SEC(".maps");
2022

2123
#if defined(ENABLE_ATOMICS_TESTS) && defined(__BPF_FEATURE_ADDR_SPACE_CAST)
22-
bool skip_tests __attribute((__section__(".data"))) = false;
24+
bool skip_all_tests __attribute((__section__(".data"))) = false;
2325
#else
24-
bool skip_tests = true;
26+
bool skip_all_tests = true;
27+
#endif
28+
29+
#if defined(ENABLE_ATOMICS_TESTS) && \
30+
defined(__BPF_FEATURE_ADDR_SPACE_CAST) && \
31+
(defined(__TARGET_ARCH_arm64) || defined(__TARGET_ARCH_x86))
32+
bool skip_lacq_srel_tests __attribute((__section__(".data"))) = false;
33+
#else
34+
bool skip_lacq_srel_tests = true;
2535
#endif
2636

2737
__u32 pid = 0;
@@ -274,4 +284,111 @@ int uaf(const void *ctx)
274284
return 0;
275285
}
276286

287+
#if __clang_major__ >= 18
288+
__u8 __arena_global load_acquire8_value = 0x12;
289+
__u16 __arena_global load_acquire16_value = 0x1234;
290+
__u32 __arena_global load_acquire32_value = 0x12345678;
291+
__u64 __arena_global load_acquire64_value = 0x1234567890abcdef;
292+
293+
__u8 __arena_global load_acquire8_result = 0;
294+
__u16 __arena_global load_acquire16_result = 0;
295+
__u32 __arena_global load_acquire32_result = 0;
296+
__u64 __arena_global load_acquire64_result = 0;
297+
#else
298+
/* clang-17 crashes if the .addr_space.1 ELF section has holes. Work around
299+
* this issue by defining the below variables as 64-bit.
300+
*/
301+
__u64 __arena_global load_acquire8_value;
302+
__u64 __arena_global load_acquire16_value;
303+
__u64 __arena_global load_acquire32_value;
304+
__u64 __arena_global load_acquire64_value;
305+
306+
__u64 __arena_global load_acquire8_result;
307+
__u64 __arena_global load_acquire16_result;
308+
__u64 __arena_global load_acquire32_result;
309+
__u64 __arena_global load_acquire64_result;
310+
#endif
311+
312+
SEC("raw_tp/sys_enter")
313+
int load_acquire(const void *ctx)
314+
{
315+
#if defined(ENABLE_ATOMICS_TESTS) && \
316+
defined(__BPF_FEATURE_ADDR_SPACE_CAST) && \
317+
(defined(__TARGET_ARCH_arm64) || defined(__TARGET_ARCH_x86))
318+
319+
#define LOAD_ACQUIRE_ARENA(SIZEOP, SIZE, SRC, DST) \
320+
{ asm volatile ( \
321+
"r1 = %[" #SRC "] ll;" \
322+
"r1 = addr_space_cast(r1, 0x0, 0x1);" \
323+
".8byte %[load_acquire_insn];" \
324+
"r3 = %[" #DST "] ll;" \
325+
"r3 = addr_space_cast(r3, 0x0, 0x1);" \
326+
"*(" #SIZE " *)(r3 + 0) = r2;" \
327+
: \
328+
: __imm_addr(SRC), \
329+
__imm_insn(load_acquire_insn, \
330+
BPF_ATOMIC_OP(BPF_##SIZEOP, BPF_LOAD_ACQ, \
331+
BPF_REG_2, BPF_REG_1, 0)), \
332+
__imm_addr(DST) \
333+
: __clobber_all); } \
334+
335+
LOAD_ACQUIRE_ARENA(B, u8, load_acquire8_value, load_acquire8_result)
336+
LOAD_ACQUIRE_ARENA(H, u16, load_acquire16_value,
337+
load_acquire16_result)
338+
LOAD_ACQUIRE_ARENA(W, u32, load_acquire32_value,
339+
load_acquire32_result)
340+
LOAD_ACQUIRE_ARENA(DW, u64, load_acquire64_value,
341+
load_acquire64_result)
342+
#undef LOAD_ACQUIRE_ARENA
343+
344+
#endif
345+
return 0;
346+
}
347+
348+
#if __clang_major__ >= 18
349+
__u8 __arena_global store_release8_result = 0;
350+
__u16 __arena_global store_release16_result = 0;
351+
__u32 __arena_global store_release32_result = 0;
352+
__u64 __arena_global store_release64_result = 0;
353+
#else
354+
/* clang-17 crashes if the .addr_space.1 ELF section has holes. Work around
355+
* this issue by defining the below variables as 64-bit.
356+
*/
357+
__u64 __arena_global store_release8_result;
358+
__u64 __arena_global store_release16_result;
359+
__u64 __arena_global store_release32_result;
360+
__u64 __arena_global store_release64_result;
361+
#endif
362+
363+
SEC("raw_tp/sys_enter")
364+
int store_release(const void *ctx)
365+
{
366+
#if defined(ENABLE_ATOMICS_TESTS) && \
367+
defined(__BPF_FEATURE_ADDR_SPACE_CAST) && \
368+
(defined(__TARGET_ARCH_arm64) || defined(__TARGET_ARCH_x86))
369+
370+
#define STORE_RELEASE_ARENA(SIZEOP, DST, VAL) \
371+
{ asm volatile ( \
372+
"r1 = " VAL ";" \
373+
"r2 = %[" #DST "] ll;" \
374+
"r2 = addr_space_cast(r2, 0x0, 0x1);" \
375+
".8byte %[store_release_insn];" \
376+
: \
377+
: __imm_addr(DST), \
378+
__imm_insn(store_release_insn, \
379+
BPF_ATOMIC_OP(BPF_##SIZEOP, BPF_STORE_REL, \
380+
BPF_REG_2, BPF_REG_1, 0)) \
381+
: __clobber_all); } \
382+
383+
STORE_RELEASE_ARENA(B, store_release8_result, "0x12")
384+
STORE_RELEASE_ARENA(H, store_release16_result, "0x1234")
385+
STORE_RELEASE_ARENA(W, store_release32_result, "0x12345678")
386+
STORE_RELEASE_ARENA(DW, store_release64_result,
387+
"0x1234567890abcdef ll")
388+
#undef STORE_RELEASE_ARENA
389+
390+
#endif
391+
return 0;
392+
}
393+
277394
char _license[] SEC("license") = "GPL";

0 commit comments

Comments
 (0)