public inbox for bpf@vger.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Emil Tsalapatis" <emil@etsalapatis.com>
Cc: bpf@vger.kernel.org
Subject: Re: [PATCH bpf-next v9 4/8] selftests/bpf: Add arena ASAN runtime to libarena
Date: Sun, 26 Apr 2026 20:12:35 +0000	[thread overview]
Message-ID: <20260426201236.7A8A7C2BCAF@smtp.kernel.org> (raw)
In-Reply-To: <20260426190338.4615-5-emil@etsalapatis.com>

Sashiko AI review found 6 potential issue(s):
- [Medium] Concurrent `asan_init` calls corrupt global ASAN state
- [Medium] Early loop exit due to `can_loop` causes silent validation bypass and incomplete poisoning
- [Medium] Missing bounds checking in `asan_poison` and `asan_unpoison` allows out-of-bounds writes
- [Medium] Uninitialized ASAN state leads to silent memory corruption
- [Low] Compilation error due to missing argument for `bpf_stream_print_stack`
- [Low] Compilation and linking errors due to missing ASAN functions and BTF anchors
--

commit 378c2bf4d6c7fb041761c5a5045f1680acbb2840
Author: Emil Tsalapatis <emil@etsalapatis.com>

selftests/bpf: Add arena ASAN runtime to libarena

This commit adds an ASAN runtime to the arena library for BPF selftests.
It implements LLVM sanitization hooks and provides manual poison/unpoison
functions.

> diff --git a/tools/testing/selftests/bpf/libarena/include/libarena/asan.h b/tools/testing/selftests/bpf/libarena/include/libarena/asan.h
> new file mode 100644
> index 000000000000..eb9fc69d9eb0
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/libarena/include/libarena/asan.h
[ ... ]
> @@ -0,0 +1,103 @@
[ ... ]
> +/*
> + * Force LLVM to emit BTF information for the stubs,
> + * because the ASAN pass in LLVM by itself doesn't.
> + */
> +#define ASAN_LOAD_STORE_SIZE(size)		\
> +	__asan_store##size,			\
> +	__asan_store##size##_noabort,		\
> +	__asan_load##size,			\
> +	__asan_load##size##_noabort,		\
> +	__asan_report_store##size,		\
> +	__asan_report_store##size##_noabort,	\
> +	__asan_report_load##size,		\
> +	__asan_report_load##size##_noabort
> +
> +__attribute__((used))
> +static void (*__asan_btf_anchors[])(intptr_t) = {
> +	ASAN_LOAD_STORE_SIZE(1),
> +	ASAN_LOAD_STORE_SIZE(2),
> +	ASAN_LOAD_STORE_SIZE(4),
> +	ASAN_LOAD_STORE_SIZE(8),
> +};

Does this array need to include variable-sized hooks (like __asan_storeN),
global handlers, and memory intrinsics? Without these in the anchors list,
the compiler might not emit BTF data for them, potentially causing BPF load
failures if LLVM instruments variable-sized accesses.

> diff --git a/tools/testing/selftests/bpf/libarena/src/asan.bpf.c b/tools/testing/selftests/bpf/libarena/src/asan.bpf.c
> new file mode 100644
> index 000000000000..64c5b990086c
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/libarena/src/asan.bpf.c
[ ... ]
> @@ -0,0 +1,553 @@
[ ... ]
> +/*
> + * BPF does not currently support the memset/memcpy/memcmp intrinsics.
> + * For large sequential copies, or assignments of large data structures,
> + * the frontend will generate an intrinsic that causes the BPF backend
> + * to exit due to a missing implementation. Provide a simple implementation
> + * just for memset to use it for poisoning/unpoisoning the map.
> + */
> +__weak int asan_memset(s8a __arg_arena *dst, s8 val, size_t size)
> +{
> +	size_t i;
> +
> +	for (i = zero; i < size && can_loop; i++)
> +		dst[i] = val;
> +
> +	return 0;
> +}

Is can_loop defined anywhere? This looks like it might cause an undeclared
identifier compilation error.

Also, if can_loop causes the loop to exit early due to instruction limits,
does this result in incomplete poisoning? asan_memset would return 0, leaving
the rest of the shadow memory uninitialized.

[ ... ]
> +static __always_inline u64 first_nonzero_byte(u64 addr, size_t size)
> +{
> +	while (size && can_loop) {
> +		if (unlikely(*(s8a *)addr))
> +			return addr;
> +		addr += 1;
> +		size -= 1;
> +	}
> +
> +	return SHADOW_ALL_ZEROES;
> +}

Similarly, if can_loop terminates this loop early, could this falsely validate
the remaining memory and allow large out-of-bounds accesses to bypass ASAN
validation?

[ ... ]
> +__weak int asan_report(s8a __arg_arena *addr, size_t sz, u32 flags)
> +{
> +	u32 reported = __sync_val_compare_and_swap(&asan_reported, false, true);
> +
> +	/* Only report the first ASAN violation. */
> +	if (reported && asan_report_once)
> +		return 0;
> +
> +	asan_violated = (u64)addr;
> +
> +	arena_stderr("Memory violation for address %p (0x%lx) for %s of size %ld\n",
> +			addr, (u64)addr,
> +			(flags & ASAN_WRITE) ? "write" : "read",
> +			sz);
> +	bpf_stream_print_stack(BPF_STDERR);

Is this missing an argument? The kernel definition of this kfunc is
bpf_stream_print_stack(int stream_id, struct bpf_prog_aux *aux). Compiling
this with vmlinux.h could result in a compilation error.

[ ... ]
> +__hidden __noasan int asan_poison(void __arena *addr, s8 val, size_t size)
> +{
> +	s8a *shadow;
> +	size_t len;
[ ... ]
> +	shadow = mem_to_shadow(addr);
> +	len = size >> ASAN_SHADOW_SHIFT;
> +
> +	asan_memset(shadow, val, len);
> +
> +	return 0;
> +}

Does this code verify that asan_init() has been called before executing?
If __asan_shadow_memory_dynamic_address is 0, mem_to_shadow() could resolve
to the beginning of the user arena, causing asan_memset() to overwrite valid
application data.

Additionally, is there a bounds check missing to ensure addr + size falls
entirely within the valid user region? If size extends beyond the user
boundaries, could this write past the shadow map and corrupt the arena's
global variables?

[ ... ]
> +__hidden __noasan int asan_unpoison(void __arena *addr, size_t size)
> +{
> +	size_t partial = size & ASAN_GRANULE_MASK;
> +	s8a *shadow;
> +	size_t len;
[ ... ]
> +	shadow = mem_to_shadow(addr);
> +	len = size >> ASAN_SHADOW_SHIFT;
> +
> +	asan_memset(shadow, 0, len);

Does this unpoisoning function have the same uninitialized state and bounds
checking concerns as asan_poison()?

[ ... ]
> +SEC("syscall")
> +__weak __noasan int asan_init(struct asan_init_args *args)
> +{
> +	u64 globals_pages = args->arena_globals_pages;
> +	u64 all_pages = args->arena_all_pages;
> +	u64 shadow_map, shadow_pgoff;
> +	u64 shadow_pages;
> +
> +	if (asan_inited)
> +		return 0;
[ ... ]
> +	shadow_pgoff = all_pages - shadow_pages - globals_pages;
> +	__asan_shadow_memory_dynamic_address = shadow_pgoff * __PAGE_SIZE;
> +
> +	/*
> +	 * Allocate the last (1/ASAN_SHADOW_SCALE)th of an arena's pages for the map
> +	 * We find the offset and size from the arena map.
> +	 *
> +	 * The allocated map pages are zeroed out, meaning all memory is marked as valid
> +	 * even if it's not allocated already. This is expected: Since the actual memory
> +	 * pages are not allocated, accesses to it will trigger page faults and will be
> +	 * reported through BPF streams. Any pages allocated through bpf_arena_alloc_pages
> +	 * should be poisoned by the allocator right after the call succeeds.
> +	 */
> +	shadow_map = (u64)bpf_arena_alloc_pages(
> +		&arena, (void __arena *)__asan_shadow_memory_dynamic_address,
> +		shadow_pages, NUMA_NO_NODE, 0);
> +	if (!shadow_map) {
> +		arena_stderr("Could not allocate shadow map\n");
> +
> +		__asan_shadow_memory_dynamic_address = 0;
> +
> +		return -ENOMEM;
> +	}
> +
> +	asan_inited = true;
> +
> +	return 0;
> +}

Can this code corrupt the global shadow memory base if invoked concurrently?

If two threads bypass the lockless asan_inited check, thread 1 could
successfully allocate memory and set asan_inited = true. Thread 2 might
then fail its allocation (since the pages are already mapped) and execute
the error path, setting __asan_shadow_memory_dynamic_address = 0.

Would this permanently corrupt the global ASAN state for all subsequent checks
in the arena?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260426190338.4615-1-emil@etsalapatis.com?part=4

  reply	other threads:[~2026-04-26 20:12 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-04-26 19:03 [PATCH bpf-next v9 0/8] Introduce arena library and runtime Emil Tsalapatis
2026-04-26 19:03 ` [PATCH bpf-next v9 1/8] selftests/bpf: Add ifdef guard for WRITE_ONCE macro in bpf_atomic.h Emil Tsalapatis
2026-04-26 19:03 ` [PATCH bpf-next v9 2/8] selftests/bpf: Add basic libarena scaffolding Emil Tsalapatis
2026-04-26 19:34   ` sashiko-bot
2026-04-26 19:03 ` [PATCH bpf-next v9 3/8] selftests/bpf: Move arena-related headers into libarena Emil Tsalapatis
2026-04-26 19:03 ` [PATCH bpf-next v9 4/8] selftests/bpf: Add arena ASAN runtime to libarena Emil Tsalapatis
2026-04-26 20:12   ` sashiko-bot [this message]
2026-04-26 19:03 ` [PATCH bpf-next v9 5/8] selftests/bpf: Add ASAN support for libarena selftests Emil Tsalapatis
2026-04-26 19:33   ` bot+bpf-ci
2026-04-26 20:28   ` sashiko-bot
2026-04-26 19:03 ` [PATCH bpf-next v9 6/8] selftests/bpf: Add buddy allocator for libarena Emil Tsalapatis
2026-04-26 19:46   ` bot+bpf-ci
2026-04-26 20:54   ` sashiko-bot
2026-04-26 19:03 ` [PATCH bpf-next v9 7/8] selftests/bpf: Add selftests for libarena buddy allocator Emil Tsalapatis
2026-04-26 21:09   ` sashiko-bot
2026-04-26 19:03 ` [PATCH bpf-next v9 8/8] selftests/bpf: Reuse stderr parsing for libarena ASAN tests Emil Tsalapatis
2026-04-26 19:46   ` bot+bpf-ci
2026-04-26 21:38   ` sashiko-bot
2026-04-27  1:20 ` [PATCH bpf-next v9 0/8] Introduce arena library and runtime patchwork-bot+netdevbpf

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260426201236.7A8A7C2BCAF@smtp.kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=emil@etsalapatis.com \
    --cc=sashiko@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox