[PATCH bpf 3/3] selftests/bpf: Test overlapping dynptr copies on xdp frags
From: Ayushman Rout
Date: Sat Oct 24 2026 - 05:36:06 EST
Add four dynptr tests. Three use an xdp packet with frags, and in each
the range goes from the linear part into frag0:
- test_dynptr_copy_xdp_overlap: bpf_dynptr_copy() of 1200 bytes with
the destination 100 bytes above the source.
- test_dynptr_read_xdp_overlap: bpf_dynptr_read() into a slice of the
same packet.
- test_dynptr_write_xdp_overlap: bpf_dynptr_write() from a slice of the
same packet.
The fourth, test_dynptr_skb_load_overlap, reads a range that spans the
head and the frags of an skb. It passes with and without the fixes and
checks that skbs are not affected.
The new tests set err when the data is wrong. The existing
test_dynptr_copy_xdp does not, so it can pass on corrupted data.
Without the fixes to bpf_xdp_copy_buf() and bpf_dynptr_copy(), the three
xdp tests fail. With them, all four tests pass.
Signed-off-by: Ayushman Rout <ayushmanrout27@xxxxxxxxx>
Assisted-by: LLM
---
.../testing/selftests/bpf/prog_tests/dynptr.c | 4 +
.../selftests/bpf/progs/dynptr_success.c | 128 ++++++++++++++++++
2 files changed, 132 insertions(+)
diff --git a/tools/testing/selftests/bpf/prog_tests/dynptr.c b/tools/testing/selftests/bpf/prog_tests/dynptr.c
index 4396560365e8..08382ed076a2 100644
--- a/tools/testing/selftests/bpf/prog_tests/dynptr.c
+++ b/tools/testing/selftests/bpf/prog_tests/dynptr.c
@@ -22,6 +22,10 @@ static struct {
{"test_dynptr_data", SETUP_SYSCALL_SLEEP},
{"test_dynptr_copy", SETUP_SYSCALL_SLEEP},
{"test_dynptr_copy_xdp", SETUP_XDP_PROG},
+ {"test_dynptr_copy_xdp_overlap", SETUP_XDP_PROG},
+ {"test_dynptr_read_xdp_overlap", SETUP_XDP_PROG},
+ {"test_dynptr_write_xdp_overlap", SETUP_XDP_PROG},
+ {"test_dynptr_skb_load_overlap", SETUP_SKB_PROG_NONLINEAR},
{"test_dynptr_memset_zero", SETUP_SYSCALL_SLEEP},
{"test_dynptr_memset_notzero", SETUP_SYSCALL_SLEEP},
{"test_dynptr_memset_zero_offset", SETUP_SYSCALL_SLEEP},
diff --git a/tools/testing/selftests/bpf/progs/dynptr_success.c b/tools/testing/selftests/bpf/progs/dynptr_success.c
index b668ebd61fc7..b6c3abb66e4b 100644
--- a/tools/testing/selftests/bpf/progs/dynptr_success.c
+++ b/tools/testing/selftests/bpf/progs/dynptr_success.c
@@ -759,6 +759,134 @@ int test_dynptr_copy_xdp(struct xdp_md *xdp)
return XDP_DROP;
}
+static __always_inline int fill_xdp(struct bpf_dynptr *ptr, __u32 start, __u32 n)
+{
+ __u8 b;
+ int i;
+
+ bpf_for(i, 0, n) {
+ b = (start + i) % 251;
+ if (bpf_dynptr_write(ptr, start + i, &b, 1, 0))
+ return 1;
+ }
+ return 0;
+}
+
+static __always_inline int check_xdp(struct bpf_dynptr *ptr, __u32 at, __u32 n, __u32 from)
+{
+ __u8 b;
+ int i;
+
+ bpf_for(i, 0, n) {
+ if (bpf_dynptr_read(&b, 1, ptr, at + i, 0))
+ return 1;
+ if (b != (__u8)((from + i) % 251))
+ return 1;
+ }
+ return 0;
+}
+
+SEC("?xdp")
+int test_dynptr_copy_xdp_overlap(struct xdp_md *xdp)
+{
+ void *data_end = (void *)(long)xdp->data_end;
+ void *data = (void *)(long)xdp->data;
+ __u32 off = data_end - data - 1000;
+ struct bpf_dynptr ptr;
+
+ bpf_dynptr_from_xdp(xdp, 0, &ptr);
+ if (fill_xdp(&ptr, off, 1300)) {
+ err = 1;
+ return XDP_DROP;
+ }
+ err = bpf_dynptr_copy(&ptr, off + 100, &ptr, off, 1200);
+ if (err)
+ return XDP_DROP;
+ if (check_xdp(&ptr, off + 100, 1200, off))
+ err = 2;
+ return XDP_DROP;
+}
+
+SEC("?xdp")
+int test_dynptr_read_xdp_overlap(struct xdp_md *xdp)
+{
+ void *data_end = (void *)(long)xdp->data_end;
+ void *data = (void *)(long)xdp->data;
+ __u32 lin = data_end - data;
+ struct bpf_dynptr ptr;
+ char *dst;
+
+ bpf_dynptr_from_xdp(xdp, 0, &ptr);
+ if (fill_xdp(&ptr, lin - 10, 84)) {
+ err = 1;
+ return XDP_DROP;
+ }
+ dst = bpf_dynptr_slice_rdwr(&ptr, lin + 10, NULL, 64);
+ if (!dst) {
+ err = 2;
+ return XDP_DROP;
+ }
+ err = bpf_dynptr_read(dst, 64, &ptr, lin - 10, 0);
+ if (err)
+ return XDP_DROP;
+ if (check_xdp(&ptr, lin + 10, 64, lin - 10))
+ err = 3;
+ return XDP_DROP;
+}
+
+SEC("?xdp")
+int test_dynptr_write_xdp_overlap(struct xdp_md *xdp)
+{
+ void *data_end = (void *)(long)xdp->data_end;
+ void *data = (void *)(long)xdp->data;
+ __u32 lin = data_end - data;
+ struct bpf_dynptr ptr;
+ char *src;
+
+ bpf_dynptr_from_xdp(xdp, 0, &ptr);
+ if (fill_xdp(&ptr, lin - 64, 118)) {
+ err = 1;
+ return XDP_DROP;
+ }
+ src = bpf_dynptr_slice_rdwr(&ptr, lin - 64, NULL, 64);
+ if (!src) {
+ err = 2;
+ return XDP_DROP;
+ }
+ err = bpf_dynptr_write(&ptr, lin - 10, src, 64, 0);
+ if (err)
+ return XDP_DROP;
+ if (check_xdp(&ptr, lin - 10, 64, lin - 64))
+ err = 3;
+ return XDP_DROP;
+}
+
+SEC("?tc")
+int test_dynptr_skb_load_overlap(struct __sk_buff *skb)
+{
+ struct bpf_dynptr ptr;
+ char ref[24];
+ char *dst;
+
+ bpf_dynptr_from_skb(skb, 0, &ptr);
+ if (bpf_dynptr_read(ref, 24, &ptr, 20, 0)) {
+ err = 1;
+ return 0;
+ }
+ dst = bpf_dynptr_slice_rdwr(&ptr, 10, NULL, 24);
+ if (!dst) {
+ err = 2;
+ return 0;
+ }
+ if (bpf_dynptr_read(dst, 24, &ptr, 20, 0)) {
+ err = 3;
+ return 0;
+ }
+ if (bpf_memcmp(dst, ref, 24))
+ err = 4;
+ return 0;
+}
+
char memset_zero_data[] = "data to be zeroed";
SEC("?tp/syscalls/sys_enter_nanosleep")
--
2.54.0