selftests/bpf: Cover verifier checks for skb_meta dynptr type

dynptr for skb metadata behaves the same way as the dynptr for skb data
with one exception - writes to skb_meta dynptr don't invalidate existing
skb and skb_meta slices.

Duplicate those the skb dynptr tests which we can, since
bpf_dynptr_from_skb_meta kfunc can be called only from TC BPF, to cover the
skb_meta dynptr verifier checks.

Also add a couple of new tests (skb_data_valid_*) to ensure we don't
invalidate the slices in the mentioned case, which are specific to skb_meta
dynptr.

Signed-off-by: Jakub Sitnicki <jakub@cloudflare.com>
Signed-off-by: Martin KaFai Lau <martin.lau@kernel.org>
Reviewed-by: Jesse Brandeburg <jbrandeburg@cloudflare.com>
Link: https://patch.msgid.link/20250814-skb-metadata-thru-dynptr-v7-3-8a39e636e0fb@cloudflare.com
This commit is contained in:
Jakub Sitnicki 2025-08-14 11:59:29 +02:00 committed by Martin KaFai Lau
parent 6877cd392b
commit 0e74eb4d57
3 changed files with 315 additions and 0 deletions

View File

@ -32,6 +32,8 @@ static struct {
{"test_ringbuf", SETUP_SYSCALL_SLEEP},
{"test_skb_readonly", SETUP_SKB_PROG},
{"test_dynptr_skb_data", SETUP_SKB_PROG},
{"test_dynptr_skb_meta_data", SETUP_SKB_PROG},
{"test_dynptr_skb_meta_flags", SETUP_SKB_PROG},
{"test_adjust", SETUP_SYSCALL_SLEEP},
{"test_adjust_err", SETUP_SYSCALL_SLEEP},
{"test_zero_size_dynptr", SETUP_SYSCALL_SLEEP},

View File

@ -269,6 +269,26 @@ int data_slice_out_of_bounds_skb(struct __sk_buff *skb)
return SK_PASS;
}
/* A metadata slice can't be accessed out of bounds */
SEC("?tc")
__failure __msg("value is outside of the allowed memory range")
int data_slice_out_of_bounds_skb_meta(struct __sk_buff *skb)
{
struct bpf_dynptr meta;
__u8 *md;
bpf_dynptr_from_skb_meta(skb, 0, &meta);
md = bpf_dynptr_slice_rdwr(&meta, 0, NULL, sizeof(*md));
if (!md)
return SK_DROP;
/* this should fail */
*(md + 1) = 42;
return SK_PASS;
}
SEC("?raw_tp")
__failure __msg("value is outside of the allowed memory range")
int data_slice_out_of_bounds_map_value(void *ctx)
@ -1089,6 +1109,26 @@ int skb_invalid_slice_write(struct __sk_buff *skb)
return SK_PASS;
}
/* bpf_dynptr_slice()s are read-only and cannot be written to */
SEC("?tc")
__failure __msg("R{{[0-9]+}} cannot write into rdonly_mem")
int skb_meta_invalid_slice_write(struct __sk_buff *skb)
{
struct bpf_dynptr meta;
__u8 *md;
bpf_dynptr_from_skb_meta(skb, 0, &meta);
md = bpf_dynptr_slice(&meta, 0, NULL, sizeof(*md));
if (!md)
return SK_DROP;
/* this should fail */
*md = 42;
return SK_PASS;
}
/* The read-only data slice is invalidated whenever a helper changes packet data */
SEC("?tc")
__failure __msg("invalid mem access 'scalar'")
@ -1192,6 +1232,188 @@ int skb_invalid_data_slice4(struct __sk_buff *skb)
return SK_PASS;
}
/* Read-only skb data slice is invalidated on write to skb metadata */
SEC("?tc")
__failure __msg("invalid mem access 'scalar'")
int ro_skb_slice_invalid_after_metadata_write(struct __sk_buff *skb)
{
struct bpf_dynptr data, meta;
__u8 *d;
bpf_dynptr_from_skb(skb, 0, &data);
bpf_dynptr_from_skb_meta(skb, 0, &meta);
d = bpf_dynptr_slice(&data, 0, NULL, sizeof(*d));
if (!d)
return SK_DROP;
bpf_dynptr_write(&meta, 0, "x", 1, 0);
/* this should fail */
val = *d;
return SK_PASS;
}
/* Read-write skb data slice is invalidated on write to skb metadata */
SEC("?tc")
__failure __msg("invalid mem access 'scalar'")
int rw_skb_slice_invalid_after_metadata_write(struct __sk_buff *skb)
{
struct bpf_dynptr data, meta;
__u8 *d;
bpf_dynptr_from_skb(skb, 0, &data);
bpf_dynptr_from_skb_meta(skb, 0, &meta);
d = bpf_dynptr_slice_rdwr(&data, 0, NULL, sizeof(*d));
if (!d)
return SK_DROP;
bpf_dynptr_write(&meta, 0, "x", 1, 0);
/* this should fail */
*d = 42;
return SK_PASS;
}
/* Read-only skb metadata slice is invalidated on write to skb data */
SEC("?tc")
__failure __msg("invalid mem access 'scalar'")
int ro_skb_meta_slice_invalid_after_payload_write(struct __sk_buff *skb)
{
struct bpf_dynptr data, meta;
__u8 *md;
bpf_dynptr_from_skb(skb, 0, &data);
bpf_dynptr_from_skb_meta(skb, 0, &meta);
md = bpf_dynptr_slice(&meta, 0, NULL, sizeof(*md));
if (!md)
return SK_DROP;
bpf_dynptr_write(&data, 0, "x", 1, 0);
/* this should fail */
val = *md;
return SK_PASS;
}
/* Read-write skb metadata slice is invalidated on write to skb data slice */
SEC("?tc")
__failure __msg("invalid mem access 'scalar'")
int rw_skb_meta_slice_invalid_after_payload_write(struct __sk_buff *skb)
{
struct bpf_dynptr data, meta;
__u8 *md;
bpf_dynptr_from_skb(skb, 0, &data);
bpf_dynptr_from_skb_meta(skb, 0, &meta);
md = bpf_dynptr_slice_rdwr(&meta, 0, NULL, sizeof(*md));
if (!md)
return SK_DROP;
bpf_dynptr_write(&data, 0, "x", 1, 0);
/* this should fail */
*md = 42;
return SK_PASS;
}
/* Read-only skb metadata slice is invalidated whenever a helper changes packet data */
SEC("?tc")
__failure __msg("invalid mem access 'scalar'")
int ro_skb_meta_slice_invalid_after_payload_helper(struct __sk_buff *skb)
{
struct bpf_dynptr meta;
__u8 *md;
bpf_dynptr_from_skb_meta(skb, 0, &meta);
md = bpf_dynptr_slice(&meta, 0, NULL, sizeof(*md));
if (!md)
return SK_DROP;
if (bpf_skb_pull_data(skb, skb->len))
return SK_DROP;
/* this should fail */
val = *md;
return SK_PASS;
}
/* Read-write skb metadata slice is invalidated whenever a helper changes packet data */
SEC("?tc")
__failure __msg("invalid mem access 'scalar'")
int rw_skb_meta_slice_invalid_after_payload_helper(struct __sk_buff *skb)
{
struct bpf_dynptr meta;
__u8 *md;
bpf_dynptr_from_skb_meta(skb, 0, &meta);
md = bpf_dynptr_slice_rdwr(&meta, 0, NULL, sizeof(*md));
if (!md)
return SK_DROP;
if (bpf_skb_pull_data(skb, skb->len))
return SK_DROP;
/* this should fail */
*md = 42;
return SK_PASS;
}
/* Read-only skb metadata slice is invalidated on write to skb metadata */
SEC("?tc")
__failure __msg("invalid mem access 'scalar'")
int ro_skb_meta_slice_invalid_after_metadata_write(struct __sk_buff *skb)
{
struct bpf_dynptr meta;
__u8 *md;
bpf_dynptr_from_skb_meta(skb, 0, &meta);
md = bpf_dynptr_slice(&meta, 0, NULL, sizeof(*md));
if (!md)
return SK_DROP;
bpf_dynptr_write(&meta, 0, "x", 1, 0);
/* this should fail */
val = *md;
return SK_PASS;
}
/* Read-write skb metadata slice is invalidated on write to skb metadata */
SEC("?tc")
__failure __msg("invalid mem access 'scalar'")
int rw_skb_meta_slice_invalid_after_metadata_write(struct __sk_buff *skb)
{
struct bpf_dynptr meta;
__u8 *md;
bpf_dynptr_from_skb_meta(skb, 0, &meta);
md = bpf_dynptr_slice_rdwr(&meta, 0, NULL, sizeof(*md));
if (!md)
return SK_DROP;
bpf_dynptr_write(&meta, 0, "x", 1, 0);
/* this should fail */
*md = 42;
return SK_PASS;
}
/* The read-only data slice is invalidated whenever a helper changes packet data */
SEC("?xdp")
__failure __msg("invalid mem access 'scalar'")
@ -1255,6 +1477,19 @@ int skb_invalid_ctx(void *ctx)
return 0;
}
/* Only supported prog type can create skb_meta-type dynptrs */
SEC("?raw_tp")
__failure __msg("calling kernel function bpf_dynptr_from_skb_meta is not allowed")
int skb_meta_invalid_ctx(void *ctx)
{
struct bpf_dynptr meta;
/* this should fail */
bpf_dynptr_from_skb_meta(ctx, 0, &meta);
return 0;
}
SEC("fentry/skb_tx_error")
__failure __msg("must be referenced or trusted")
int BPF_PROG(skb_invalid_ctx_fentry, void *skb)
@ -1665,6 +1900,29 @@ int clone_skb_packet_data(struct __sk_buff *skb)
return 0;
}
/* A skb clone's metadata slice becomes invalid anytime packet data changes */
SEC("?tc")
__failure __msg("invalid mem access 'scalar'")
int clone_skb_packet_meta(struct __sk_buff *skb)
{
struct bpf_dynptr clone, meta;
__u8 *md;
bpf_dynptr_from_skb_meta(skb, 0, &meta);
bpf_dynptr_clone(&meta, &clone);
md = bpf_dynptr_slice_rdwr(&clone, 0, NULL, sizeof(*md));
if (!md)
return SK_DROP;
if (bpf_skb_pull_data(skb, skb->len))
return SK_DROP;
/* this should fail */
*md = 42;
return 0;
}
/* A xdp clone's data slices should be invalid anytime packet data changes */
SEC("?xdp")
__failure __msg("invalid mem access 'scalar'")

View File

@ -211,6 +211,61 @@ int test_dynptr_skb_data(struct __sk_buff *skb)
return 1;
}
SEC("?tc")
int test_dynptr_skb_meta_data(struct __sk_buff *skb)
{
struct bpf_dynptr meta;
__u8 *md;
int ret;
err = 1;
ret = bpf_dynptr_from_skb_meta(skb, 0, &meta);
if (ret)
return 1;
/* This should return NULL. Must use bpf_dynptr_slice API */
err = 2;
md = bpf_dynptr_data(&meta, 0, sizeof(*md));
if (md)
return 1;
err = 0;
return 1;
}
/* Check that skb metadata dynptr ops don't accept any flags. */
SEC("?tc")
int test_dynptr_skb_meta_flags(struct __sk_buff *skb)
{
const __u64 INVALID_FLAGS = ~0ULL;
struct bpf_dynptr meta;
__u8 buf;
int ret;
err = 1;
ret = bpf_dynptr_from_skb_meta(skb, INVALID_FLAGS, &meta);
if (ret != -EINVAL)
return 1;
err = 2;
ret = bpf_dynptr_from_skb_meta(skb, 0, &meta);
if (ret)
return 1;
err = 3;
ret = bpf_dynptr_read(&buf, 0, &meta, 0, INVALID_FLAGS);
if (ret != -EINVAL)
return 1;
err = 4;
ret = bpf_dynptr_write(&meta, 0, &buf, 0, INVALID_FLAGS);
if (ret != -EINVAL)
return 1;
err = 0;
return 1;
}
SEC("tp/syscalls/sys_enter_nanosleep")
int test_adjust(void *ctx)
{