From: sashiko-bot@kernel.org
To: "Alexei Starovoitov" <alexei.starovoitov@gmail.com>, bpf@vger.kernel.org
Subject: Re: [PATCH bpf-next v2 09/35] bpf: Find the registers that hold a number or a pointer to arena
Date: Thu, 05 Nov 2026 21:40:05 +0000 [thread overview]
Message-ID: <sashiko-outbox-200251@kernel.org> (raw)
In-Reply-To: <20261105204513.2230344-10-alexei.starovoitov@gmail.com>
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [Medium] bpf: misclassified 32-bit context loads in flow_loaded_kind()
- [Medium] bpf: misclassified void * special kfuncs in flow_returned_kind()
- [Medium] bpf: stack pointer spill via BPF_STORE_REL lost in flow_kinds_step()
--
commit d616e924eec822ed1af91bad97267316408cf00d
Author: Alexei Starovoitov <ast@kernel.org>
bpf: Find the registers that hold a number or a pointer to arena
This commit adds bpf_flow_compute_kinds() in kernel/bpf/flow.c to perform a
forward dataflow pass before the main verification pass, identifying which
registers and stack slots hold plain scalars/arena pointers, scalar-only map
value pointers, or context pointers on every path reaching each instruction.
> diff --git a/kernel/bpf/flow.c b/kernel/bpf/flow.c
> index 0f5acdf1cb946..837d5054c2a3d 100644
> --- a/kernel/bpf/flow.c
> +++ b/kernel/bpf/flow.c
> @@ -514,3 +514,310 @@ int bpf_flow_place_guards(struct bpf_verifier_env *env)
[ ... ]
> +/* What holds on both ways to an insn. Returns true if that is less than before. */
> +static bool flow_kinds_meet(struct flow_kinds *to, const struct flow_kinds *from)
> +{
> + unsigned long lost = 0;
> + int i, j;
> +
> + for (i = 0; i < FLOW_KINDS; i++) {
> + for (j = 0; j < ARRAY_SIZE(to->is[i]); j++) {
> + lost |= to->is[i][j] & ~from->is[i][j];
> + to->is[i][j] &= from->is[i][j];
> + }
> + }
> + return lost;
> +}
[ ... ]
> +/* What @insn loads through the register @base */
> +static enum flow_kind flow_loaded_kind(struct bpf_verifier_env *env, const struct flow_kinds *k,
> + const struct bpf_insn *insn, u8 base)
> +{
> + struct bpf_insn_access_aux info = { .reg_type = SCALAR_VALUE };
> + int size = bpf_size_to_bytes(BPF_SIZE(insn->code));
> + bool tstamp = env->prog->tstamp_type_access, valid;
> +
> + /* a number that is good for a load is an address in arena */
> + if (test_bit(base, k->is[FLOW_IS_VALUE]))
> + return FLOW_IS_PLAIN;
> + /* only the context has pointers that are shorter than a register */
> + if (!test_bit(base, k->is[FLOW_IS_CTX]))
> + return size < BPF_REG_SIZE ? FLOW_IS_PLAIN : FLOW_IS_OTHER;
[Severity: Medium]
Can flow_loaded_kind() in kernel/bpf/flow.c misclassify 32-bit context
pointer loads (such as skb->data or xdp_md->data) as FLOW_IS_PLAIN when a
register holds the context pointer on one incoming path and a non-context
value on another?
In bpf_flow_compute_kinds(), all bits in struct flow_kinds start as 1 and are
intersected across predecessors in flow_kinds_meet() via bitwise AND:
to->is[i][j] &= from->is[i][j];
As a result, test_bit(base, k->is[FLOW_IS_CTX]) is a must-analysis bit that
is 1 only when base holds the unmodified context pointer on every path
reaching the instruction, and becomes 0 whenever base may hold a non-context
value on at least one path (for example, after a branch or across static
subprogram callsites).
However, flow_loaded_kind() checks !test_bit(base, k->is[FLOW_IS_CTX]) and
returns FLOW_IS_PLAIN for size < BPF_REG_SIZE, treating a cleared bit in
FLOW_IS_CTX as meaning that base cannot be a context pointer on any path.
When a 32-bit context pointer field is loaded through such a register,
flow_loaded_kind() marks the resulting packet pointer as FLOW_IS_PLAIN,
allowing the main pass to join states with distinct packet offsets and reject
valid programs. In addition, when k->is[FLOW_IS_CTX] drops from 1 to 0 during
the fixpoint loop, flow_loaded_kind() transitions its result for sub-8-byte
non-scalar context loads from FLOW_IS_OTHER (0) back up to FLOW_IS_PLAIN (1),
making the transfer function non-monotone.
> + valid = env->ops->is_valid_access &&
> + env->ops->is_valid_access(insn->off, size, BPF_READ, env->prog, &info);
> + /* it notes what the prog reads, and this is a question */
> + env->prog->tstamp_type_access = tstamp;
> + return valid && info.reg_type == SCALAR_VALUE ? FLOW_IS_PLAIN : FLOW_IS_OTHER;
> +}
> +
> +/* What a helper or a kfunc returns */
> +static enum flow_kind flow_returned_kind(struct bpf_verifier_env *env, int idx)
> +{
> + struct bpf_insn_aux_data *aux = &env->insn_aux_data[idx];
> + struct bpf_insn *insn = &env->prog->insnsi[idx];
> + const struct bpf_func_proto *fn;
> + struct bpf_call_arg_meta meta;
> + const struct btf_type *t;
> +
> + if (bpf_pseudo_kfunc_call(insn)) {
> + if (bpf_fetch_kfunc_arg_meta(env, insn->imm, insn->off, &meta) < 0)
> + return FLOW_IS_OTHER;
> + t = btf_type_skip_modifiers(meta.btf, meta.func_proto->type, NULL);
> + if (!btf_type_is_ptr(t))
> + return FLOW_IS_PLAIN;
> + /* void * is a number, unless the verifier knows the kfunc. Such kfunc may fail. */
> + t = btf_type_skip_modifiers(meta.btf, t->type, NULL);
> + return btf_type_is_void(t) && !(meta.kfunc_flags & KF_RET_NULL) ? FLOW_IS_PLAIN :
> + FLOW_IS_OTHER;
[Severity: Medium]
Does flow_returned_kind() in kernel/bpf/flow.c misclassify the return value
of bpf_cast_to_kern_ctx() and bpf_rdonly_cast() as FLOW_IS_PLAIN?
Both kfuncs have a void * return type in BTF, but neither has KF_RET_NULL set
in common_btf_ids in kernel/bpf/helpers.c:
BTF_ID_FLAGS(func, bpf_cast_to_kern_ctx, KF_FASTCALL)
BTF_ID_FLAGS(func, bpf_rdonly_cast, KF_FASTCALL | KF_PERFMON)
In kernel/bpf/verifier.c, check_special_kfunc() sets R0 for these calls to
PTR_TO_BTF_ID (or PTR_TO_MEM):
} else if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_cast_to_kern_ctx])) {
mark_reg_known_zero(env, regs, BPF_REG_0);
regs[BPF_REG_0].type = PTR_TO_BTF_ID | PTR_TRUSTED;
regs[BPF_REG_0].btf = desc_btf;
regs[BPF_REG_0].btf_id = meta->ret_btf_id;
} else if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_rdonly_cast])) {
Because KF_RET_NULL is not set on either kfunc, flow_returned_kind() returns
FLOW_IS_PLAIN, which sets both FLOW_IS_PLAIN and FLOW_IS_VALUE on R0 and
causes the returned pointer and any pointers loaded through it (via
flow_loaded_kind() checking FLOW_IS_VALUE) to be treated as FLOW_IS_PLAIN,
allowing states with different constant offsets to be joined.
> + }
[ ... ]
> +static bool flow_kinds_step(struct bpf_verifier_env *env, struct flow_kinds *kinds, u16 *ret,
> + struct flow_kinds *k, int idx)
> +{
> + struct bpf_subprog_info *sub = env->subprog_info;
> + struct bpf_insn *insn = &env->prog->insnsi[idx];
> + u8 class = BPF_CLASS(insn->code), op = BPF_OP(insn->code);
> + u8 dst = insn->dst_reg, src = insn->src_reg;
> + bool wide = BPF_SIZE(insn->code) == BPF_DW && BPF_MODE(insn->code) == BPF_MEM &&
> + !(insn->off % BPF_REG_SIZE);
> + bool lost = false;
> + u16 regs = U16_MAX;
> + int i, s;
[ ... ]
> + } else if (class == BPF_LDX) {
> + s = flow_mem_place(insn, src);
> + if (wide && s >= 0)
> + flow_kind_copy(k, dst, k, s);
> + else
> + flow_kind_set(k, dst, flow_loaded_kind(env, k, insn, src));
> + } else if (class == BPF_STX || class == BPF_ST) {
> + i = bpf_atomic_load_reg(insn);
> + if (i >= 0)
> + flow_kind_set(k, i, flow_loaded_kind(env, k, insn,
> + insn->imm == BPF_LOAD_ACQ ? src : dst));
> + s = flow_mem_place(insn, dst);
> + if (class == BPF_STX && wide && s >= 0)
> + flow_kind_copy(k, s, k, src);
> + else if (s >= 0)
> + flow_kind_set(k, s, FLOW_IS_PLAIN);
[Severity: Medium]
What happens in flow_kinds_step() in kernel/bpf/flow.c if a 64-bit pointer is
spilled to a stack slot using a store-release instruction (BPF_STX |
BPF_ATOMIC | BPF_DW with imm == BPF_STORE_REL)?
In kernel/bpf/verifier.c, check_atomic_store() handles BPF_STORE_REL by
calling check_store_reg(env, insn, true), which spills a pointer register to
the stack on an aligned 8-byte write.
In flow_kinds_step(), wide requires BPF_MODE(insn->code) == BPF_MEM, which is
false for BPF_ATOMIC instructions. When a 64-bit BPF_STORE_REL writes a
non-arena pointer to a tracked stack slot (s >= 0), wide is false, so
flow_kinds_step() takes the else branch and sets slot s to FLOW_IS_PLAIN
instead of copying the kind from src. A subsequent 64-bit fill from that
stack slot will then classify the reloaded pointer register as FLOW_IS_PLAIN
(and similarly a 64-bit BPF_LOAD_ACQ from a stack slot will not copy the
stack slot kind).
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261105204513.2230344-1-alexei.starovoitov@gmail.com?part=9
next prev parent reply other threads:[~2026-11-05 21:40 UTC|newest]
Thread overview: 66+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-05 20:44 [PATCH bpf-next v2 00/35] bpf: A verifier that joins states Alexei Starovoitov
2026-11-05 20:44 ` [PATCH bpf-next v2 01/35] bpf: Add may_goto to the loops that the verifier marks Alexei Starovoitov
2026-11-05 21:14 ` sashiko-bot
2026-11-05 20:44 ` [PATCH bpf-next v2 02/35] bpf: Limit depth and time of recursive subprogs at run time Alexei Starovoitov
2026-11-05 21:35 ` bot+bpf-ci
2026-11-05 20:44 ` [PATCH bpf-next v2 03/35] bpf: Add union and widening of the range of a register Alexei Starovoitov
2026-11-05 21:34 ` bot+bpf-ci
2026-11-05 20:44 ` [PATCH bpf-next v2 04/35] bpf: Add join of registers and stack slots Alexei Starovoitov
2026-11-05 21:16 ` sashiko-bot
2026-11-05 21:35 ` bot+bpf-ci
2026-11-05 20:44 ` [PATCH bpf-next v2 05/35] bpf: Add join of verifier states Alexei Starovoitov
2026-11-05 21:35 ` bot+bpf-ci
2026-11-05 21:40 ` sashiko-bot
2026-11-05 20:44 ` [PATCH bpf-next v2 06/35] bpf: Tell which arguments of a call a join must leave alone Alexei Starovoitov
2026-11-05 20:44 ` [PATCH bpf-next v2 07/35] bpf: Add the data structures of the main pass over the control flow graph Alexei Starovoitov
2026-11-05 20:44 ` [PATCH bpf-next v2 08/35] bpf: Order the insns, find what stops a loop and where may_goto can go Alexei Starovoitov
2026-11-05 20:44 ` [PATCH bpf-next v2 09/35] bpf: Find the registers that hold a number or a pointer to arena Alexei Starovoitov
2026-11-05 21:35 ` bot+bpf-ci
2026-11-05 21:40 ` sashiko-bot [this message]
2026-11-05 20:44 ` [PATCH bpf-next v2 10/35] bpf: Find the registers and stack slots that later checks depend on Alexei Starovoitov
2026-11-05 21:35 ` bot+bpf-ci
2026-11-05 20:44 ` [PATCH bpf-next v2 11/35] bpf: Add the sets of states at an insn and the entries of functions Alexei Starovoitov
2026-11-05 20:44 ` [PATCH bpf-next v2 12/35] bpf: Compare states of different iterations of a loop Alexei Starovoitov
2026-11-05 20:44 ` [PATCH bpf-next v2 13/35] bpf: Decide which states are not joined Alexei Starovoitov
2026-11-05 21:35 ` bot+bpf-ci
2026-11-05 20:44 ` [PATCH bpf-next v2 14/35] bpf: Merge the state that arrives into the set at an insn Alexei Starovoitov
2026-11-05 21:35 ` bot+bpf-ci
2026-11-05 20:44 ` [PATCH bpf-next v2 15/35] bpf: Move states along edges, into loops and subprogs and out of them Alexei Starovoitov
2026-11-05 21:35 ` bot+bpf-ci
2026-11-05 20:44 ` [PATCH bpf-next v2 16/35] bpf: Make the state after a call from the caller's state and an exit state Alexei Starovoitov
2026-11-05 20:44 ` [PATCH bpf-next v2 17/35] bpf: Enter and leave loops in the main pass, build flow.c Alexei Starovoitov
2026-11-05 21:35 ` bot+bpf-ci
2026-11-05 20:44 ` [PATCH bpf-next v2 18/35] bpf: Tell the passes over the graph where an added may_goto goes Alexei Starovoitov
2026-11-05 22:19 ` sashiko-bot
2026-11-05 20:44 ` [PATCH bpf-next v2 19/35] bpf: Verify programs as a fixpoint over the control flow graph Alexei Starovoitov
2026-11-05 21:35 ` bot+bpf-ci
2026-11-05 22:45 ` sashiko-bot
2026-11-05 20:44 ` [PATCH bpf-next v2 20/35] bpf: Keep the count of a loop when a register has no closed form Alexei Starovoitov
2026-11-05 21:35 ` bot+bpf-ci
2026-11-05 20:44 ` [PATCH bpf-next v2 21/35] bpf: Verify static subprogs on their own Alexei Starovoitov
2026-11-05 21:51 ` bot+bpf-ci
2026-11-05 22:45 ` sashiko-bot
2026-11-05 20:44 ` [PATCH bpf-next v2 22/35] bpf: Allow recursive subprogs Alexei Starovoitov
2026-11-05 21:51 ` bot+bpf-ci
2026-11-05 23:09 ` sashiko-bot
2026-11-05 20:45 ` [PATCH bpf-next v2 23/35] bpf: Remove is_state_visited(), bpf_compute_scc() and iter.depth Alexei Starovoitov
2026-11-05 20:45 ` [PATCH bpf-next v2 24/35] bpf: Remove precision tracking Alexei Starovoitov
2026-11-05 20:45 ` [PATCH bpf-next v2 25/35] bpf: Remove prune points, levels of state comparison and pop_stack() Alexei Starovoitov
2026-11-05 20:45 ` [PATCH bpf-next v2 26/35] bpf: Update comments about paths, pruning and explored states Alexei Starovoitov
2026-11-05 20:45 ` [PATCH bpf-next v2 27/35] bpf: Track a pointer to one of a set of functions Alexei Starovoitov
2026-11-05 20:45 ` [PATCH bpf-next v2 28/35] bpf: callx reads what the subprogs whose address is taken read Alexei Starovoitov
2026-11-05 20:45 ` [PATCH bpf-next v2 29/35] selftests/bpf: Adjust tests to log lines that are gone Alexei Starovoitov
2026-11-05 21:35 ` bot+bpf-ci
2026-11-05 20:45 ` [PATCH bpf-next v2 30/35] selftests/bpf: Adjust tests to new verdicts, messages and order Alexei Starovoitov
2026-11-05 21:35 ` bot+bpf-ci
2026-11-05 20:45 ` [PATCH bpf-next v2 31/35] selftests/bpf: Test states that are not joined Alexei Starovoitov
2026-11-05 21:35 ` bot+bpf-ci
2026-11-05 20:45 ` [PATCH bpf-next v2 32/35] selftests/bpf: Test static subprogs that are verified on their own Alexei Starovoitov
2026-11-05 21:35 ` bot+bpf-ci
2026-11-05 20:45 ` [PATCH bpf-next v2 33/35] selftests/bpf: Test loops that are counted, walked or widened Alexei Starovoitov
2026-11-05 20:45 ` [PATCH bpf-next v2 34/35] selftests/bpf: Test progs that run out of budget Alexei Starovoitov
2026-11-05 21:35 ` bot+bpf-ci
2026-11-05 23:19 ` sashiko-bot
2026-11-05 20:45 ` [PATCH bpf-next v2 35/35] selftests/bpf: Test callx of one of a set of functions Alexei Starovoitov
2026-11-05 23:17 ` sashiko-bot
2026-11-06 0:34 ` [PATCH bpf-next v2 00/35] bpf: A verifier that joins states Alexei Starovoitov
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=sashiko-outbox-200251@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=alexei.starovoitov@gmail.com \
--cc=bpf@vger.kernel.org \
--cc=sashiko-reviews@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox