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

