selftests/bpf: Cover helper memory access permissions

Check that fixed-size and sized helper input/output buffers require both
read and write permission. Cover the MTU and FIB helpers, including read-only
and write-only rejection and read/write positive controls. Exercise the XDP
prototypes and the sock_ops header-option input/output argument as well.

Keep write-only maps usable as destinations for partial-output helpers
bpf_snprintf() and bpf_sysctl_get_name(), while rejecting a read-only
snprintf destination. Also retain a write-only-map destination for
bpf_get_current_comm(), whose output is annotated MEM_UNINIT and fully
initialized by the helper.

Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Link: https://patch.msgid.link/20260921023843.411943-12-memxor@gmail.com
This commit is contained in:
Eduard Zingerman 2026-09-21 04:38:35 +02:00 committed by Alexei Starovoitov
parent 7a6cef39b7
commit ad36589c3f
No known key found for this signature in database
2 changed files with 231 additions and 0 deletions

View File

@ -1,4 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
/* Converted from tools/testing/selftests/bpf/verifier/helper_access_var_len.c */
#include <linux/bpf.h>
@ -822,6 +823,148 @@ __naked void bytes_no_leak_init_memory(void)
: __clobber_all);
}
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__uint(max_entries, 1);
__uint(map_flags, BPF_F_WRONLY_PROG);
__type(key, __u32);
__type(value, struct bpf_fib_lookup);
} map_fib_wo SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__uint(max_entries, 1);
__type(key, __u32);
__type(value, struct bpf_fib_lookup);
} map_fib_rw SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__uint(max_entries, 1);
__uint(map_flags, BPF_F_RDONLY_PROG);
__type(key, __u32);
__type(value, struct bpf_fib_lookup);
} map_fib_ro SEC(".maps");
SEC("tc")
__failure __msg("read from map forbidden")
int writeonly_sized_input(struct __sk_buff *ctx)
{
struct bpf_fib_lookup *params;
__u32 key = 0;
params = bpf_map_lookup_elem(&map_fib_wo, &key);
if (params)
bpf_fib_lookup(ctx, params, sizeof(*params), 0);
return 0;
}
SEC("tc")
__success
int readwrite_sized_input(struct __sk_buff *ctx)
{
struct bpf_fib_lookup *params;
__u32 key = 0;
params = bpf_map_lookup_elem(&map_fib_rw, &key);
if (params)
bpf_fib_lookup(ctx, params, sizeof(*params), 0);
return 0;
}
SEC("tc")
__failure __msg("write into map forbidden")
int readonly_sized_output(struct __sk_buff *ctx)
{
struct bpf_fib_lookup *params;
__u32 key = 0;
params = bpf_map_lookup_elem(&map_fib_ro, &key);
if (params)
bpf_fib_lookup(ctx, params, sizeof(*params), 0);
return 0;
}
SEC("xdp")
__failure __msg("read from map forbidden")
int xdp_writeonly_sized_input(struct xdp_md *ctx)
{
struct bpf_fib_lookup *params;
__u32 key = 0;
params = bpf_map_lookup_elem(&map_fib_wo, &key);
if (params)
bpf_fib_lookup(ctx, params, sizeof(*params), 0);
return XDP_PASS;
}
SEC("sockops")
__failure __msg("read from map forbidden")
int writeonly_header_option(struct bpf_sock_ops *ctx)
{
struct bpf_fib_lookup *buf;
__u32 key = 0;
buf = bpf_map_lookup_elem(&map_fib_wo, &key);
if (buf)
bpf_load_hdr_opt(ctx, buf, sizeof(*buf), 0);
return 0;
}
SEC("sockops")
__success
int readwrite_header_option(struct bpf_sock_ops *ctx)
{
struct bpf_fib_lookup *buf;
__u32 key = 0;
buf = bpf_map_lookup_elem(&map_fib_rw, &key);
if (buf)
bpf_load_hdr_opt(ctx, buf, sizeof(*buf), 0);
return 0;
}
SEC("tc")
__success
int snprintf_writeonly_output(struct __sk_buff *ctx)
{
void *buf;
__u32 key = 0;
buf = bpf_map_lookup_elem(&map_fib_wo, &key);
if (buf)
bpf_snprintf(buf, 16, "ok", NULL, 0);
return 0;
}
SEC("tc")
__failure __msg("write into map forbidden")
int snprintf_readonly_output(struct __sk_buff *ctx)
{
void *buf;
__u32 key = 0;
buf = bpf_map_lookup_elem(&map_fib_ro, &key);
if (buf)
bpf_snprintf(buf, 16, "ok", NULL, 0);
return 0;
}
SEC("cgroup/sysctl")
__success
__caps_unpriv(CAP_BPF | CAP_NET_ADMIN)
__success_unpriv
int sysctl_writeonly_output(struct bpf_sysctl *ctx)
{
void *buf;
__u32 key = 0;
buf = bpf_map_lookup_elem(&map_fib_wo, &key);
if (buf)
bpf_sysctl_get_name(ctx, buf, 16, 0);
return 0;
}
SEC("cgroup/sysctl")
__success
__caps_unpriv(CAP_BPF | CAP_NET_ADMIN)

View File

@ -17,4 +17,92 @@ int tc_uninit_mtu(struct __sk_buff *ctx)
return TCX_PASS;
}
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__uint(max_entries, 1);
__uint(map_flags, BPF_F_WRONLY_PROG);
__type(key, __u32);
__type(value, __u32);
} map_mtu_wo SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__uint(max_entries, 1);
__type(key, __u32);
__type(value, __u32);
} map_mtu_rw SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__uint(max_entries, 1);
__uint(map_flags, BPF_F_RDONLY_PROG);
__type(key, __u32);
__type(value, __u32);
} map_mtu_ro SEC(".maps");
SEC("tc/ingress")
__failure __msg("read from map forbidden")
int tc_writeonly_mtu(struct __sk_buff *ctx)
{
__u32 key = 0;
__u32 *mtu;
mtu = bpf_map_lookup_elem(&map_mtu_wo, &key);
if (mtu)
bpf_check_mtu(ctx, 0, mtu, 0, 0);
return TCX_PASS;
}
SEC("tc/ingress")
__success
int tc_readwrite_mtu(struct __sk_buff *ctx)
{
__u32 key = 0;
__u32 *mtu;
mtu = bpf_map_lookup_elem(&map_mtu_rw, &key);
if (mtu)
bpf_check_mtu(ctx, 0, mtu, 0, 0);
return TCX_PASS;
}
SEC("xdp")
__failure __msg("read from map forbidden")
int xdp_writeonly_mtu(struct xdp_md *ctx)
{
__u32 key = 0;
__u32 *mtu;
mtu = bpf_map_lookup_elem(&map_mtu_wo, &key);
if (mtu)
bpf_check_mtu(ctx, 0, mtu, 0, 0);
return XDP_PASS;
}
SEC("tc/ingress")
__failure __msg("write into map forbidden")
int tc_readonly_mtu(struct __sk_buff *ctx)
{
__u32 key = 0;
__u32 *mtu;
mtu = bpf_map_lookup_elem(&map_mtu_ro, &key);
if (mtu)
bpf_check_mtu(ctx, 0, mtu, 0, 0);
return TCX_PASS;
}
SEC("tc/ingress")
__success
int tc_writeonly_output(struct __sk_buff *ctx)
{
__u32 key = 0;
__u32 *out;
out = bpf_map_lookup_elem(&map_mtu_wo, &key);
if (out)
bpf_get_current_comm(out, sizeof(*out));
return TCX_PASS;
}
char LICENSE[] SEC("license") = "GPL";