bpf: Ensure kptr_struct_meta is non-NULL for collection insert and refcount_acquire
authorDave Marchevsky <davemarchevsky@fb.com>
Mon, 21 Aug 2023 19:33:05 +0000 (12:33 -0700)
committerAlexei Starovoitov <ast@kernel.org>
Fri, 25 Aug 2023 16:23:16 +0000 (09:23 -0700)
It's straightforward to prove that kptr_struct_meta must be non-NULL for
any valid call to these kfuncs:

  * btf_parse_struct_metas in btf.c creates a btf_struct_meta for any
    struct in user BTF with a special field (e.g. bpf_refcount,
    {rb,list}_node). These are stored in that BTF's struct_meta_tab.

  * __process_kf_arg_ptr_to_graph_node in verifier.c ensures that nodes
    have {rb,list}_node field and that it's at the correct offset.
    Similarly, check_kfunc_args ensures bpf_refcount field existence for
    node param to bpf_refcount_acquire.

  * So a btf_struct_meta must have been created for the struct type of
    node param to these kfuncs

  * That BTF and its struct_meta_tab are guaranteed to still be around.
    Any arbitrary {rb,list} node the BPF program interacts with either:
    came from bpf_obj_new or a collection removal kfunc in the same
    program, in which case the BTF is associated with the program and
    still around; or came from bpf_kptr_xchg, in which case the BTF was
    associated with the map and is still around

Instead of silently continuing with NULL struct_meta, which caused
confusing bugs such as those addressed by commit 2140a6e3422d ("bpf: Set
kptr_struct_meta for node param to list and rbtree insert funcs"), let's
error out. Then, at runtime, we can confidently say that the
implementations of these kfuncs were given a non-NULL kptr_struct_meta,
meaning that special-field-specific functionality like
bpf_obj_free_fields and the bpf_obj_drop change introduced later in this
series are guaranteed to execute.

This patch doesn't change functionality, just makes it easier to reason
about existing functionality.

Signed-off-by: Dave Marchevsky <davemarchevsky@fb.com>
Acked-by: Yonghong Song <yonghong.song@linux.dev>
Link: https://lore.kernel.org/r/20230821193311.3290257-2-davemarchevsky@fb.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
kernel/bpf/verifier.c

index 0680569..5a61089 100644 (file)
@@ -18276,6 +18276,13 @@ static int fixup_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
                struct btf_struct_meta *kptr_struct_meta = env->insn_aux_data[insn_idx].kptr_struct_meta;
                struct bpf_insn addr[2] = { BPF_LD_IMM64(BPF_REG_2, (long)kptr_struct_meta) };
 
+               if (desc->func_id == special_kfunc_list[KF_bpf_refcount_acquire_impl] &&
+                   !kptr_struct_meta) {
+                       verbose(env, "verifier internal error: kptr_struct_meta expected at insn_idx %d\n",
+                               insn_idx);
+                       return -EFAULT;
+               }
+
                insn_buf[0] = addr[0];
                insn_buf[1] = addr[1];
                insn_buf[2] = *insn;
@@ -18283,6 +18290,7 @@ static int fixup_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
        } else if (desc->func_id == special_kfunc_list[KF_bpf_list_push_back_impl] ||
                   desc->func_id == special_kfunc_list[KF_bpf_list_push_front_impl] ||
                   desc->func_id == special_kfunc_list[KF_bpf_rbtree_add_impl]) {
+               struct btf_struct_meta *kptr_struct_meta = env->insn_aux_data[insn_idx].kptr_struct_meta;
                int struct_meta_reg = BPF_REG_3;
                int node_offset_reg = BPF_REG_4;
 
@@ -18292,6 +18300,12 @@ static int fixup_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
                        node_offset_reg = BPF_REG_5;
                }
 
+               if (!kptr_struct_meta) {
+                       verbose(env, "verifier internal error: kptr_struct_meta expected at insn_idx %d\n",
+                               insn_idx);
+                       return -EFAULT;
+               }
+
                __fixup_collection_insert_kfunc(&env->insn_aux_data[insn_idx], struct_meta_reg,
                                                node_offset_reg, insn, insn_buf, cnt);
        } else if (desc->func_id == special_kfunc_list[KF_bpf_cast_to_kern_ctx] ||