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 Sat, Jan 21, 2023 at 04:44:42AM IST, Joanne Koong wrote:
> 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?
>

I've added this comment to whichever statement triggers the error, and a
short comment over the 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;
> }

Yes, looks good. I added this and one more test which tests the unint ->
check_mem_access -> destroy path as well, and will respin.



[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