On Wed, 27 Aug 2025 at 21:55, Yonghong Song <yonghong.song@xxxxxxxxx> wrote: > > > > On 8/27/25 8:37 AM, Puranjay Mohan wrote: > > Add selftests for testing the reporting of arena page faults through BPF > > streams. Two new bpf programs are added that read and write to an > > unmapped arena address and the fault reporting is verified in the > > userspace through streams. > > > > Signed-off-by: Puranjay Mohan <puranjay@xxxxxxxxxx> > > --- > > .../testing/selftests/bpf/prog_tests/stream.c | 33 +++++++++++++++- > > tools/testing/selftests/bpf/progs/stream.c | 39 +++++++++++++++++++ > > 2 files changed, 71 insertions(+), 1 deletion(-) > > > > diff --git a/tools/testing/selftests/bpf/prog_tests/stream.c b/tools/testing/selftests/bpf/prog_tests/stream.c > > index 36a1a1ebde692..8fdc83260ea14 100644 > > --- a/tools/testing/selftests/bpf/prog_tests/stream.c > > +++ b/tools/testing/selftests/bpf/prog_tests/stream.c > > @@ -41,6 +41,22 @@ struct { > > "([a-zA-Z_][a-zA-Z0-9_]*\\+0x[0-9a-fA-F]+/0x[0-9a-fA-F]+\n" > > "|[ \t]+[^\n]+\n)*", > > }, > > + { > > + offsetof(struct stream, progs.stream_arena_read_fault), > > + "ERROR: Arena READ access at unmapped address 0x.*\n" > > + "CPU: [0-9]+ UID: 0 PID: [0-9]+ Comm: .*\n" > > + "Call trace:\n" > > + "([a-zA-Z_][a-zA-Z0-9_]*\\+0x[0-9a-fA-F]+/0x[0-9a-fA-F]+\n" > > + "|[ \t]+[^\n]+\n)*", > > + }, > > + { > > + offsetof(struct stream, progs.stream_arena_write_fault), > > + "ERROR: Arena WRITE access at unmapped address 0x.*\n" > > + "CPU: [0-9]+ UID: 0 PID: [0-9]+ Comm: .*\n" > > + "Call trace:\n" > > + "([a-zA-Z_][a-zA-Z0-9_]*\\+0x[0-9a-fA-F]+/0x[0-9a-fA-F]+\n" > > + "|[ \t]+[^\n]+\n)*", > > + }, > > }; > > > > static int match_regex(const char *pattern, const char *string) > > @@ -63,6 +79,7 @@ void test_stream_errors(void) > > struct stream *skel; > > int ret, prog_fd; > > char buf[1024]; > > + char fault_addr[64] = {0}; > > You can replace '{0}' to '{}' so the whole array will be initialized. > > > > > skel = stream__open_and_load(); > > if (!ASSERT_OK_PTR(skel, "stream__open_and_load")) > > @@ -85,6 +102,14 @@ void test_stream_errors(void) > > continue; > > } > > #endif > > +#if !defined(__x86_64__) && !defined(__aarch64__) > > + ASSERT_TRUE(1, "Arena fault reporting unsupported, skip."); > > + if (i == 2 || i == 3) { > > + ret = bpf_prog_stream_read(prog_fd, 2, buf, sizeof(buf), &ropts); > > + ASSERT_EQ(ret, 0, "stream read"); > > + continue; > > + } > > +#endif > > > > ret = bpf_prog_stream_read(prog_fd, BPF_STREAM_STDERR, buf, sizeof(buf), &ropts); > > ASSERT_GT(ret, 0, "stream read"); > > @@ -92,8 +117,14 @@ void test_stream_errors(void) > > buf[ret] = '\0'; > > > > ret = match_regex(stream_error_arr[i].errstr, buf); > > - if (!ASSERT_TRUE(ret == 1, "regex match")) > > + if (ret && (i == 2 || i == 3)) { > > + sprintf(fault_addr, "0x%lx", skel->bss->fault_addr); > > + ret = match_regex(fault_addr, buf); > > + } > > + if (!ASSERT_TRUE(ret == 1, "regex match")) { > > fprintf(stderr, "Output from stream:\n%s\n", buf); > > + fprintf(stderr, "Fault Addr: 0x%lx\n", skel->bss->fault_addr); > > This will fault addr even for i == 0 or i == 1 and those address may be confusing > for test 0/1. > > > + } > > } > > > > stream__destroy(skel); > > diff --git a/tools/testing/selftests/bpf/progs/stream.c b/tools/testing/selftests/bpf/progs/stream.c > > index 35790897dc879..9de015ac3ced5 100644 > > --- a/tools/testing/selftests/bpf/progs/stream.c > > +++ b/tools/testing/selftests/bpf/progs/stream.c > > @@ -5,6 +5,7 @@ > > #include <bpf/bpf_helpers.h> > > #include "bpf_misc.h" > > #include "bpf_experimental.h" > > +#include "bpf_arena_common.h" > > > > struct arr_elem { > > struct bpf_res_spin_lock lock; > > @@ -17,10 +18,17 @@ struct { > > __type(value, struct arr_elem); > > } arrmap SEC(".maps"); > > > > +struct { > > + __uint(type, BPF_MAP_TYPE_ARENA); > > + __uint(map_flags, BPF_F_MMAPABLE); > > + __uint(max_entries, 1); /* number of pages */ > > +} arena SEC(".maps"); > > + > > #define ENOSPC 28 > > #define _STR "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" > > > > int size; > > +u64 fault_addr; > > > > SEC("syscall") > > __success __retval(0) > > @@ -76,4 +84,35 @@ int stream_syscall(void *ctx) > > return 0; > > } > > > > +SEC("syscall") > > +__success __retval(0) > > +int stream_arena_write_fault(void *ctx) > > +{ > > + struct bpf_arena *ptr = (void *)&arena; > > + u64 user_vm_start; > > + > > + barrier_var(ptr); > > Do we need this barrier_var()? I tried llvm20 and it works fine without the > above barrier_var(). Puranjay should add some context in the commit log or comments for this, but I think the reason was that we found that GCC would diagnose accesses into ptr assuming it points into the actual typeof(arena) object defined in the C file (which is the right thing to do from a language standpoint), but that obviously leads to misdiagnosis once we access something in struct bpf_arena that goes out of bounds for this type. So laundering the pointer through barrier_var gets rid of the provenance information and the warning/error is gone. But it would be better to add a comment since it's not obvious why. > > > + user_vm_start = ptr->user_vm_start; > > + > > Remove this line. > > > + fault_addr = user_vm_start + 0xbeef; > > + *(u32 __arena *)(user_vm_start + 0xbeef) = 1; > > Simplify to *(u32 __arena *)fault = 1; > > > + > > Remove this line. > > > + return 0; > > +} > > + > > +SEC("syscall") > > +__success __retval(0) > > +int stream_arena_read_fault(void *ctx) > > +{ > > + struct bpf_arena *ptr = (void *)&arena; > > + u64 user_vm_start; > > + > > + barrier_var(ptr); > > Is this necessary? > > > + user_vm_start = ptr->user_vm_start; > > + > > Remove this line. > > > + fault_addr = user_vm_start + 0xbeef; > > + > > Remove this line. > > > + return *(u32 __arena *)(user_vm_start + 0xbeef); > > return*(u32 __arena *)fault_addr. > > > +} > > + > > char _license[] SEC("license") = "GPL"; >