BPF List
 help / color / mirror / Atom feed
From: Eduard Zingerman <eddyz87@gmail.com>
To: Andrei Matei <andreimatei1@gmail.com>
Cc: bpf@vger.kernel.org, andrii.nakryiko@gmail.com
Subject: Re: [PATCH bpf-next v2 1/1] bpf: Simplify checking size of helper accesses
Date: Tue, 19 Dec 2023 19:01:12 +0200	[thread overview]
Message-ID: <0994aae8e3086cb93f25a47ee9e81a6894dbff26.camel@gmail.com> (raw)
In-Reply-To: <CABWLseu+uALXXwaSGJ=zJhoZuWH3Lajby-ip8oKAmTOLxci7Vw@mail.gmail.com>

On Mon, 2023-12-18 at 21:54 -0500, Andrei Matei wrote:
> On Mon, Dec 18, 2023 at 7:04 PM Eduard Zingerman <eddyz87@gmail.com> wrote:
> > 
> > On Sat, 2023-12-16 at 20:06 -0500, Andrei Matei wrote:
> > [...]
> > 
> > > (*) Besides standing to reason that the checks for a bigger size access
> > > are a super-set of the checks for a smaller size access, I have also
> > > mechanically verified this by reading the code for all types of
> > > pointers. I could convince myself that it's true for all but
> > > PTR_TO_BTF_ID (check_ptr_to_btf_access). There, simply looking
> > > line-by-line does not immediately prove what we want. If anyone has any
> > > qualms, let me know.
> > 
> > check_help_mem_access() is a bit obfuscated :)
> > After staring at it for a bit I have a question regarding
> > check_ptr_to_btf_access():
> > - it can call btf_struct_access(),
> >   which in can call btf_struct_walk(),
> >   which has the following check:
> > 
> >                 if (btf_type_is_ptr(mtype)) {
> >                         const struct btf_type *stype, *t;
> >                         enum bpf_type_flag tmp_flag = 0;
> >                         u32 id;
> > 
> >                         if (msize != size || off != moff) {
> >                                 bpf_log(log,
> >                                         "cannot access ptr member %s with moff %u in struct %s with off %u size %u\n",
> >                                         mname, moff, tname, off, size);
> >                                 return -EACCES;
> >                         }
> > 
> > - previously this code was executed twice, for size 0 and for size
> >   umax_value of the size register;
> > - now this code is executed only for umax_value of the size register;
> > - is it possible that with size 0 this code could have reported error
> >   -EACCESS error, which would be missed now?
> 
> I don't have a good answer. I too have looked at check_ptr_to_btf_access() and
> ended up confused -- but then again, I don't know what's supposed to be allowed
> and what's supposed to not be allowed. I will say, though, that I don't think
> the code as it stands make sense, and I don't think any interaction between the
> zero-size check and btf access is intentional. Around [1] we've looked a bit at
> the history of this zero-size check, and it's been there forever, predating
> most of the code around it. What convinces me personally that the zero-size
> check was not load-bearing is the fact that we were only performing
> the check iff
> umin == 0 -- we were not consistently performing a check for the umin value.
> Also, obviously, we were not performing a check for every possible value in
> between umin and umax. So I can't really imagine positive benefits of the
> inconsistent check we were doing. But then again, I cannot actually speak with
> confidence about it.

Not checking consistently for all possible offsets is a good argument, thank you.

> As a btw, I'll say that we don't allow variable-offset accesses to btf ptr [2].
> I don't know if this should influence how we treat the access size... but
> maybe? Like, should we disallow variable-sized accesses on the same argument as
> disallowing variable-offset ones (whatever that argument may be)? I don't know
> what I'm talking about (generally BTF is foreign to me), but I imagine this all
> means that currently the verifier allows one to read from an array field by
> starting at a compile-time constant offset, and extending to a variable size.
> However, you cannot start from an arbitrary offset, though. Does this
> combination of being strict about the offset but permissive about the size make
> sense?

I agree with you, that disallowing variable size access in BTF case
might make sense. check_ptr_to_btf_access() calls either:
a. env->ops->btf_struct_access(), which is one of the following:
   1. _tc_cls_act_btf_struct_access() (through a function pointer),
      which allows accessing exactly one field: struct nf_conn->mark;
   2. bpf_tcp_ca_btf_struct_access, which allows accessing several
      fields in sock, tcp_sock and inet_connection_sock structures.
b. btf_struct_access(), which checks the following:
   1. part with btf_find_struct_meta() checks that access does not reach
      to some forbidden field;
   2. btf_struct_walk() checks that offset and size of the access match
      offset and size of some field in the target BTF structure;

Technically, checks a.1, a.2 and b.1 are ok with variable size access,
but b.2 is not and it does not seem to be verified.

I tried a patch below and test_progs seem to pass locally
(but I have some troubles with my local setup at the moment,
 so it should be double-checked).

> I'll take guidance. If people prefer we don't touch this code at all, that's
> fine. Although it doesn't feel good to be driven simply by fear.

Would be good if others could comment.

[...]

---

diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index cf2a09408bdc..946415d11338 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -7328,6 +7328,7 @@ static int check_mem_size_reg(struct bpf_verifier_env *env,
 {
        int err;
        const bool size_is_const = tnum_is_const(reg->var_off);
+       struct bpf_reg_state *ptr_reg = &cur_regs(env)[regno - 1];
 
        /* This is used to refine r0 return value bounds for helpers
         * that enforce this value as an upper bound on return values.
@@ -7373,6 +7374,13 @@ static int check_mem_size_reg(struct bpf_verifier_env *env,
                verbose(env, "verifier bug: !zero_size_allowed should have been handled already\n");
                return -EFAULT;
        }
+
+       if (base_type(ptr_reg->type) == PTR_TO_BTF_ID && !size_is_const) {
+               verbose(env, "variable length access to r%d %s is not allowed",
+                       regno - 1, reg_type_str(env, ptr_reg->type));
+               return -EACCES;
+       }
+
        err = check_helper_mem_access(env, regno - 1,
                                      reg->umax_value,
                                      /* zero_size_allowed: we asserted above that umax_value is

  reply	other threads:[~2023-12-19 17:01 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-12-17  1:06 [PATCH bpf-next v2 0/1] bpf: Simplify checking size of helper accesses Andrei Matei
2023-12-17  1:06 ` [PATCH bpf-next v2 1/1] " Andrei Matei
2023-12-19  0:04   ` Eduard Zingerman
2023-12-19  2:54     ` Andrei Matei
2023-12-19 17:01       ` Eduard Zingerman [this message]
2023-12-19 19:08         ` Andrii Nakryiko
2023-12-19 19:24           ` Eduard Zingerman
2023-12-19 19:31             ` Andrii Nakryiko
2023-12-20  3:33           ` Alexei Starovoitov
2023-12-19 19:03   ` Andrii Nakryiko
2023-12-19 19:53     ` Andrei Matei
2023-12-21  5:00       ` 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=0994aae8e3086cb93f25a47ee9e81a6894dbff26.camel@gmail.com \
    --to=eddyz87@gmail.com \
    --cc=andreimatei1@gmail.com \
    --cc=andrii.nakryiko@gmail.com \
    --cc=bpf@vger.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