]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
selftests/bpf: Cover verifier checks for skb_meta dynptr type
authorJakub Sitnicki <jakub@cloudflare.com>
Thu, 14 Aug 2025 09:59:29 +0000 (11:59 +0200)
committerMartin KaFai Lau <martin.lau@kernel.org>
Mon, 18 Aug 2025 17:29:42 +0000 (10:29 -0700)
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
tools/testing/selftests/bpf/prog_tests/dynptr.c
tools/testing/selftests/bpf/progs/dynptr_fail.c
tools/testing/selftests/bpf/progs/dynptr_success.c

index 9b2d9ceda2102392b432b2fac658a94fd050fd80..b9f86cb91e81b578cb201754516e926dee60eea1 100644 (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},
index bd8f15229f5c736abe34187af2e6b450d6fa4b7c..dda6a8dada826cc5244398af241188b6edecb3d3 100644 (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'")
index 8315273cb900c249c0e284b0aa47c481bdd22827..127dea342e5a67dda33e0a39e84d135206d2f3f1 100644 (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)
 {