Files
linux/tools/testing/selftests/bpf/prog_tests/stream.c
Puranjay Mohan 86f2225065 selftests/bpf: Add tests for arena fault reporting
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.

The added bpf programs need to access the user_vm_start in struct
bpf_arena, this is done by casting &arena to struct bpf_arena *, but
barrier_var() is used on this ptr before accessing ptr->user_vm_start;
to stop GCC from issuing an out-of-bound access due to the cast from
smaller map struct to larger "struct bpf_arena"

Signed-off-by: Puranjay Mohan <puranjay@kernel.org>
Acked-by: Eduard Zingerman <eddyz87@gmail.com>
Link: https://lore.kernel.org/r/20250911145808.58042-7-puranjay@kernel.org
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2025-09-11 13:00:44 -07:00

109 lines
2.9 KiB
C

// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */
#include <test_progs.h>
#include <sys/mman.h>
#include "stream.skel.h"
#include "stream_fail.skel.h"
void test_stream_failure(void)
{
RUN_TESTS(stream_fail);
}
void test_stream_success(void)
{
RUN_TESTS(stream);
return;
}
void test_stream_syscall(void)
{
LIBBPF_OPTS(bpf_test_run_opts, opts);
LIBBPF_OPTS(bpf_prog_stream_read_opts, ropts);
struct stream *skel;
int ret, prog_fd;
char buf[64];
skel = stream__open_and_load();
if (!ASSERT_OK_PTR(skel, "stream__open_and_load"))
return;
prog_fd = bpf_program__fd(skel->progs.stream_syscall);
ret = bpf_prog_test_run_opts(prog_fd, &opts);
ASSERT_OK(ret, "ret");
ASSERT_OK(opts.retval, "retval");
ASSERT_LT(bpf_prog_stream_read(0, BPF_STREAM_STDOUT, buf, sizeof(buf), &ropts), 0, "error");
ret = -errno;
ASSERT_EQ(ret, -EINVAL, "bad prog_fd");
ASSERT_LT(bpf_prog_stream_read(prog_fd, 0, buf, sizeof(buf), &ropts), 0, "error");
ret = -errno;
ASSERT_EQ(ret, -ENOENT, "bad stream id");
ASSERT_LT(bpf_prog_stream_read(prog_fd, BPF_STREAM_STDOUT, NULL, sizeof(buf), NULL), 0, "error");
ret = -errno;
ASSERT_EQ(ret, -EFAULT, "bad stream buf");
ret = bpf_prog_stream_read(prog_fd, BPF_STREAM_STDOUT, buf, 2, NULL);
ASSERT_EQ(ret, 2, "bytes");
ret = bpf_prog_stream_read(prog_fd, BPF_STREAM_STDOUT, buf, 2, NULL);
ASSERT_EQ(ret, 1, "bytes");
ret = bpf_prog_stream_read(prog_fd, BPF_STREAM_STDOUT, buf, 1, &ropts);
ASSERT_EQ(ret, 0, "no bytes stdout");
ret = bpf_prog_stream_read(prog_fd, BPF_STREAM_STDERR, buf, 1, &ropts);
ASSERT_EQ(ret, 0, "no bytes stderr");
stream__destroy(skel);
}
static void test_address(struct bpf_program *prog, unsigned long *fault_addr_p)
{
LIBBPF_OPTS(bpf_test_run_opts, opts);
LIBBPF_OPTS(bpf_prog_stream_read_opts, ropts);
int ret, prog_fd;
char fault_addr[64];
char buf[1024];
prog_fd = bpf_program__fd(prog);
ret = bpf_prog_test_run_opts(prog_fd, &opts);
ASSERT_OK(ret, "ret");
ASSERT_OK(opts.retval, "retval");
sprintf(fault_addr, "0x%lx", *fault_addr_p);
ret = bpf_prog_stream_read(prog_fd, BPF_STREAM_STDERR, buf, sizeof(buf), &ropts);
ASSERT_GT(ret, 0, "stream read");
ASSERT_LE(ret, 1023, "len for buf");
buf[ret] = '\0';
if (!ASSERT_HAS_SUBSTR(buf, fault_addr, "fault_addr")) {
fprintf(stderr, "Output from stream:\n%s\n", buf);
fprintf(stderr, "Fault Addr: %s\n", fault_addr);
}
}
void test_stream_arena_fault_address(void)
{
struct stream *skel;
#if !defined(__x86_64__) && !defined(__aarch64__)
printf("%s:SKIP: arena fault reporting not supported\n", __func__);
test__skip();
return;
#endif
skel = stream__open_and_load();
if (!ASSERT_OK_PTR(skel, "stream__open_and_load"))
return;
if (test__start_subtest("read_fault"))
test_address(skel->progs.stream_arena_read_fault, &skel->bss->fault_addr);
if (test__start_subtest("write_fault"))
test_address(skel->progs.stream_arena_write_fault, &skel->bss->fault_addr);
stream__destroy(skel);
}