bpf.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Alexei Starovoitov <alexei.starovoitov@gmail.com>
To: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Cc: "Joanne Koong" <joannelkoong@gmail.com>,
	bpf <bpf@vger.kernel.org>,
	"Martin KaFai Lau" <martin.lau@kernel.org>,
	"Andrii Nakryiko" <andrii@kernel.org>,
	"Alexei Starovoitov" <ast@kernel.org>,
	"Daniel Borkmann" <daniel@iogearbox.net>,
	"Network Development" <netdev@vger.kernel.org>,
	"Toke Høiland-Jørgensen" <toke@kernel.org>
Subject: Re: [PATCH v13 bpf-next 09/10] bpf: Add bpf_dynptr_slice and bpf_dynptr_slice_rdwr
Date: Tue, 7 Mar 2023 07:45:14 -0800	[thread overview]
Message-ID: <CAADnVQ+xOrCSwgxGQXNM5wHfOwV+x0csHfNyDYBHgyGVXgc2Ow@mail.gmail.com> (raw)
In-Reply-To: <20230307102233.bemr47x625ity26z@apollo>

On Tue, Mar 7, 2023 at 2:22 AM Kumar Kartikeya Dwivedi <memxor@gmail.com> wrote:
>
> On Tue, Mar 07, 2023 at 03:23:25AM CET, Alexei Starovoitov wrote:
> > On Sun, Mar 5, 2023 at 11:10 PM Kumar Kartikeya Dwivedi
> > <memxor@gmail.com> wrote:
> > >
> > > On Wed, Mar 01, 2023 at 04:49:52PM CET, Joanne Koong wrote:
> > > > Two new kfuncs are added, bpf_dynptr_slice and bpf_dynptr_slice_rdwr.
> > > > The user must pass in a buffer to store the contents of the data slice
> > > > if a direct pointer to the data cannot be obtained.
> > > >
> > > > For skb and xdp type dynptrs, these two APIs are the only way to obtain
> > > > a data slice. However, for other types of dynptrs, there is no
> > > > difference between bpf_dynptr_slice(_rdwr) and bpf_dynptr_data.
> > > >
> > > > For skb type dynptrs, the data is copied into the user provided buffer
> > > > if any of the data is not in the linear portion of the skb. For xdp type
> > > > dynptrs, the data is copied into the user provided buffer if the data is
> > > > between xdp frags.
> > > >
> > > > If the skb is cloned and a call to bpf_dynptr_data_rdwr is made, then
> > > > the skb will be uncloned (see bpf_unclone_prologue()).
> > > >
> > > > Please note that any bpf_dynptr_write() automatically invalidates any prior
> > > > data slices of the skb dynptr. This is because the skb may be cloned or
> > > > may need to pull its paged buffer into the head. As such, any
> > > > bpf_dynptr_write() will automatically have its prior data slices
> > > > invalidated, even if the write is to data in the skb head of an uncloned
> > > > skb. Please note as well that any other helper calls that change the
> > > > underlying packet buffer (eg bpf_skb_pull_data()) invalidates any data
> > > > slices of the skb dynptr as well, for the same reasons.
> > > >
> > > > Signed-off-by: Joanne Koong <joannelkoong@gmail.com>
> > > > ---
> > >
> > > Sorry for chiming in late.
> > >
> > > I see one potential hole in bpf_dynptr_slice_rdwr. If the returned pointer is
> > > actually pointing to the stack (but verified as a PTR_TO_MEM in verifier state),
> > > we won't reflect changes to the stack state in the verifier for writes happening
> > > through it.
> > >
> > > For the worst case scenario, this will basically allow overwriting values of
> > > spilled pointers and doing arbitrary kernel memory reads/writes. This is only an
> > > issue when bpf_dynptr_slice_rdwr at runtime returns a pointer to the supplied
> > > buffer residing on program stack. To verify, by forcing the memcpy to buffer for
> > > skb_header_pointer I was able to make it dereference a garbage value for
> > > l4lb_all selftest.
> > >
> > > --- a/kernel/bpf/helpers.c
> > > +++ b/kernel/bpf/helpers.c
> > > @@ -2253,7 +2253,13 @@ __bpf_kfunc void *bpf_dynptr_slice(const struct bpf_dynptr_kern *ptr, u32 offset
> > >         case BPF_DYNPTR_TYPE_RINGBUF:
> > >                 return ptr->data + ptr->offset + offset;
> > >         case BPF_DYNPTR_TYPE_SKB:
> > > -               return skb_header_pointer(ptr->data, ptr->offset + offset, len, buffer);
> > > +       {
> > > +               void *p = skb_header_pointer(ptr->data, ptr->offset + offset, len, buffer);
> > > +               if (p == buffer)
> > > +                       return p;
> > > +               memcpy(buffer, p, len);
> > > +               return buffer;
> > > +       }
> > >
> > > --- a/tools/testing/selftests/bpf/progs/test_l4lb_noinline_dynptr.c
> > > +++ b/tools/testing/selftests/bpf/progs/test_l4lb_noinline_dynptr.c
> > > @@ -470,7 +470,10 @@ int balancer_ingress(struct __sk_buff *ctx)
> > >         eth = bpf_dynptr_slice_rdwr(&ptr, 0, buffer, sizeof(buffer));
> > >         if (!eth)
> > >                 return TC_ACT_SHOT;
> > > -       eth_proto = eth->eth_proto;
> > > +       *(void **)buffer = ctx;
> >
> > Great catch.
> > To fix the issue I think we should simply disallow such
> > stack abuse. The compiler won't be spilling registers
> > into C array on the stack.
> > This manual spill/fill is exploiting verifier logic.
> > After bpf_dynptr_slice_rdwr() we can mark all slots of the
> > buffer as STACK_POISON or some better name and
> > reject spill into such slots.
> >
>
> I agree this is simpler, but I'm not sure it will work properly. Verifier won't
> know when the lifetime of the buffer ends, so if we disallow spills until its
> written over it's going to be a pain for users.
>
> Something like:
>
> for (...) {
>         char buf[64];
>         bpf_dynptr_slice_rdwr(..., buf, 64);
>         ...
> }
>
> .. and then compiler decides to spill something where buf was located on stack
> outside the for loop. The verifier can't know when buf goes out of scope to
> unpoison the slots.

You're saying the "verifier doesn't know when buf ...".
The same applies to the compiler. It has no visibility
into what bpf_dynptr_slice_rdwr is doing.
So it never spills into a declared C array
as I tried to explain in the previous reply.
Spill/fill slots are always invisible to C.
(unless of course you do pointer arithmetic asm style)

> > > +       *(void **)eth = (void *)0xdeadbeef;
> > > +       ctx = *(void **)buffer;
> > > +       eth_proto = eth->eth_proto + ctx->len;
> > >         if (eth_proto == bpf_htons(ETH_P_IP))
> > >                 err = process_packet(&ptr, eth, nh_off, false, ctx);
> > >
> > > I think the proper fix is to treat it as a separate return type distinct from
> > > PTR_TO_MEM like PTR_TO_MEM_OR_PKT (or handle PTR_TO_MEM | DYNPTR_* specially),
> > > fork verifier state whenever there is a write, so that one path verifies it as
> > > PTR_TO_PACKET, while another as PTR_TO_STACK (if buffer was a stack ptr). I
> > > think for the rest it's not a problem, but there are allow_ptr_leak checks
> > > applied to PTR_TO_STACK and PTR_TO_MAP_VALUE, so that needs to be rechecked.
> > > Then we ensure that program is safe in either path.
> > >
> > > Also we need to fix regsafe to not consider other PTR_TO_MEMs equivalent to such
> > > a pointer. We could also fork verifier states on return, to verify either path
> > > separately right from the point following the call instruction.
> >
> > This is too complex imo.
>
> A better way to phrase this is to verify with R0 = PTR_TO_PACKET in one path,
> and push_stack with R0 = buffer's reg->type + size set to len in the other path
> for exploration later. In terms of verifier infra everything is there already,
> it just needs to analyze both cases which fall into the regular code handling
> the reg->type's. Probably then no adjustments to regsafe are needed either. It's
> like exploring branch instructions.

I still don't like it. There is no reason to go a complex path
when much simpler suffices.

  reply	other threads:[~2023-03-07 15:45 UTC|newest]

Thread overview: 50+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-03-01 15:49 [PATCH v13 bpf-next 00/10] Add skb + xdp dynptrs Joanne Koong
2023-03-01 15:49 ` [PATCH v13 bpf-next 01/10] bpf: Support "sk_buff" and "xdp_buff" as valid kfunc arg types Joanne Koong
2023-03-01 15:49 ` [PATCH v13 bpf-next 02/10] bpf: Refactor process_dynptr_func Joanne Koong
2023-03-01 15:49 ` [PATCH v13 bpf-next 03/10] bpf: Allow initializing dynptrs in kfuncs Joanne Koong
2023-03-06  7:36   ` Kumar Kartikeya Dwivedi
2023-03-07  6:53     ` Joanne Koong
2023-03-07 23:53       ` Andrii Nakryiko
2023-03-01 15:49 ` [PATCH v13 bpf-next 04/10] bpf: Define no-ops for externally called bpf dynptr functions Joanne Koong
2023-03-01 15:49 ` [PATCH v13 bpf-next 05/10] bpf: Refactor verifier dynptr into get_dynptr_arg_reg Joanne Koong
2023-03-01 15:49 ` [PATCH v13 bpf-next 06/10] bpf: Add __uninit kfunc annotation Joanne Koong
2023-03-01 15:49 ` [PATCH v13 bpf-next 07/10] bpf: Add skb dynptrs Joanne Koong
2023-03-01 15:49 ` [PATCH v13 bpf-next 08/10] bpf: Add xdp dynptrs Joanne Koong
2023-03-01 15:49 ` [PATCH v13 bpf-next 09/10] bpf: Add bpf_dynptr_slice and bpf_dynptr_slice_rdwr Joanne Koong
2023-03-02  3:29   ` kernel test robot
2023-03-02  3:53     ` Joanne Koong
2023-03-06  7:10   ` Kumar Kartikeya Dwivedi
2023-03-07  2:23     ` Alexei Starovoitov
2023-03-07 10:22       ` Kumar Kartikeya Dwivedi
2023-03-07 15:45         ` Alexei Starovoitov [this message]
2023-03-07 17:35           ` Kumar Kartikeya Dwivedi
2023-03-08  0:01             ` Andrii Nakryiko
2023-03-10 21:15               ` Alexei Starovoitov
2023-03-10 21:29                 ` Andrii Nakryiko
2023-03-10 21:54                   ` Kumar Kartikeya Dwivedi
2023-03-10 21:54                   ` Alexei Starovoitov
2023-03-13  6:31                     ` Joanne Koong
2023-03-13 14:41                       ` Kumar Kartikeya Dwivedi
2023-03-16 18:55                         ` Andrii Nakryiko
2023-03-27  7:47                           ` Joanne Koong
2023-03-28 21:42                             ` Andrii Nakryiko
2023-04-09  0:22                               ` Joanne Koong
2023-04-12 19:05                                 ` Andrii Nakryiko
2023-03-10 21:38                 ` Kumar Kartikeya Dwivedi
2023-03-10 21:49                   ` Alexei Starovoitov
2023-03-01 15:49 ` [PATCH v13 bpf-next 10/10] selftests/bpf: tests for using dynptrs to parse skb and xdp buffers Joanne Koong
2023-03-01 18:08   ` Alexei Starovoitov
2023-03-01 18:43     ` Andrii Nakryiko
2023-03-02  4:28     ` Joanne Koong
2023-03-08  1:55       ` Ilya Leoshkevich
2023-03-08  7:22         ` Joanne Koong
2023-03-08 14:24           ` Ilya Leoshkevich
2023-03-09  8:13             ` Joanne Koong
2023-03-10  3:40               ` Ilya Leoshkevich
2023-03-10  5:12                 ` Stanislav Fomichev
2023-03-10 17:43                   ` Alexei Starovoitov
2023-03-01 18:10 ` [PATCH v13 bpf-next 00/10] Add skb + xdp dynptrs patchwork-bot+netdevbpf
2023-03-08  8:16 ` Jakub Kicinski
2023-03-08 17:08   ` Andrii Nakryiko
2023-03-08 17:28     ` Jakub Kicinski
2023-03-08 19:02       ` Andrii Nakryiko

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=CAADnVQ+xOrCSwgxGQXNM5wHfOwV+x0csHfNyDYBHgyGVXgc2Ow@mail.gmail.com \
    --to=alexei.starovoitov@gmail.com \
    --cc=andrii@kernel.org \
    --cc=ast@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=joannelkoong@gmail.com \
    --cc=martin.lau@kernel.org \
    --cc=memxor@gmail.com \
    --cc=netdev@vger.kernel.org \
    --cc=toke@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).