Re: [PATCH bpf-next v3 2/2] selftests/bpf: Add trampolines multi-level pointer params test coverage
From: Alexei Starovoitov
Date: Mon Feb 23 2026 - 12:02:29 EST
On Mon, Feb 23, 2026 at 12:32 AM Slava Imameev
<slava.imameev@xxxxxxxxxxxxxxx> wrote:
>
> Multi-level pointer params and return value test coverage for BPF
> trampolines:
> - fentry/fexit programs covering struct and void double/triple
> pointer parameters and returned values
> - verifier context tests covering multi-level pointers as parameters
> - verifier context tests covering multi-level pointers as returned
> values
> - verifier context tests for lsm to check trusted parameters handling
> - verifier context tests covering out-of-bound access after cast
> - verifier BPF helper tests to validate no change in verifier
> behaviour
>
> Signed-off-by: Slava Imameev <slava.imameev@xxxxxxxxxxxxxxx>
> ---
> net/bpf/test_run.c | 130 ++++++
> .../prog_tests/fentry_fexit_multi_level_ptr.c | 206 +++++++++
> .../selftests/bpf/prog_tests/verifier.c | 2 +
> .../progs/fentry_fexit_pptr_nullable_test.c | 56 +++
> .../bpf/progs/fentry_fexit_pptr_test.c | 67 +++
> .../bpf/progs/fentry_fexit_void_ppptr_test.c | 38 ++
> .../bpf/progs/fentry_fexit_void_pptr_test.c | 71 +++
> .../bpf/progs/verifier_ctx_multilevel_ptr.c | 435 ++++++++++++++++++
> 8 files changed, 1005 insertions(+)
> create mode 100644 tools/testing/selftests/bpf/prog_tests/fentry_fexit_multi_level_ptr.c
> create mode 100644 tools/testing/selftests/bpf/progs/fentry_fexit_pptr_nullable_test.c
> create mode 100644 tools/testing/selftests/bpf/progs/fentry_fexit_pptr_test.c
> create mode 100644 tools/testing/selftests/bpf/progs/fentry_fexit_void_ppptr_test.c
> create mode 100644 tools/testing/selftests/bpf/progs/fentry_fexit_void_pptr_test.c
> create mode 100644 tools/testing/selftests/bpf/progs/verifier_ctx_multilevel_ptr.c
>
> diff --git a/net/bpf/test_run.c b/net/bpf/test_run.c
> index 178c4738e63b..9f6ee2eb01cd 100644
> --- a/net/bpf/test_run.c
> +++ b/net/bpf/test_run.c
> @@ -24,6 +24,9 @@
> #include <net/netdev_rx_queue.h>
> #include <net/xdp.h>
> #include <net/netfilter/nf_bpf_link.h>
> +#include <linux/set_memory.h>
> +#include <linux/string.h>
> +#include <asm/tlbflush.h>
>
> #define CREATE_TRACE_POINTS
> #include <trace/events/bpf_test_run.h>
> @@ -563,6 +566,42 @@ noinline int bpf_fentry_test10(const void *a)
> return (long)a;
> }
>
> +struct bpf_fentry_test_pptr_t {
> + u32 value1;
> + u32 value2;
> +};
> +
> +noinline int bpf_fentry_test11_pptr_nullable(struct bpf_fentry_test_pptr_t **pptr__nullable)
> +{
> + if (!pptr__nullable)
> + return -1;
> +
> + return (*pptr__nullable)->value1;
> +}
> +
> +noinline u32 **bpf_fentry_test12_pptr(u32 id, u32 **pptr)
> +{
> + /* prevent DCE */
> + asm volatile("" : "+r"(id));
> + asm volatile("" : "+r"(pptr));
> + return pptr;
> +}
> +
> +noinline u8 bpf_fentry_test13_pptr(void **pptr)
> +{
> + void *ptr;
> +
> + return copy_from_kernel_nofault(&ptr, pptr, sizeof(pptr)) == 0;
> +}
> +
> +/* Test the verifier can handle multi-level pointer types with qualifiers. */
> +noinline void ***bpf_fentry_test14_ppptr(void **volatile *const ppptr)
> +{
> + /* prevent DCE */
> + asm volatile("" :: "r"(ppptr) : "memory");
> + return (void ***)ppptr;
> +}
> +
> noinline void bpf_fentry_test_sinfo(struct skb_shared_info *sinfo)
> {
> }
> @@ -670,20 +709,110 @@ static void *bpf_test_init(const union bpf_attr *kattr, u32 user_size,
> return data;
> }
>
> +static void *create_bad_kaddr(void)
> +{
> + /*
> + * Try to get an address that passes kernel range checks but causes
> + * a page fault handler invocation if accessed from a BPF program.
> + */
> +#if defined(CONFIG_ARCH_HAS_SET_MEMORY) && defined(CONFIG_X86)
> + void *addr = vmalloc(PAGE_SIZE);
> +
> + if (!addr)
> + return NULL;
> + /* Make it non-present - any access will fault */
> + if (set_memory_np((unsigned long)addr, 1)) {
> + vfree(addr);
> + return NULL;
> + }
> + return addr;
> +#elif defined(CONFIG_ARCH_HAS_SET_DIRECT_MAP)
> + struct page *page = alloc_page(GFP_KERNEL);
> +
> + if (!page)
> + return NULL;
> + /* Remove from direct map - any access will fault */
> + if (set_direct_map_invalid_noflush(page)) {
> + __free_page(page);
> + return NULL;
> + }
> + flush_tlb_kernel_range((unsigned long)page_address(page),
> + (unsigned long)page_address(page) + PAGE_SIZE);
> + return page_address(page);
> +#endif
This is serious overkill for a test.
See how bpf_testmod_return_ptr() does it.
pw-bot: cr