Re: [PATCH bpf-next v4 12/12] selftests/bpf: Add dynptr helper tests

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, Jan 19, 2023 at 11:04 PM Kumar Kartikeya Dwivedi
<memxor@xxxxxxxxx> wrote:
>
> First test that we allow overwriting dynptr slots and reinitializing
> them in unreferenced case, and disallow overwriting for referenced case.
> Include tests to ensure slices obtained from destroyed dynptrs are being
> invalidated on their destruction. The destruction needs to be scoped, as
> in slices of dynptr A should not be invalidated when dynptr B is
> destroyed. Next, test that MEM_UNINIT doesn't allow writing dynptr stack
> slots.
>
> Signed-off-by: Kumar Kartikeya Dwivedi <memxor@xxxxxxxxx>

Acked-by: Joanne Koong <joannelkoong@xxxxxxxxx>

> ---
>  .../testing/selftests/bpf/progs/dynptr_fail.c | 129 ++++++++++++++++++
>  1 file changed, 129 insertions(+)
>
> diff --git a/tools/testing/selftests/bpf/progs/dynptr_fail.c b/tools/testing/selftests/bpf/progs/dynptr_fail.c
> index 1cbec5468879..c10abb98e47d 100644
> --- a/tools/testing/selftests/bpf/progs/dynptr_fail.c
> +++ b/tools/testing/selftests/bpf/progs/dynptr_fail.c
> @@ -900,3 +900,132 @@ int dynptr_partial_slot_invalidate(struct __sk_buff *ctx)
>         );
>         return 0;
>  }
> +
> +SEC("?raw_tp")
> +__success
> +int dynptr_overwrite_unref(void *ctx)
> +{
> +       struct bpf_dynptr ptr;
> +
> +       if (get_map_val_dynptr(&ptr))
> +               return 0;
> +       if (get_map_val_dynptr(&ptr))
> +               return 0;
> +       if (get_map_val_dynptr(&ptr))
> +               return 0;
> +
> +       return 0;
> +}
> +
> +SEC("?raw_tp")
> +__failure __msg("R1 type=scalar expected=percpu_ptr_")
> +int dynptr_invalidate_slice_or_null(void *ctx)
> +{
> +       struct bpf_dynptr ptr;
> +       __u8 *p;
> +
> +       if (get_map_val_dynptr(&ptr))
> +               return 0;
> +
> +       p = bpf_dynptr_data(&ptr, 0, 1);
> +       *(__u8 *)&ptr = 0;
> +       bpf_this_cpu_ptr(p);
> +       return 0;
> +}
> +

nit: do you mind adding in a comment ("/* this should fail */") above
the line that triggers the verifier error to the new tests?


> +SEC("?raw_tp")
> +__failure __msg("R7 invalid mem access 'scalar'")
> +int dynptr_invalidate_slice_failure(void *ctx)
> +{
> +       struct bpf_dynptr ptr1;
> +       struct bpf_dynptr ptr2;
> +       __u8 *p1, *p2;
> +
> +       if (get_map_val_dynptr(&ptr1))
> +               return 0;
> +       if (get_map_val_dynptr(&ptr2))
> +               return 0;
> +
> +       p1 = bpf_dynptr_data(&ptr1, 0, 1);
> +       if (!p1)
> +               return 0;
> +       p2 = bpf_dynptr_data(&ptr2, 0, 1);
> +       if (!p2)
> +               return 0;
> +
> +       *(__u8 *)&ptr1 = 0;
> +       return *p1;
> +}
> +
> +SEC("?raw_tp")
> +__success
> +int dynptr_invalidate_slice_success(void *ctx)
> +{
> +       struct bpf_dynptr ptr1;
> +       struct bpf_dynptr ptr2;
> +       __u8 *p1, *p2;
> +
> +       if (get_map_val_dynptr(&ptr1))
> +               return 1;
> +       if (get_map_val_dynptr(&ptr2))
> +               return 1;
> +
> +       p1 = bpf_dynptr_data(&ptr1, 0, 1);
> +       if (!p1)
> +               return 1;
> +       p2 = bpf_dynptr_data(&ptr2, 0, 1);
> +       if (!p2)
> +               return 1;
> +
> +       *(__u8 *)&ptr1 = 0;
> +       return *p2;
> +}
> +
> +SEC("?raw_tp")
> +__failure __msg("cannot overwrite referenced dynptr")
> +int dynptr_overwrite_ref(void *ctx)
> +{
> +       struct bpf_dynptr ptr;
> +
> +       bpf_ringbuf_reserve_dynptr(&ringbuf, 64, 0, &ptr);
> +       if (get_map_val_dynptr(&ptr))
> +               bpf_ringbuf_discard_dynptr(&ptr, 0);
> +       return 0;
> +}
> +
> +/* Reject writes to dynptr slot from bpf_dynptr_read */
> +SEC("?raw_tp")
> +__failure __msg("potential write to dynptr at off=-16")
> +int dynptr_read_into_slot(void *ctx)
> +{
> +       union {
> +               struct {
> +                       char _pad[48];
> +                       struct bpf_dynptr ptr;
> +               };
> +               char buf[64];
> +       } data;
> +
> +       bpf_ringbuf_reserve_dynptr(&ringbuf, 64, 0, &data.ptr);
> +       /* this should fail */
> +       bpf_dynptr_read(data.buf, sizeof(data.buf), &data.ptr, 0, 0);
> +
> +       return 0;
> +}
> +
> +/* Reject writes to dynptr slot for uninit arg */
> +SEC("?raw_tp")
> +__failure __msg("potential write to dynptr at off=-16")
> +int uninit_write_into_slot(void *ctx)
> +{
> +       struct {
> +               char buf[64];
> +               struct bpf_dynptr ptr;
> +       } data;
> +
> +       bpf_ringbuf_reserve_dynptr(&ringbuf, 80, 0, &data.ptr);
> +       /* this should fail */
> +       bpf_get_current_comm(data.buf, 80);
> +
> +       return 0;
> +}

Another test I think would be helpful is verifying that data slices
are invalidated if the dynptr is invalidated within a callback.
Something like:

static int callback(__u32 index, void *data)
{
        *(__u32 *)data = 123;

        return 0;
}

/* If the dynptr is written into in a callback function, its data
slices should be invalidated as well */
SEC("?raw_tp")
__failure __msg("invalid mem access 'scalar'")
int invalid_data_slices(void *ctx)
{
        struct bpf_dynptr ptr;
        __u32 *slice;

        get_map_val_dynptr(&ptr);

        slice  = bpf_dynptr_data(&ptr, 0, sizeof(_u32));
        if (!slice)
                return 0;

        bpf_loop(10, callback, &ptr, 0);

        /* this should fail */
        *slice = 1;

        return 0;
}

> --
> 2.39.1
>



[Index of Archives]     [Linux Samsung SoC]     [Linux Rockchip SoC]     [Linux Actions SoC]     [Linux for Synopsys ARC Processors]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]


  Powered by Linux