[PATCH bpf v2 2/2] selftests/bpf: Test overlapping dynptr copies on xdp frags
From: Ayushman Rout
Date: Mon Oct 26 2026 - 12:39:09 EST
Add dynptr tests for copies within one xdp packet with frags, where the
source and the destination overlap and the destination is above the
source:
- test_dynptr_copy_xdp_overlap: both ranges cross from the linear part
into frag0. bpf_dynptr_copy() of 1200 bytes with the destination 100
bytes above the source.
- test_dynptr_copy_xdp_overlap_src_linear: the source is in the linear
part and the destination crosses into frag0.
- test_dynptr_copy_xdp_overlap_dst_frag: the source crosses into frag0
and the destination is in frag0.
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 fix to bpf_dynptr_copy(), test_dynptr_copy_xdp_overlap
fails. With it, all the new tests pass.
Signed-off-by: Ayushman Rout <ayushmanrout27@xxxxxxxxx>
---
.../testing/selftests/bpf/prog_tests/dynptr.c | 3 +
.../selftests/bpf/progs/dynptr_success.c | 90 +++++++++++++++++++
2 files changed, 93 insertions(+)
diff --git a/tools/testing/selftests/bpf/prog_tests/dynptr.c b/tools/testing/selftests/bpf/prog_tests/dynptr.c
index 4396560365e8..cbb2cc9969be 100644
--- a/tools/testing/selftests/bpf/prog_tests/dynptr.c
+++ b/tools/testing/selftests/bpf/prog_tests/dynptr.c
@@ -22,6 +22,9 @@ 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_copy_xdp_overlap_src_linear", SETUP_XDP_PROG},
+ {"test_dynptr_copy_xdp_overlap_dst_frag", SETUP_XDP_PROG},
{"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..56be4eafc270 100644
--- a/tools/testing/selftests/bpf/progs/dynptr_success.c
+++ b/tools/testing/selftests/bpf/progs/dynptr_success.c
@@ -759,6 +759,96 @@ 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_copy_xdp_overlap_src_linear(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;
+
+ bpf_dynptr_from_xdp(xdp, 0, &ptr);
+ if (fill_xdp(&ptr, lin - 64, 118)) {
+ err = 1;
+ return XDP_DROP;
+ }
+ err = bpf_dynptr_copy(&ptr, lin - 10, &ptr, lin - 64, 64);
+ if (err)
+ return XDP_DROP;
+ if (check_xdp(&ptr, lin - 10, 64, lin - 64))
+ err = 2;
+ return XDP_DROP;
+}
+
+SEC("?xdp")
+int test_dynptr_copy_xdp_overlap_dst_frag(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;
+
+ bpf_dynptr_from_xdp(xdp, 0, &ptr);
+ if (fill_xdp(&ptr, lin - 10, 84)) {
+ err = 1;
+ return XDP_DROP;
+ }
+ err = bpf_dynptr_copy(&ptr, lin + 10, &ptr, lin - 10, 64);
+ if (err)
+ return XDP_DROP;
+ if (check_xdp(&ptr, lin + 10, 64, lin - 10))
+ err = 2;
+ return XDP_DROP;
+}
+
char memset_zero_data[] = "data to be zeroed";
SEC("?tp/syscalls/sys_enter_nanosleep")
--
2.54.0