ARG_DONTCARE is the zero-valued terminator for the fixed-size helper argument array rather than an argument whose value is ignored. Rename it to ARG_UNUSED and stop helper argument iteration explicitly when it is encountered. Commit c3fd8e5fd100 ("bpf: Reject non-scalar bpf_loop iteration counts") introduced ARG_SCALAR for arguments that must be scalars. Reuse it for integer and enum global subprogram arguments, which already require SCALAR_VALUE despite being classified as ARG_ANYTHING, and for exception callback arguments. This leaves ARG_ANYTHING with its legacy helper behavior of accepting pointers when pointer leaks are allowed. Also pass ARG_PTR_TO_MEM when checking a global subprogram memory argument instead of using the prototype terminator as a placeholder. No functional change. Signed-off-by: Amery Hung --- include/linux/bpf.h | 2 +- kernel/bpf/btf.c | 2 +- kernel/bpf/verifier.c | 25 ++++++++++++------------- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index e80963971f68..dcbf8cea45d6 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -874,7 +874,7 @@ enum bpf_type_flag { /* function argument constraints */ enum bpf_arg_type { - ARG_DONTCARE = 0, /* unused argument in helper function */ + ARG_UNUSED = 0, /* unused argument; terminates argument iteration */ /* the following constraints used to prototype * bpf_map_lookup/update/delete_elem() functions diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index 31057c8f3a7c..122a4101ce94 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -8244,7 +8244,7 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog) return -EINVAL; } if (btf_type_is_int(t) || btf_is_any_enum(t)) { - sub->args[i].arg_type = ARG_ANYTHING; + sub->args[i].arg_type = ARG_SCALAR; continue; } if (!is_global) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 42d89db5df07..5d21bee45805 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -8843,9 +8843,6 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg, u32 key_size; int err = 0; - if (arg_type == ARG_DONTCARE) - return 0; - if (regno >= 0) { err = check_reg_arg(env, regno, SRC_OP); if (err) @@ -9356,7 +9353,7 @@ static bool check_raw_mode_ok(const struct bpf_func_proto *fn, struct bpf_call_a int i; for (i = 0; i < ARRAY_SIZE(fn->arg_type); i++) { - if (fn->arg_type[i] == ARG_DONTCARE) + if (fn->arg_type[i] == ARG_UNUSED) break; if (!arg_type_is_raw_mem(fn->arg_type[i])) continue; @@ -9406,7 +9403,7 @@ static bool check_btf_id_ok(const struct bpf_func_proto *fn) int i; for (i = 0; i < ARRAY_SIZE(fn->arg_type); i++) { - if (fn->arg_type[i] == ARG_DONTCARE) + if (fn->arg_type[i] == ARG_UNUSED) break; if (base_type(fn->arg_type[i]) == ARG_PTR_TO_BTF_ID) return !!fn->arg_btf_id[i]; @@ -9429,7 +9426,7 @@ static bool check_mem_arg_rw_flag_ok(const struct bpf_func_proto *fn) for (i = 0; i < ARRAY_SIZE(fn->arg_type); i++) { enum bpf_arg_type arg_type = fn->arg_type[i]; - if (arg_type == ARG_DONTCARE) + if (arg_type == ARG_UNUSED) break; if (base_type(arg_type) != ARG_PTR_TO_MEM) continue; @@ -9447,7 +9444,7 @@ static bool check_proto_release_reg(const struct bpf_func_proto *fn, struct bpf_ for (i = 0; i < ARRAY_SIZE(fn->arg_type); i++) { enum bpf_arg_type arg_type = fn->arg_type[i]; - if (arg_type == ARG_DONTCARE) + if (arg_type == ARG_UNUSED) break; if (arg_type_is_release(arg_type)) { if (meta->release_regno) @@ -9818,7 +9815,7 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog, struct bpf_reg_state *reg = get_func_arg_reg(caller, regs, i); struct bpf_subprog_arg_info *arg = &sub->args[i]; - if (arg->arg_type == ARG_ANYTHING) { + if (arg->arg_type == ARG_SCALAR) { if (reg->type != SCALAR_VALUE) { bpf_log(log, "%s is not a scalar\n", reg_arg_name(env, argno)); return -EINVAL; @@ -9842,7 +9839,7 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog, return -EINVAL; } } else if (base_type(arg->arg_type) == ARG_PTR_TO_MEM) { - ret = check_func_arg_reg_off(env, reg, argno, ARG_DONTCARE); + ret = check_func_arg_reg_off(env, reg, argno, ARG_PTR_TO_MEM); if (ret < 0) return ret; if (check_mem_reg(env, reg, argno, arg->mem_size, BPF_READ | BPF_WRITE, NULL, @@ -11029,6 +11026,8 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn meta.fn = fn; /* check args */ for (i = 0; i < MAX_BPF_FUNC_REG_ARGS; i++) { + if (fn->arg_type[i] == ARG_UNUSED) + break; err = check_func_arg(env, i, &meta, insn_idx); if (err) return err; @@ -12924,7 +12923,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me for (i = 0; i < nargs; i++) { struct bpf_reg_state *reg = get_func_arg_reg(caller, regs, i); const struct btf_type *t, *ref_t, *resolve_ret; - enum bpf_arg_type arg_type = ARG_DONTCARE; + enum bpf_arg_type arg_type = ARG_UNUSED; argno_t argno = argno_from_arg(i + 1); int regno = reg_from_argno(argno); bool btf_id_fixed_off_ok = true; @@ -17991,7 +17990,7 @@ bool bpf_get_call_summary(struct bpf_verifier_env *env, struct bpf_insn *call, cs->is_void = fn->ret_type == RET_VOID; cs->num_params = 0; for (i = 0; i < ARRAY_SIZE(fn->arg_type); ++i) { - if (fn->arg_type[i] == ARG_DONTCARE) + if (fn->arg_type[i] == ARG_UNUSED) break; cs->num_params++; } @@ -19795,7 +19794,7 @@ static int do_check_common(struct bpf_verifier_env *env, int subprog) } /* Also ensure the callback only has a single scalar argument. */ - if (sub->arg_cnt != 1 || sub->args[0].arg_type != ARG_ANYTHING) { + if (sub->arg_cnt != 1 || sub->args[0].arg_type != ARG_SCALAR) { verbose(env, "exception cb only supports single integer argument\n"); ret = -EINVAL; goto out; @@ -19808,7 +19807,7 @@ static int do_check_common(struct bpf_verifier_env *env, int subprog) if (arg->arg_type == ARG_PTR_TO_CTX) { reg->type = PTR_TO_CTX; mark_reg_known_zero(env, regs, i); - } else if (arg->arg_type == ARG_ANYTHING) { + } else if (arg->arg_type == ARG_SCALAR) { reg->type = SCALAR_VALUE; mark_reg_unknown(env, regs, i); } else if (arg->arg_type == ARG_PTR_TO_DYNPTR) { -- 2.52.0