[PATCH 4/6] selftests/bpf: libarena: Add calloc() call

Emil Tsalapatis <[email protected]>
Newsgroups org.kernel.vger.bpf
Message-ID <[email protected]>
The arena memory allocator currently does not provide the
option to zero-initialize the allocated memory. Provide
a calloc() operation that calls memset() on the returned
memory. Reuse naive memset implementation already present
in the arena ASAN code for now. Subsequent patches will
optimize the function.

Signed-off-by: Emil Tsalapatis <[email protected]>
---
 .../bpf/libarena/include/libarena/common.h    | 18 +++++++++++++
 .../selftests/bpf/libarena/src/asan.bpf.c     | 21 ++-------------
 .../selftests/bpf/libarena/src/common.bpf.c   | 27 ++++++++++++++++++-
 3 files changed, 46 insertions(+), 20 deletions(-)

diff --git a/tools/testing/selftests/bpf/libarena/include/libarena/common.h b/tools/testing/selftests/bpf/libarena/include/libarena/common.h
index 931ace9a49e2..d32a51ff5e7f 100644
--- a/tools/testing/selftests/bpf/libarena/include/libarena/common.h
+++ b/tools/testing/selftests/bpf/libarena/include/libarena/common.h
@@ -49,6 +49,7 @@ extern volatile u64 asan_violated;
 int arena_fls(__u64 word);
 
 void __arena *arena_malloc(size_t size);
+void __arena *arena_calloc(size_t ncount, size_t size);
 void arena_free(void __arena *ptr);
 
 /*
@@ -61,6 +62,23 @@ void arena_free(void __arena *ptr);
  */
 #define arena_subprog_init() do { asm volatile ("" :: "r"(&arena)); } while (0)
 
+/*
+ * BPF does not currently support the memset 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
+ * implementations for the intrinsic.
+ */
+static inline int arena_memset(s8 __arena *dst, s8 val, size_t size)
+{
+	size_t i;
+
+	for (i = zero; i < size && can_loop; i++)
+		dst[i] = val;
+
+	return 0;
+}
+
 #else /* ! __BPF__ */
 
 #include <stdint.h>
diff --git a/tools/testing/selftests/bpf/libarena/src/asan.bpf.c b/tools/testing/selftests/bpf/libarena/src/asan.bpf.c
index 5135d5c72a46..de656b69d13a 100644
--- a/tools/testing/selftests/bpf/libarena/src/asan.bpf.c
+++ b/tools/testing/selftests/bpf/libarena/src/asan.bpf.c
@@ -103,23 +103,6 @@ volatile bool asan_inited = false;
  */
 volatile bool asan_report_once = false;
 
-/*
- * 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(s8 __arena *dst, s8 val, size_t size)
-{
-	size_t i;
-
-	for (i = zero; i < size && can_loop; i++)
-		dst[i] = val;
-
-	return 0;
-}
-
 /* Validate a 1-byte access, always within a single byte. */
 static __always_inline bool memory_is_poisoned_1(s8 __arena *addr)
 {
@@ -422,7 +405,7 @@ __hidden __noasan int asan_poison(void __arena *addr, s8 val, size_t size)
 	shadow = mem_to_shadow(addr);
 	len = size >> ASAN_SHADOW_SHIFT;
 
-	asan_memset(shadow, val, len);
+	arena_memset(shadow, val, len);
 
 	return 0;
 }
@@ -463,7 +446,7 @@ __hidden __noasan int asan_unpoison(void __arena *addr, size_t size)
 	shadow = mem_to_shadow(addr);
 	len = size >> ASAN_SHADOW_SHIFT;
 
-	asan_memset(shadow, 0, len);
+	arena_memset(shadow, 0, len);
 
 	/*
 	 * If we are allocating a non-granule aligned region, we need to adjust
diff --git a/tools/testing/selftests/bpf/libarena/src/common.bpf.c b/tools/testing/selftests/bpf/libarena/src/common.bpf.c
index 569f0f64d518..785d4872cb49 100644
--- a/tools/testing/selftests/bpf/libarena/src/common.bpf.c
+++ b/tools/testing/selftests/bpf/libarena/src/common.bpf.c
@@ -1,5 +1,7 @@
 // SPDX-License-Identifier: LGPL-2.1 OR BSD-2-Clause
 /* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
+#include <limits.h>
+
 #include <libarena/common.h>
 #include <libarena/asan.h>
 #include <libarena/buddy.h>
@@ -48,10 +50,33 @@ __weak void __arena *arena_malloc(size_t size)
 	return buddy_alloc(&buddy, size);
 }
 
+__weak void __arena *arena_calloc(size_t ncount, size_t size)
+{
+	void __arena *mem;
+	size_t total;
+
+	/*
+	 * Ideally we'd be using __builtin_mul_overflow here,
+	 * but the BPF compiler backend doesn't implement __multi3.
+	 * There are ways to optimize the division away from the
+	 * overflow check, but any costs are dwarfed by the
+	 * buddy_alloc() call. Keep it simple for now.
+	 */
+	if (unlikely(ncount && size >= ULLONG_MAX / ncount))
+		return NULL;
+
+	total = ncount * size;
+
+	mem = buddy_alloc(&buddy, total);
+	if (likely(mem))
+		arena_memset(mem, 0, total);
+
+	return mem;
+}
+
 __weak void arena_free(void __arena *ptr)
 {
 	buddy_free(&buddy, ptr);
 }
 
-
 char _license[] SEC("license") = "GPL";
-- 
2.54.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.