mirror of
https://github.com/torvalds/linux.git
synced 2026-05-28 17:13:52 +02:00
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:
parent
6877cd392b
commit
0e74eb4d57
|
|
@ -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},
|
||||
|
|
|
|||
|
|
@ -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'")
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user