Commit 490c99d4 authored by Ilya Leoshkevich's avatar Ilya Leoshkevich Committed by Daniel Borkmann
Browse files

selftests/bpf: Add UAF tests for arena atomics



Check that __sync_*() functions don't cause kernel panics when handling
freed arena pages.

x86_64 does not support some arena atomics yet, and aarch64 may or may
not support them, based on the availability of LSE atomics at run time.
Do not enable this test for these architectures for simplicity.

Signed-off-by: default avatarIlya Leoshkevich <iii@linux.ibm.com>
Signed-off-by: default avatarDaniel Borkmann <daniel@iogearbox.net>
Link: https://lore.kernel.org/bpf/20240701234304.14336-12-iii@linux.ibm.com
parent b6349fd3
Loading
Loading
Loading
Loading
+18 −0
Original line number Diff line number Diff line
@@ -146,6 +146,22 @@ static void test_xchg(struct arena_atomics *skel)
	ASSERT_EQ(skel->arena->xchg32_result, 1, "xchg32_result");
}

static void test_uaf(struct arena_atomics *skel)
{
	LIBBPF_OPTS(bpf_test_run_opts, topts);
	int err, prog_fd;

	/* No need to attach it, just run it directly */
	prog_fd = bpf_program__fd(skel->progs.uaf);
	err = bpf_prog_test_run_opts(prog_fd, &topts);
	if (!ASSERT_OK(err, "test_run_opts err"))
		return;
	if (!ASSERT_OK(topts.retval, "test_run_opts retval"))
		return;

	ASSERT_EQ(skel->arena->uaf_recovery_fails, 0, "uaf_recovery_fails");
}

void test_arena_atomics(void)
{
	struct arena_atomics *skel;
@@ -180,6 +196,8 @@ void test_arena_atomics(void)
		test_cmpxchg(skel);
	if (test__start_subtest("xchg"))
		test_xchg(skel);
	if (test__start_subtest("uaf"))
		test_uaf(skel);

cleanup:
	arena_atomics__destroy(skel);
+76 −0
Original line number Diff line number Diff line
@@ -169,3 +169,79 @@ int xchg(const void *ctx)

	return 0;
}

__u64 __arena_global uaf_sink;
volatile __u64 __arena_global uaf_recovery_fails;

SEC("syscall")
int uaf(const void *ctx)
{
	if (pid != (bpf_get_current_pid_tgid() >> 32))
		return 0;
#if defined(ENABLE_ATOMICS_TESTS) && !defined(__TARGET_ARCH_arm64) && \
    !defined(__TARGET_ARCH_x86)
	__u32 __arena *page32;
	__u64 __arena *page64;
	void __arena *page;

	page = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0);
	bpf_arena_free_pages(&arena, page, 1);
	uaf_recovery_fails = 24;

	page32 = (__u32 __arena *)page;
	uaf_sink += __sync_fetch_and_add(page32, 1);
	uaf_recovery_fails -= 1;
	__sync_add_and_fetch(page32, 1);
	uaf_recovery_fails -= 1;
	uaf_sink += __sync_fetch_and_sub(page32, 1);
	uaf_recovery_fails -= 1;
	__sync_sub_and_fetch(page32, 1);
	uaf_recovery_fails -= 1;
	uaf_sink += __sync_fetch_and_and(page32, 1);
	uaf_recovery_fails -= 1;
	__sync_and_and_fetch(page32, 1);
	uaf_recovery_fails -= 1;
	uaf_sink += __sync_fetch_and_or(page32, 1);
	uaf_recovery_fails -= 1;
	__sync_or_and_fetch(page32, 1);
	uaf_recovery_fails -= 1;
	uaf_sink += __sync_fetch_and_xor(page32, 1);
	uaf_recovery_fails -= 1;
	__sync_xor_and_fetch(page32, 1);
	uaf_recovery_fails -= 1;
	uaf_sink += __sync_val_compare_and_swap(page32, 0, 1);
	uaf_recovery_fails -= 1;
	uaf_sink += __sync_lock_test_and_set(page32, 1);
	uaf_recovery_fails -= 1;

	page64 = (__u64 __arena *)page;
	uaf_sink += __sync_fetch_and_add(page64, 1);
	uaf_recovery_fails -= 1;
	__sync_add_and_fetch(page64, 1);
	uaf_recovery_fails -= 1;
	uaf_sink += __sync_fetch_and_sub(page64, 1);
	uaf_recovery_fails -= 1;
	__sync_sub_and_fetch(page64, 1);
	uaf_recovery_fails -= 1;
	uaf_sink += __sync_fetch_and_and(page64, 1);
	uaf_recovery_fails -= 1;
	__sync_and_and_fetch(page64, 1);
	uaf_recovery_fails -= 1;
	uaf_sink += __sync_fetch_and_or(page64, 1);
	uaf_recovery_fails -= 1;
	__sync_or_and_fetch(page64, 1);
	uaf_recovery_fails -= 1;
	uaf_sink += __sync_fetch_and_xor(page64, 1);
	uaf_recovery_fails -= 1;
	__sync_xor_and_fetch(page64, 1);
	uaf_recovery_fails -= 1;
	uaf_sink += __sync_val_compare_and_swap(page64, 0, 1);
	uaf_recovery_fails -= 1;
	uaf_sink += __sync_lock_test_and_set(page64, 1);
	uaf_recovery_fails -= 1;
#endif

	return 0;
}

char _license[] SEC("license") = "GPL";