From: Eduard Zingerman <eddyz87@gmail.com>
To: Andrii Nakryiko <andrii.nakryiko@gmail.com>
Cc: bpf@vger.kernel.org, ast@kernel.org, andrii@kernel.org,
daniel@iogearbox.net, martin.lau@linux.dev, kernel-team@fb.com,
yhs@fb.com
Subject: Re: [PATCH bpf-next v3 3/4] bpf: verify scalar ids mapping in regsafe() using check_ids()
Date: Thu, 08 Jun 2023 23:58:38 +0300 [thread overview]
Message-ID: <342f5aaa30ad5ad1a476ffe997e1669d58a8c8ed.camel@gmail.com> (raw)
In-Reply-To: <5bb3a6c3daf8c36a88eae6d0a3a8e52d7b24f842.camel@gmail.com>
On Thu, 2023-06-08 at 22:05 +0300, Eduard Zingerman wrote:
[...]
> > Hm.. It's clever and pretty minimal, I like it. We are basically
> > allocating virtual ID for SCALAR that doesn't have id, just to make
> > sure we get a conflict if the SCALAR with ID cannot be mapped into two
> > different SCALARs, right?
> >
> > The only question would be if it's safe to do that for case when
> > old_reg->id != 0 and cur_reg->id == 0? E.g., if in old (verified)
> > state we have r6.id = r7.id = 123, and in new state we have r6.id = 0
> > and r7.id = 0, then your implementation above will say that states are
> > equivalent. But are they, given there is a link between r6 and r7 that
> > might be required for correctness. Which we don't have in current
> > state.
>
> You mean the other way around, rold.id == 0, rcur.id != 0, right?
> (below 0/2 means: original value 0, replaced by new id 2).
>
> (1) old cur
> r6.id 0/2 1
> r7.id 0/3 1 check_ids returns true
>
> (2) old cur
> r6.id 1 0/2
> r7.id 1 0/3 check_ids returns false
>
> Also, (1) is no different from (3):
>
> (3) old cur
> r6.id 1 3
> r7.id 2 3 check_ids returns true
>
> Current check:
>
> if (!rold->precise)
> return true;
> return range_within(rold, rcur) &&
> tnum_in(rold->var_off, rcur->var_off) &&
> check_ids(rold->id, rcur->id, idmap);
>
> Will reject (1) and (2), but will accept (3).
>
> New check:
>
> if (!rold->precise)
> return true;
> return range_within(rold, rcur) &&
> tnum_in(rold->var_off, rcur->var_off) &&
> check_scalar_ids(rold->id, rcur->id, idmap);
>
> Will reject (2), but will accept (1) and (3).
>
> And modification of check_scalar_ids() to generate IDs only for rold
> or only for rcur would not reject (3) either.
>
> (3) would be rejected only if current check is used together with
> elimination of unique scalar IDs from old states.
>
> My previous experiments show that eliminating unique IDs from old
> states and not eliminating unique IDs from cur states is *very* bad
> for performance.
>
> >
> > So with this we are getting to my original concerns with your
> > !rold->id approach, which tries to ignore the necessity of link
> > between registers.
> >
> > What if we do this only for old registers? Then, (in old state) r6.id
> > = 0, r7.id = 0, (in new state) r6.id = r7.id = 123. This will be
> > rejected because first we'll map 123 to newly allocated X for r6.id,
> > and then when we try to match r7.id=123 to another allocated ID X+1
> > we'll get a conflict, right?
[...]
Ok, here is what I think is the final version:
a. for each old or cur ID generate temporary unique ID;
b. for scalars use modified check_ids that forbids mapping same 'cur'
ID to several different 'old' IDs.
(a) allows to reject situations like:
(1) old cur (2) old cur
r6.id 0 1 r6.id 1 0
r7.id 0 1 r7.id 1 0
(b) allows to reject situations like:
(3) old cur
r6.id 1 3
r7.id 2 3
And whether some scalar ID is unique or not does not matter for the
algorithm.
Tests are passing, katran example is fine (350k insns, 29K states),
minor veristat regression:
File Program States (DIFF)
--------- ------------------------------ -------------
bpf_xdp.o tail_nodeport_nat_ingress_ipv4 +3 (+0.80%)
bpf_xdp.o tail_rev_nodeport_lb4 +2 (+0.50%)
--- 8< -----------------------------
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 235d7eded565..5794dc7830db 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -15149,6 +15149,31 @@ static bool check_ids(u32 old_id, u32 cur_id, struct bpf_id_pair *idmap)
return false;
}
+static bool check_scalar_ids(struct bpf_verifier_env *env, u32 old_id, u32 cur_id,
+ struct bpf_id_pair *idmap)
+{
+ unsigned int i;
+
+ old_id = old_id ? old_id : env->id_gen++;
+ cur_id = cur_id ? cur_id : env->id_gen++;
+
+ for (i = 0; i < BPF_ID_MAP_SIZE; i++) {
+ if (!idmap[i].old) {
+ /* Reached an empty slot; haven't seen this id before */
+ idmap[i].old = old_id;
+ idmap[i].cur = cur_id;
+ return true;
+ }
+ if (idmap[i].old == old_id)
+ return idmap[i].cur == cur_id;
+ if (idmap[i].cur == cur_id)
+ return false;
+ }
+ /* We ran out of idmap slots, which should be impossible */
+ WARN_ON_ONCE(1);
+ return false;
+}
+
static void clean_func_state(struct bpf_verifier_env *env,
struct bpf_func_state *st)
{
@@ -15325,7 +15350,7 @@ static bool regsafe(struct bpf_verifier_env *env, struct bpf_reg_state *rold,
*/
return range_within(rold, rcur) &&
tnum_in(rold->var_off, rcur->var_off) &&
- check_ids(rold->id, rcur->id, idmap);
+ check_scalar_ids(env, rold->id, rcur->id, idmap);
case PTR_TO_MAP_KEY:
case PTR_TO_MAP_VALUE:
case PTR_TO_MEM:
----------------------------- >8 ---
next prev parent reply other threads:[~2023-06-08 20:58 UTC|newest]
Thread overview: 21+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-06-06 22:24 [PATCH bpf-next v3 0/4] verify scalar ids mapping in regsafe() Eduard Zingerman
2023-06-06 22:24 ` [PATCH bpf-next v3 1/4] bpf: use scalar ids in mark_chain_precision() Eduard Zingerman
2023-06-07 21:40 ` Andrii Nakryiko
2023-06-08 12:35 ` Eduard Zingerman
2023-06-08 15:43 ` Alexei Starovoitov
2023-06-08 17:30 ` Eduard Zingerman
2023-06-06 22:24 ` [PATCH bpf-next v3 2/4] selftests/bpf: check if mark_chain_precision() follows scalar ids Eduard Zingerman
2023-06-07 21:40 ` Andrii Nakryiko
2023-06-08 16:17 ` Eduard Zingerman
2023-06-08 17:33 ` Andrii Nakryiko
2023-06-08 17:35 ` Eduard Zingerman
2023-06-06 22:24 ` [PATCH bpf-next v3 3/4] bpf: verify scalar ids mapping in regsafe() using check_ids() Eduard Zingerman
2023-06-07 21:40 ` Andrii Nakryiko
2023-06-08 1:26 ` Eduard Zingerman
2023-06-08 17:21 ` Andrii Nakryiko
2023-06-08 19:05 ` Eduard Zingerman
2023-06-08 20:58 ` Eduard Zingerman [this message]
2023-06-08 22:37 ` Andrii Nakryiko
2023-06-08 23:40 ` Eduard Zingerman
2023-06-06 22:24 ` [PATCH bpf-next v3 4/4] selftests/bpf: verify that check_ids() is used for scalars in regsafe() Eduard Zingerman
2023-06-07 21:40 ` 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=342f5aaa30ad5ad1a476ffe997e1669d58a8c8ed.camel@gmail.com \
--to=eddyz87@gmail.com \
--cc=andrii.nakryiko@gmail.com \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=kernel-team@fb.com \
--cc=martin.lau@linux.dev \
--cc=yhs@fb.com \
/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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.