aboutsummaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
authorEduard Zingerman <eddyz87@gmail.com>2026-10-09 03:47:21 -0700
committerAlexei Starovoitov <ast@kernel.org>2026-10-09 20:29:12 +0000
commit8f3bd4537faad487a1aed4df24c1af4587904568 (patch)
treefaabaa0dc83f36124837f43fae1cd3c91458d259 /kernel
parent830e4372de5c8e1e0bbea925e74717b8fc259dfd (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.c18
-rw-r--r--kernel/bpf/verifier.c43
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: