Thread (38 messages) flat view 38 messages, 4 authors, 2022-02-24

Re: [PATCH bpf-next v1 04/15] bpf: Allow storing referenced PTR_TO_BTF_ID in map

From: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Date: 2022-02-24 08:43:34
Also in: bpf, netfilter-devel

On Thu, Feb 24, 2022 at 03:22:43AM IST, Alexei Starovoitov wrote:
On Tue, Feb 22, 2022 at 7:04 PM Kumar Kartikeya Dwivedi
[off-list ref] wrote:
quoted
On Tue, Feb 22, 2022 at 09:50:00PM IST, Alexei Starovoitov wrote:
quoted
On Mon, Feb 21, 2022 at 11:10 PM Kumar Kartikeya Dwivedi
[off-list ref] wrote:
quoted
On Tue, Feb 22, 2022 at 12:23:49PM IST, Alexei Starovoitov wrote:
quoted
On Sun, Feb 20, 2022 at 07:18:02PM +0530, Kumar Kartikeya Dwivedi wrote:
quoted
 static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regno,
                        int off, int bpf_size, enum bpf_access_type t,
-                       int value_regno, bool strict_alignment_once)
+                       int value_regno, bool strict_alignment_once,
+                       struct bpf_reg_state *atomic_load_reg)
No new side effects please.
value_regno is not pretty already.
At least its known ugliness that we need to clean up one day.
quoted
 static int check_atomic(struct bpf_verifier_env *env, int insn_idx, struct bpf_insn *insn)
 {
+   struct bpf_reg_state atomic_load_reg;
    int load_reg;
    int err;

+   __mark_reg_unknown(env, &atomic_load_reg);
+
    switch (insn->imm) {
    case BPF_ADD:
    case BPF_ADD | BPF_FETCH:
@@ -4813,6 +4894,7 @@ static int check_atomic(struct bpf_verifier_env *env, int insn_idx, struct bpf_i
            else
                    load_reg = insn->src_reg;

+           atomic_load_reg = *reg_state(env, load_reg);
            /* check and record load of old value */
            err = check_reg_arg(env, load_reg, DST_OP);
            if (err)
@@ -4825,20 +4907,21 @@ static int check_atomic(struct bpf_verifier_env *env, int insn_idx, struct bpf_i
    }

    /* Check whether we can read the memory, with second call for fetch
-    * case to simulate the register fill.
+    * case to simulate the register fill, which also triggers checks
+    * for manipulation of BTF ID pointers embedded in BPF maps.
     */
    err = check_mem_access(env, insn_idx, insn->dst_reg, insn->off,
-                          BPF_SIZE(insn->code), BPF_READ, -1, true);
+                          BPF_SIZE(insn->code), BPF_READ, -1, true, NULL);
    if (!err && load_reg >= 0)
            err = check_mem_access(env, insn_idx, insn->dst_reg, insn->off,
                                   BPF_SIZE(insn->code), BPF_READ, load_reg,
-                                  true);
+                                  true, load_reg >= 0 ? &atomic_load_reg : NULL);
Special xchg logic should be down outside of check_mem_access()
instead of hidden by layers of calls.
Right, it's ugly, but if we don't capture the reg state before that
check_reg_arg(env, load_reg, DST_OP), it's not possible to see the actual
PTR_TO_BTF_ID being moved into the map, since check_reg_arg will do a
mark_reg_unknown for value_regno. Any other ideas on what I can do?

37086bfdc737 ("bpf: Propagate stack bounds to registers in atomics w/ BPF_FETCH")
changed the order of check_mem_access and DST_OP check_reg_arg.
That highlights my point that side effects are bad.
That commit tries to work around that behavior and makes things
harder to extend like you found out with xchg logic.
Another option would be to add bpf_kptr_xchg() helper
instead of dealing with insn. It will be tiny bit slower,
but it will work on all architectures. While xchg bpf jit is
on x86,s390,mips so far.
Right, but kfunc is currently limited to x86, which is required to obtain a
refcounted PTR_TO_BTF_ID that you can move into the map, so it wouldn't make
much of a difference.
Well the patches to add trampoline support to powerpc were already posted.
quoted
quoted
We need to think more on how to refactor check_mem_acess without
digging ourselves into an even bigger hole.
So I'm ok with working on untangling check_mem_access as a follow up, but for
now should we go forward with how it is? Just looking at it yesterday makes me
think it's going to require a fair amount of refactoring and discussion.

Also, do you have any ideas on how to change it? Do you want it to work like how
is_valid_access callbacks work? So passing something like a bpf_insn_access_aux
into the call, where it sets how it'd like to update the register, and then
actual updates take place in caller context?
I don't like callbacks in general.
They're fine for walk_the_tree, for_each_elem accessors,
but passing a callback into check_mem_access is not great.
I didn't mean passing a callback, I meant passing a struct like you mentioned in
a previous comment to another patch (btf_field_info) where we can set state that
must be updated for the register, and then updates are done by the caller, to
separate the 'side effects' from the other checks. is_valid_access verifier
callback receive a similar bpf_insn_access_aux parameter which is then used to
update register state.
Do you mind going with a bpf_kptr_xchg() helper for now
and optimizing into direct xchg insn later?
I don't have a problem with that. I just didn't see any advantages (except the
wider architecture support that you pointed out). We still have to special case
some places in check_helper_call (since it needs to transfer R1's btf_id to R0,
and work with all PTR_TO_BTF_ID, not just 1), so the implementation is similar.

I guess for most usecases it wouldn't matter much.
It's not clear whether it's going to be faster to be noticeable.
Just for curiosity, I measured a loop of 5000 xchg ops, one with bpf_xchg, one
with bpf_kptr_xchg. This is the simple case (uncontended, raw cost of both
operations) xchg insn is at ~4 nsecs, bpf_kptr_xchg is at ~8 nsecs (single
socket 8 core Intel i5 @ 2.5GHz). I'm guessing in a complicated case spill/fill
of caller saved regs will also come into play for the helper case.

--
Kartikeya
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help