Add verifier tests for skb dynptr writer kfuncs that must invalidate
checked direct packet pointers. Cover bpf_dynptr_memset(),
bpf_dynptr_copy() when the skb dynptr is the destination, and probe-read
into an skb dynptr.

Keep a source-only bpf_dynptr_copy() case as a positive control, since
copying from an skb dynptr into memory does not mutate packet data.

Add global subprogram regressions for stale caller packet pointers after
a dynptr writer call and stale packet pointer use inside a global
subprogram whose dynptr argument may be skb-backed.

Signed-off-by: Yiyang Chen <[email protected]>
---
 .../testing/selftests/bpf/progs/dynptr_fail.c | 140 ++++++++++++++++++
 1 file changed, 140 insertions(+)

diff --git a/tools/testing/selftests/bpf/progs/dynptr_fail.c 
b/tools/testing/selftests/bpf/progs/dynptr_fail.c
index 344fb2aa0813d..d79cef5342d67 100644
--- a/tools/testing/selftests/bpf/progs/dynptr_fail.c
+++ b/tools/testing/selftests/bpf/progs/dynptr_fail.c
@@ -1274,6 +1274,146 @@ int skb_invalid_data_slice4(struct __sk_buff *skb)
        return SK_PASS;
 }
 
+char dynptr_kfunc_data[8] = "test";
+char dynptr_kfunc_dst[8];
+
+extern int bpf_dynptr_copy(const struct bpf_dynptr *dst, __u64 dst_off,
+                          const struct bpf_dynptr *src, __u64 src_off,
+                          __u64 size) __ksym __weak;
+extern int bpf_dynptr_memset(const struct bpf_dynptr *ptr, __u64 offset,
+                            __u64 size, __u8 val) __ksym __weak;
+extern int bpf_probe_read_kernel_dynptr(const struct bpf_dynptr *dptr,
+                                       __u64 off, __u64 size,
+                                       const void *unsafe_ptr__ign) __ksym 
__weak;
+
+__noinline int global_dynptr_kfunc_memset(struct bpf_dynptr *ptr)
+{
+       return bpf_dynptr_memset(ptr, 0, 1, 0);
+}
+
+__noinline int global_dynptr_kfunc_memset_and_read(struct __sk_buff *skb,
+                                                  struct bpf_dynptr *ptr)
+{
+       __u8 *data = (void *)(long)skb->data;
+       __u8 *data_end = (void *)(long)skb->data_end;
+
+       if (data + 1 > data_end)
+               return SK_DROP;
+
+       bpf_dynptr_memset(ptr, 0, 1, 0);
+
+       /* this should fail */
+       return *data;
+}
+
+/* Direct packet pointers are invalidated after a dynptr kfunc writes to an 
skb */
+SEC("?tc")
+__failure __msg("invalid mem access 'scalar'")
+int skb_pkt_ptr_invalid_after_dynptr_memset(struct __sk_buff *skb)
+{
+       __u8 *data = (void *)(long)skb->data;
+       __u8 *data_end = (void *)(long)skb->data_end;
+       struct bpf_dynptr ptr;
+
+       if (data + 1 > data_end)
+               return SK_DROP;
+
+       bpf_dynptr_from_skb(skb, 0, &ptr);
+       bpf_dynptr_memset(&ptr, 0, 1, 0);
+
+       /* this should fail */
+       return *data;
+}
+
+/* Global subprogs with dynptr writer kfuncs invalidate caller packet pointers 
*/
+SEC("?tc")
+__failure __msg("invalid mem access 'scalar'")
+int skb_pkt_ptr_invalid_after_global_dynptr_memset(struct __sk_buff *skb)
+{
+       __u8 *data = (void *)(long)skb->data;
+       __u8 *data_end = (void *)(long)skb->data_end;
+       struct bpf_dynptr ptr;
+
+       if (data + 1 > data_end)
+               return SK_DROP;
+
+       bpf_dynptr_from_skb(skb, 0, &ptr);
+       global_dynptr_kfunc_memset(&ptr);
+
+       /* this should fail */
+       return *data;
+}
+
+/* Global subprog dynptr args may be packet-backed and must invalidate locally 
*/
+SEC("?tc")
+__failure __msg("invalid mem access 'scalar'")
+int skb_pkt_ptr_invalid_inside_global_dynptr_memset(struct __sk_buff *skb)
+{
+       struct bpf_dynptr ptr;
+
+       bpf_dynptr_from_skb(skb, 0, &ptr);
+
+       return global_dynptr_kfunc_memset_and_read(skb, &ptr);
+}
+
+/* Direct packet pointers are invalidated after bpf_dynptr_copy() writes to an 
skb */
+SEC("?tc")
+__failure __msg("invalid mem access 'scalar'")
+int skb_pkt_ptr_invalid_after_dynptr_copy_dst(struct __sk_buff *skb)
+{
+       __u8 *data = (void *)(long)skb->data;
+       __u8 *data_end = (void *)(long)skb->data_end;
+       struct bpf_dynptr dst, src;
+
+       if (data + 1 > data_end)
+               return SK_DROP;
+
+       bpf_dynptr_from_skb(skb, 0, &dst);
+       bpf_dynptr_from_mem(dynptr_kfunc_data, sizeof(dynptr_kfunc_data), 0, 
&src);
+       bpf_dynptr_copy(&dst, 0, &src, 0, 1);
+
+       /* this should fail */
+       return *data;
+}
+
+/* Direct packet pointers stay valid when an skb dynptr is only copied from */
+SEC("?tc")
+__success
+int skb_pkt_ptr_valid_after_dynptr_copy_src(struct __sk_buff *skb)
+{
+       __u8 *data = (void *)(long)skb->data;
+       __u8 *data_end = (void *)(long)skb->data_end;
+       struct bpf_dynptr dst, src;
+
+       if (data + 1 > data_end)
+               return SK_DROP;
+
+       bpf_dynptr_from_skb(skb, 0, &src);
+       bpf_dynptr_from_mem(dynptr_kfunc_dst, sizeof(dynptr_kfunc_dst), 0, 
&dst);
+       bpf_dynptr_copy(&dst, 0, &src, 0, 1);
+
+       return *data;
+}
+
+/* Direct packet pointers are invalidated after probe-read writes to an skb 
dynptr */
+SEC("?tc")
+__failure __msg("invalid mem access 'scalar'")
+int skb_pkt_ptr_invalid_after_probe_read_kernel_dynptr(struct __sk_buff *skb)
+{
+       __u8 *data = (void *)(long)skb->data;
+       __u8 *data_end = (void *)(long)skb->data_end;
+       struct bpf_dynptr ptr;
+
+       if (data + 1 > data_end)
+               return SK_DROP;
+
+       bpf_dynptr_from_skb(skb, 0, &ptr);
+       bpf_probe_read_kernel_dynptr(&ptr, 0, 1, dynptr_kfunc_data);
+
+       /* this should fail */
+       return *data;
+}
+
 /* Read-only skb data slice is invalidated on write to skb metadata */
 SEC("?tc")
 __failure __msg("invalid mem access 'scalar'")
-- 
2.34.1


Reply via email to