diff mbox series

[1/3] bpf: verifier: Accept dynptr mem as mem in helpers

Message ID 20230406004018.1439952-2-drosen@google.com (mailing list archive)
State Accepted
Commit 2012c867c8005d72c949e274133df429ece78808
Headers show
Series Dynptr Verifier Adjustments | expand

Commit Message

Daniel Rosenberg April 6, 2023, 12:40 a.m. UTC
This allows using memory retrieved from dynptrs with helper functions
that accept ARG_PTR_TO_MEM. For instance, results from bpf_dynptr_data
can be passed along to bpf_strncmp.

Signed-off-by: Daniel Rosenberg <drosen@google.com>
---
 kernel/bpf/verifier.c | 4 ++++
 1 file changed, 4 insertions(+)

Comments

Andrii Nakryiko April 6, 2023, 8:55 p.m. UTC | #1
On Wed, Apr 5, 2023 at 5:40 PM Daniel Rosenberg <drosen@google.com> wrote:
>
> This allows using memory retrieved from dynptrs with helper functions
> that accept ARG_PTR_TO_MEM. For instance, results from bpf_dynptr_data
> can be passed along to bpf_strncmp.
>
> Signed-off-by: Daniel Rosenberg <drosen@google.com>
> ---

I think patches like this should be targeted against bpf-next, so for
next revision please send them against bpf-next tree.

>  kernel/bpf/verifier.c | 4 ++++
>  1 file changed, 4 insertions(+)
>
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 56f569811f70..20beab52812a 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -7164,12 +7164,16 @@ static int check_reg_type(struct bpf_verifier_env *env, u32 regno,
>          * ARG_PTR_TO_MEM + MAYBE_NULL is compatible with PTR_TO_MEM and PTR_TO_MEM + MAYBE_NULL,
>          * but ARG_PTR_TO_MEM is compatible only with PTR_TO_MEM but NOT with PTR_TO_MEM + MAYBE_NULL
>          *
> +        * ARG_PTR_TO_MEM is compatible with PTR_TO_MEM that is tagged with a dynptr type.
> +        *
>          * Therefore we fold these flags depending on the arg_type before comparison.
>          */
>         if (arg_type & MEM_RDONLY)
>                 type &= ~MEM_RDONLY;
>         if (arg_type & PTR_MAYBE_NULL)
>                 type &= ~PTR_MAYBE_NULL;
> +       if (base_type(arg_type) == ARG_PTR_TO_MEM)
> +               type &= ~DYNPTR_TYPE_FLAG_MASK;

Something feels off here. Can you paste a bit of verifier log for the
failure you were getting. And let's have a selftest for this situation
as well.

ARG_PTR_TO_MEM shouldn't be qualified with the DYNPTR_TYPE flag, it's
just memory, there is no need to know what type of dynptr it was
derived from. So if that happens, the problem is somewhere else. Let's
root cause and fix that. Having a selftest that demonstrates the
problem will help with that.


>
>         if (meta->func_id == BPF_FUNC_kptr_xchg && type & MEM_ALLOC)
>                 type &= ~MEM_ALLOC;
> --
> 2.40.0.577.gac1e443424-goog
>
Alexei Starovoitov April 6, 2023, 10:13 p.m. UTC | #2
On Thu, Apr 6, 2023 at 1:55 PM Andrii Nakryiko
<andrii.nakryiko@gmail.com> wrote:
>
> On Wed, Apr 5, 2023 at 5:40 PM Daniel Rosenberg <drosen@google.com> wrote:
> >
> > This allows using memory retrieved from dynptrs with helper functions
> > that accept ARG_PTR_TO_MEM. For instance, results from bpf_dynptr_data
> > can be passed along to bpf_strncmp.
> >
> > Signed-off-by: Daniel Rosenberg <drosen@google.com>
> > ---
>
> I think patches like this should be targeted against bpf-next, so for
> next revision please send them against bpf-next tree.
>
> >  kernel/bpf/verifier.c | 4 ++++
> >  1 file changed, 4 insertions(+)
> >
> > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> > index 56f569811f70..20beab52812a 100644
> > --- a/kernel/bpf/verifier.c
> > +++ b/kernel/bpf/verifier.c
> > @@ -7164,12 +7164,16 @@ static int check_reg_type(struct bpf_verifier_env *env, u32 regno,
> >          * ARG_PTR_TO_MEM + MAYBE_NULL is compatible with PTR_TO_MEM and PTR_TO_MEM + MAYBE_NULL,
> >          * but ARG_PTR_TO_MEM is compatible only with PTR_TO_MEM but NOT with PTR_TO_MEM + MAYBE_NULL
> >          *
> > +        * ARG_PTR_TO_MEM is compatible with PTR_TO_MEM that is tagged with a dynptr type.
> > +        *
> >          * Therefore we fold these flags depending on the arg_type before comparison.
> >          */
> >         if (arg_type & MEM_RDONLY)
> >                 type &= ~MEM_RDONLY;
> >         if (arg_type & PTR_MAYBE_NULL)
> >                 type &= ~PTR_MAYBE_NULL;
> > +       if (base_type(arg_type) == ARG_PTR_TO_MEM)
> > +               type &= ~DYNPTR_TYPE_FLAG_MASK;
>
> Something feels off here. Can you paste a bit of verifier log for the
> failure you were getting. And let's have a selftest for this situation
> as well.
>
> ARG_PTR_TO_MEM shouldn't be qualified with the DYNPTR_TYPE flag, it's
> just memory, there is no need to know what type of dynptr it was
> derived from. So if that happens, the problem is somewhere else. Let's
> root cause and fix that. Having a selftest that demonstrates the
> problem will help with that.

+1
All of the DYNPTR_TYPE_FLAG_MASK flags cannot appear in type == reg->type
here.
They are either dynamic flags inside bpf_dynptr_kern->size
or in arg_type.
Like in bpf_dynptr_from_mem_proto.
Daniel Rosenberg April 6, 2023, 10:35 p.m. UTC | #3
On Thu, Apr 6, 2023 at 1:55 PM Andrii Nakryiko
<andrii.nakryiko@gmail.com> wrote:
>
> Something feels off here. Can you paste a bit of verifier log for the
> failure you were getting. And let's have a selftest for this situation
> as well.
>
> ARG_PTR_TO_MEM shouldn't be qualified with the DYNPTR_TYPE flag, it's
> just memory, there is no need to know what type of dynptr it was
> derived from. So if that happens, the problem is somewhere else. Let's
> root cause and fix that. Having a selftest that demonstrates the
> problem will help with that.
>
>
This label is added by dynptr_slice(_rdwr)

if (meta.func_id == special_kfunc_list[KF_bpf_dynptr_slice] ||
   meta.func_id == special_kfunc_list[KF_bpf_dynptr_slice_rdwr]) {
enum bpf_type_flag type_flag =
get_dynptr_type_flag(meta.initialized_dynptr.type);
...
regs[BPF_REG_0].type = PTR_TO_MEM | type_flag;

That extra flag was causing the type to be unexpected later on.
I'll add a selftest for this as well.
Daniel Rosenberg May 2, 2023, 1:12 a.m. UTC | #4
On Thu, Apr 6, 2023 at 3:13 PM Alexei Starovoitov
<alexei.starovoitov@gmail.com> wrote:
>
> +1
> All of the DYNPTR_TYPE_FLAG_MASK flags cannot appear in type == reg->type
> here.
> They are either dynamic flags inside bpf_dynptr_kern->size
> or in arg_type.
> Like in bpf_dynptr_from_mem_proto.

Looking at this a bit more, I believe this is to enforce packet
restrictions for DYNPTR_TYPE_SKB and DYNPTR_TYPE_XDP. When a helper
function alters a packet, dynptr slices of it are invalidated. If I
remove that annotation entirely, then the invalid_data_slice family of
tests fail. bpf_dynptr_from_mem_proto is fine since that's just local
dynptrs, which don't have any extra limitations.
Andrii Nakryiko May 3, 2023, 6:39 p.m. UTC | #5
On Mon, May 1, 2023 at 6:12 PM Daniel Rosenberg <drosen@google.com> wrote:
>
> On Thu, Apr 6, 2023 at 3:13 PM Alexei Starovoitov
> <alexei.starovoitov@gmail.com> wrote:
> >
> > +1
> > All of the DYNPTR_TYPE_FLAG_MASK flags cannot appear in type == reg->type
> > here.
> > They are either dynamic flags inside bpf_dynptr_kern->size
> > or in arg_type.
> > Like in bpf_dynptr_from_mem_proto.
>
> Looking at this a bit more, I believe this is to enforce packet
> restrictions for DYNPTR_TYPE_SKB and DYNPTR_TYPE_XDP. When a helper
> function alters a packet, dynptr slices of it are invalidated. If I
> remove that annotation entirely, then the invalid_data_slice family of
> tests fail. bpf_dynptr_from_mem_proto is fine since that's just local
> dynptrs, which don't have any extra limitations.


Ah, ok, thanks for investigating!
diff mbox series

Patch

diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 56f569811f70..20beab52812a 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -7164,12 +7164,16 @@  static int check_reg_type(struct bpf_verifier_env *env, u32 regno,
 	 * ARG_PTR_TO_MEM + MAYBE_NULL is compatible with PTR_TO_MEM and PTR_TO_MEM + MAYBE_NULL,
 	 * but ARG_PTR_TO_MEM is compatible only with PTR_TO_MEM but NOT with PTR_TO_MEM + MAYBE_NULL
 	 *
+	 * ARG_PTR_TO_MEM is compatible with PTR_TO_MEM that is tagged with a dynptr type.
+	 *
 	 * Therefore we fold these flags depending on the arg_type before comparison.
 	 */
 	if (arg_type & MEM_RDONLY)
 		type &= ~MEM_RDONLY;
 	if (arg_type & PTR_MAYBE_NULL)
 		type &= ~PTR_MAYBE_NULL;
+	if (base_type(arg_type) == ARG_PTR_TO_MEM)
+		type &= ~DYNPTR_TYPE_FLAG_MASK;
 
 	if (meta->func_id == BPF_FUNC_kptr_xchg && type & MEM_ALLOC)
 		type &= ~MEM_ALLOC;