diff options
| author | Eduard Zingerman <eddyz87@gmail.com> | 2026-10-09 03:47:21 -0700 |
|---|---|---|
| committer | Alexei Starovoitov <ast@kernel.org> | 2026-10-09 20:29:12 +0000 |
| commit | 8f3bd4537faad487a1aed4df24c1af4587904568 (patch) | |
| tree | faabaa0dc83f36124837f43fae1cd3c91458d259 /kernel | |
| parent | 830e4372de5c8e1e0bbea925e74717b8fc259dfd (diff) | |
bpf: Describe kfunc stack accesses with arg_access_info
Return kfunc stack effects through arg_access_info while preserving the
existing BTF size lookup and read/def classification. Retain __sz/__szk
handling and conservatively mark pointer arguments as possible writes.
Remove the temporary signed-size adapter from liveness. Add liveness
assertions for privileged and unprivileged __uninit outputs.
Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Link: https://patch.msgid.link/20261009-scev-minimal-rebase-v3-3-bcba9bc53ac9@gmail.com
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/bpf/liveness.c | 18 | ||||
| -rw-r--r-- | kernel/bpf/verifier.c | 43 |
2 files changed, 24 insertions, 37 deletions
diff --git a/kernel/bpf/liveness.c b/kernel/bpf/liveness.c index 353b52031577..d4089a488784 100644 --- a/kernel/bpf/liveness.c +++ b/kernel/bpf/liveness.c @@ -1604,21 +1604,6 @@ static int record_load_store_access(struct bpf_verifier_env *env, return 0; } -/* Adapt the signed kfunc access size until its producer is converted. */ -static struct arg_access_info stack_access_info(s64 bytes) -{ - bool write = bytes < 0 && bytes != S64_MIN && -bytes < U32_MAX; - - return (struct arg_access_info) { - .size = bytes == S64_MIN - ? U32_MAX - : min_t(u64, bytes < 0 ? -bytes : bytes, U32_MAX), - .may_read = bytes > 0 || bytes == S64_MIN, - .may_write = write, - .must_write = write, - }; -} - static int record_arg_access(struct bpf_verifier_env *env, struct func_instance *instance, struct bpf_insn *insn, @@ -1636,8 +1621,7 @@ static int record_arg_access(struct bpf_verifier_env *env, if (bpf_helper_call(insn)) { info = bpf_helper_stack_access_bytes(env, insn, arg_idx, insn_idx); } else if (bpf_pseudo_kfunc_call(insn)) { - info = stack_access_info(bpf_kfunc_stack_access_bytes(env, insn, - arg_idx, insn_idx)); + info = bpf_kfunc_stack_access_bytes(env, insn, arg_idx, insn_idx); } else { for (int f = 0; f <= depth; f++) { err = mark_stack_read_all(env, instance, f, insn_idx); diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index bbb81ba0f2dc..8987c9a69d10 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -14487,28 +14487,29 @@ out: } /* - * Determine how many bytes a kfunc accesses through a stack pointer at - * argument position @arg (0-based, corresponding to R1-R5). - * - * Returns: - * > 0 known read access size in bytes - * 0 doesn't access memory through that argument (ex: not a pointer) - * S64_MIN unknown - * < 0 known write access of (-return) bytes + * Describe a kfunc's stack access through argument slot @arg (0-based). + * As for helpers, must_write describes destruction of prior verifier state, + * rather than a guarantee that the callee writes every byte at runtime. */ -s64 bpf_kfunc_stack_access_bytes(struct bpf_verifier_env *env, struct bpf_insn *insn, - int arg, int insn_idx) +struct arg_access_info +bpf_kfunc_stack_access_bytes(struct bpf_verifier_env *env, struct bpf_insn *insn, + int arg, int insn_idx) { struct bpf_insn_aux_data *aux = &env->insn_aux_data[insn_idx]; + struct arg_access_info info = { + .size = U32_MAX, + .may_read = true, + .may_write = true, + }; struct bpf_call_arg_meta meta; const struct btf_param *args; const struct btf_type *t, *ref_t; const struct btf *btf; u32 i, slot, nargs, type_size; - s64 size; + u64 size; if (bpf_fetch_kfunc_arg_meta(env, insn->imm, insn->off, &meta) < 0) - return S64_MIN; + return info; btf = meta.btf; args = btf_params(meta.func_proto); @@ -14523,11 +14524,11 @@ s64 bpf_kfunc_stack_access_bytes(struct bpf_verifier_env *env, struct bpf_insn * for (i = 0, slot = 0; i < nargs && slot < arg; i++) slot += btf_arg_slots(btf_type_skip_modifiers(btf, args[i].type, NULL)); if (i >= nargs || slot != arg) - return 0; + return (struct arg_access_info) {}; t = btf_type_skip_modifiers(btf, args[i].type, NULL); if (!btf_type_is_ptr(t)) - return 0; + return (struct arg_access_info) {}; /* dynptr: fixed 16-byte on-stack representation */ if (is_kfunc_arg_dynptr(btf, &args[i])) { @@ -14543,11 +14544,11 @@ s64 bpf_kfunc_stack_access_bytes(struct bpf_verifier_env *env, struct bpf_insn * if (size_reg <= MAX_BPF_FUNC_REG_ARGS && (aux->const_reg_mask & BIT(size_reg))) { - size = (s64)aux->const_reg_vals[size_reg]; + size = aux->const_reg_vals[size_reg]; goto out; } /* Unknown size: the read may extend anywhere up to the frame top. */ - return S64_MIN; + return info; } /* fixed-size pointed-to type: resolve via BTF */ @@ -14557,15 +14558,17 @@ s64 bpf_kfunc_stack_access_bytes(struct bpf_verifier_env *env, struct bpf_insn * goto out; } - return S64_MIN; + return info; out: + info.size = min_t(u64, size, U32_MAX); /* KF_ITER_NEW kfuncs initialize the iterator state at arg 0 */ if (arg == 0 && meta.kfunc_flags & KF_ITER_NEW) - return -size; + info.may_read = false; if (is_kfunc_arg_uninit(btf, &args[i]) && (is_kfunc_arg_dynptr(btf, &args[i]) || env->allow_uninit_stack)) - return -size; - return size; + info.may_read = false; + info.must_write = !info.may_read && info.size != U32_MAX; + return info; } /* check special kfuncs and return: |
