All of lore.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.