Skip to content

Commit 64cca36

Browse files
etsalKernel Patches Daemon
authored andcommitted
selftests/bpf: Reuse stderr parsing for libarena ASAN tests
Add code to directly test the output of libarena ASAN tests. The code reuses testing infrastructure originally for BPF streams to verify that ASAN emits call stacks when the selftests trigger a memory error. Since stderr() testing uses logic from test_progs, it is only available on the test_progs-based selftest runner. The standalone runner still uses internal ASAN state to verify access errors are triaged as expected. Signed-off-by: Emil Tsalapatis <emil@etsalapatis.com>
1 parent fcc37e2 commit 64cca36

5 files changed

Lines changed: 76 additions & 12 deletions

File tree

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// SPDX-License-Identifier: LGPL-2.1 OR BSD-2-Clause
2+
/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
3+
#pragma once
4+
5+
#ifdef __BPF__
6+
7+
/* Selftests use these tags for compatibility with test_progs. */
8+
#define __test_tag(tag) __attribute__((btf_decl_tag("comment:" XSTR(__COUNTER__) ":" tag)))
9+
#define __stderr(msg) __test_tag("test_expect_stderr=" msg)
10+
#define __stderr_unpriv(msg) __test_tag("test_expect_stderr_unpriv=" msg)
11+
12+
#define XSTR(s) STR(s)
13+
#define STR(s) #s
14+
15+
#endif

tools/testing/selftests/bpf/libarena/selftests/st_asan_buddy.bpf.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
#include <asan.h>
77
#include <buddy.h>
88

9+
/* Required for parsing the ASAN call stacks. */
10+
#include <test_progs_compat.h>
11+
912
extern buddy_t buddy;
1013

1114
#ifdef BPF_ARENA_ASAN
@@ -142,6 +145,11 @@ static __always_inline int asan_test_buddy_blob_single(void)
142145
}
143146

144147
SEC("syscall")
148+
__stderr("Memory violation for address {{.*}} for write of size 1")
149+
__stderr("CPU: {{[0-9]+}} UID: 0 PID: {{[0-9]+}} Comm: {{.*}}")
150+
__stderr("Call trace:\n"
151+
"{{([a-zA-Z_][a-zA-Z0-9_]*\\+0x[0-9a-fA-F]+/0x[0-9a-fA-F]+\n"
152+
"|[ \t]+[^\n]+\n)*}}")
145153
__weak int asan_test_buddy_oob(void)
146154
{
147155
size_t sizes[] = {
@@ -175,6 +183,11 @@ __weak int asan_test_buddy_oob(void)
175183
}
176184

177185
SEC("syscall")
186+
__stderr("Memory violation for address {{.*}} for write of size 1")
187+
__stderr("CPU: {{[0-9]+}} UID: 0 PID: {{[0-9]+}} Comm: {{.*}}")
188+
__stderr("Call trace:\n"
189+
"{{([a-zA-Z_][a-zA-Z0-9_]*\\+0x[0-9a-fA-F]+/0x[0-9a-fA-F]+\n"
190+
"|[ \t]+[^\n]+\n)*}}")
178191
__weak int asan_test_buddy_uaf(void)
179192
{
180193
size_t sizes[] = { 16, 32, 64, 128, 256, 512, 1024, 16384 };
@@ -206,6 +219,11 @@ __weak int asan_test_buddy_uaf(void)
206219
}
207220

208221
SEC("syscall")
222+
__stderr("Memory violation for address {{.*}} for write of size 1")
223+
__stderr("CPU: {{[0-9]+}} UID: 0 PID: {{[0-9]+}} Comm: {{.*}}")
224+
__stderr("Call trace:\n"
225+
"{{([a-zA-Z_][a-zA-Z0-9_]*\\+0x[0-9a-fA-F]+/0x[0-9a-fA-F]+\n"
226+
"|[ \t]+[^\n]+\n)*}}")
209227
__weak int asan_test_buddy_blob(void)
210228
{
211229
const int iters = 10;

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ static void run_libarena_asan_test(struct libarena_asan *skel,
2525

2626
ret = libarena_run_prog(bpf_program__fd(prog));
2727
ASSERT_OK(ret, name);
28+
29+
verify_test_stderr(skel->obj, prog);
2830
}
2931

3032
static void run_test(void)

tools/testing/selftests/bpf/test_loader.c

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ void test_loader_fini(struct test_loader *tester)
9393
free(tester->log_buf);
9494
}
9595

96-
static void free_msgs(struct expected_msgs *msgs)
96+
void free_msgs(struct expected_msgs *msgs)
9797
{
9898
int i;
9999

@@ -789,6 +789,43 @@ static void emit_stderr(const char *stderr, bool force)
789789
fprintf(stdout, "STDERR:\n=============\n%s=============\n", stderr);
790790
}
791791

792+
static void verify_stderr(int prog_fd, struct expected_msgs *msgs)
793+
{
794+
LIBBPF_OPTS(bpf_prog_stream_read_opts, ropts);
795+
char *buf;
796+
int ret;
797+
798+
if (!msgs->cnt)
799+
return;
800+
801+
buf = malloc(TEST_LOADER_LOG_BUF_SZ);
802+
if (!ASSERT_OK_PTR(buf, "malloc"))
803+
return;
804+
805+
ret = bpf_prog_stream_read(prog_fd, 2, buf, TEST_LOADER_LOG_BUF_SZ - 1,
806+
&ropts);
807+
if (ret > 0) {
808+
buf[ret] = '\0';
809+
emit_stderr(buf, false);
810+
validate_msgs(buf, msgs, emit_stderr);
811+
} else {
812+
ASSERT_GT(ret, 0, "stderr stream read");
813+
}
814+
815+
free(buf);
816+
}
817+
818+
void verify_test_stderr(struct bpf_object *obj, struct bpf_program *prog)
819+
{
820+
struct test_spec spec = {};
821+
822+
if (parse_test_spec(NULL, obj, prog, &spec))
823+
return;
824+
825+
verify_stderr(bpf_program__fd(prog), &spec.priv.stderr);
826+
free_test_spec(&spec);
827+
}
828+
792829
static void emit_stdout(const char *bpf_stdout, bool force)
793830
{
794831
if (!force && env.verbosity == VERBOSE_NONE)
@@ -1314,17 +1351,7 @@ void run_subtest(struct test_loader *tester,
13141351
goto tobj_cleanup;
13151352
}
13161353

1317-
if (subspec->stderr.cnt) {
1318-
err = get_stream(2, bpf_program__fd(tprog),
1319-
tester->log_buf, tester->log_buf_sz);
1320-
if (err <= 0) {
1321-
PRINT_FAIL("Unexpected retval from get_stream(): %d, errno = %d\n",
1322-
err, errno);
1323-
goto tobj_cleanup;
1324-
}
1325-
emit_stderr(tester->log_buf, false /*force*/);
1326-
validate_msgs(tester->log_buf, &subspec->stderr, emit_stderr);
1327-
}
1354+
verify_stderr(bpf_program__fd(tprog), &subspec->stderr);
13281355

13291356
if (subspec->stdout.cnt) {
13301357
err = get_stream(1, bpf_program__fd(tprog),

tools/testing/selftests/bpf/test_progs.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -563,5 +563,7 @@ struct expected_msgs {
563563

564564
void validate_msgs(const char *log_buf, struct expected_msgs *msgs,
565565
void (*emit_fn)(const char *buf, bool force));
566+
void free_msgs(struct expected_msgs *msgs);
567+
void verify_test_stderr(struct bpf_object *obj, struct bpf_program *prog);
566568

567569
#endif /* __TEST_PROGS_H */

0 commit comments

Comments
 (0)