From: Hao Sun <sunhao.th@gmail.com>
To: bpf@vger.kernel.org
Cc: ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org,
eddyz87@gmail.com, john.fastabend@gmail.com,
martin.lau@linux.dev, song@kernel.org, yonghong.song@linux.dev,
linux-kernel@vger.kernel.org, sunhao.th@gmail.com,
Hao Sun <hao.sun@inf.ethz.ch>
Subject: [PATCH RFC 12/17] bpf: Track path constraint
Date: Thu, 6 Nov 2025 13:52:50 +0100 [thread overview]
Message-ID: <20251106125255.1969938-13-hao.sun@inf.ethz.ch> (raw)
In-Reply-To: <20251106125255.1969938-1-hao.sun@inf.ethz.ch>
Record per-branch conditions during `bcf_track()` and build a single
conjunction to represent the path suffix constraint.
- Add `record_path_cond()`: after processing each instruction under tracking,
examine the previous instruction; if it is a conditional jump over scalars,
construct a boolean condition that matches the taken/not-taken edge, and then
append the condition id to `env->bcf.br_conds`.
- When tracking completes, if there are recorded conditions, build
`env->bcf.path_cond` as either the single condition or a BCF_BOOL|BCF_CONJ
of all collected conditions.
- In `bcf_refine()`, if both `path_cond` and a refinement-specific
`refine_cond` exist, combine them via a 2-ary conjunction so userspace proves
exactly the path-specific condition.
Signed-off-by: Hao Sun <hao.sun@inf.ethz.ch>
---
kernel/bpf/verifier.c | 113 +++++++++++++++++++++++++++++++++++++++---
1 file changed, 107 insertions(+), 6 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 3f2981db1d40..f1e8e70f9f61 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -20589,6 +20589,70 @@ static int bcf_match_path(struct bpf_verifier_env *env)
return PATH_MATCH;
}
+static int record_path_cond(struct bpf_verifier_env *env)
+{
+ int prev_insn_idx = env->prev_insn_idx;
+ struct bpf_reg_state *regs = cur_regs(env);
+ struct bpf_reg_state *dst, *src;
+ int dst_expr, src_expr;
+ struct bpf_insn *insn;
+ u8 class, op, bits;
+ bool jmp32, non_taken;
+ int cond_expr;
+
+ if (prev_insn_idx < 0)
+ return 0;
+
+ insn = &env->prog->insnsi[prev_insn_idx];
+ class = BPF_CLASS(insn->code);
+ op = BPF_OP(insn->code);
+ if (class != BPF_JMP && class != BPF_JMP32)
+ return 0;
+ if (op == BPF_CALL || op == BPF_EXIT || op == BPF_JA || op == BPF_JCOND)
+ return 0;
+ if (insn->off == 0)
+ return 0;
+
+ dst = regs + insn->dst_reg;
+ src = regs + insn->src_reg;
+ if (BPF_SRC(insn->code) == BPF_K) {
+ src = &env->fake_reg[0];
+ memset(src, 0, sizeof(*src));
+ src->type = SCALAR_VALUE;
+ __mark_reg_known(src, insn->imm);
+ }
+ if (dst->type != SCALAR_VALUE || src->type != SCALAR_VALUE)
+ return 0;
+
+ jmp32 = (class == BPF_JMP32);
+ bits = jmp32 ? 32 : 64;
+ dst_expr = bcf_reg_expr(env, dst, jmp32);
+ src_expr = bcf_reg_expr(env, src, jmp32);
+ if (dst_expr < 0 || src_expr < 0)
+ return -ENOMEM;
+
+ non_taken = (prev_insn_idx + 1 == env->insn_idx);
+ if (op == BPF_JSET) {
+ int and_expr, zero_expr;
+
+ and_expr = bcf_build_expr(env, BCF_BV | BPF_AND, bits, 2,
+ dst_expr, src_expr);
+ zero_expr = bcf_val(env, 0, jmp32);
+ op = BPF_JNE;
+ if (non_taken)
+ op = BPF_JEQ;
+ cond_expr = bcf_build_expr(env, BCF_BOOL | op, 0, 2,
+ and_expr, zero_expr);
+ } else {
+ if (non_taken)
+ op = rev_opcode(op);
+ cond_expr = bcf_build_expr(env, BCF_BOOL | op, 0, 2, dst_expr,
+ src_expr);
+ }
+
+ return bcf_add_cond(env, cond_expr);
+}
+
static int do_check(struct bpf_verifier_env *env)
{
bool pop_log = !(env->log.level & BPF_LOG_LEVEL2);
@@ -20656,8 +20720,9 @@ static int do_check(struct bpf_verifier_env *env)
if (path == PATH_MISMATCH)
goto process_bpf_exit;
- else if (path == PATH_DONE)
- return 0;
+ err = record_path_cond(env);
+ if (err || path == PATH_DONE)
+ return err;
}
if (signal_pending(current))
@@ -24023,11 +24088,37 @@ static int bcf_track(struct bpf_verifier_env *env,
if (!err && !same_callsites(env->cur_state, st))
err = -EFAULT;
- if (!err) {
- tracked_regs = cur_regs(env);
- for (i = 0; i < BPF_REG_FP; i++)
- regs[i].bcf_expr = tracked_regs[i].bcf_expr;
+ if (err)
+ goto out;
+
+ tracked_regs = cur_regs(env);
+ for (i = 0; i < BPF_REG_FP; i++)
+ regs[i].bcf_expr = tracked_regs[i].bcf_expr;
+
+ /* Build the path constraint. */
+ if (bcf->br_cond_cnt == 1) {
+ bcf->path_cond = *bcf->br_conds;
+ } else if (bcf->br_cond_cnt > 1) {
+ struct bcf_expr *cond_expr;
+ int cond;
+
+ cond = bcf_alloc_expr(env, bcf->br_cond_cnt + 1);
+ if (cond < 0) {
+ err = cond;
+ goto out;
+ }
+ cond_expr = bcf->exprs + cond;
+ cond_expr->code = BCF_BOOL | BCF_CONJ;
+ cond_expr->params = 0;
+ cond_expr->vlen = bcf->br_cond_cnt;
+ memcpy(cond_expr->args, bcf->br_conds,
+ sizeof(u32) * bcf->br_cond_cnt);
+ bcf->path_cond = cond;
}
+out:
+ kfree(bcf->br_conds);
+ bcf->br_conds = NULL;
+ bcf->br_cond_cnt = 0;
free_verifier_state(env->cur_state, true);
env->cur_state = NULL;
@@ -24134,6 +24225,7 @@ static int __used bcf_refine(struct bpf_verifier_env *env,
refine_state_fn refine_cb, void *ctx)
{
struct bpf_reg_state *regs = st->frame[st->curframe]->regs;
+ struct bcf_refine_state *bcf = &env->bcf;
struct bpf_verifier_state *base;
int i, err;
@@ -24168,6 +24260,15 @@ static int __used bcf_refine(struct bpf_verifier_env *env,
if (!err && refine_cb)
err = refine_cb(env, st, ctx);
+ /* The final condition is the conj of path_cond and refine_cond. */
+ if (!err && bcf->refine_cond >= 0 && bcf->path_cond >= 0) {
+ bcf->refine_cond = bcf_build_expr(env, BCF_BOOL | BCF_CONJ, 0,
+ 2, bcf->path_cond,
+ bcf->refine_cond);
+ if (bcf->refine_cond < 0)
+ err = bcf->refine_cond;
+ }
+
if (!err && (env->bcf.refine_cond >= 0 || env->bcf.path_cond >= 0))
mark_bcf_requested(env);
--
2.34.1
next prev parent reply other threads:[~2025-11-06 12:53 UTC|newest]
Thread overview: 18+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-11-06 12:52 [PATCH RFC 00/17] bpf: Introduce proof-based verifier enhancement Hao Sun
2025-11-06 12:52 ` [PATCH RFC 01/17] bpf: Add BCF expr and proof rule definitions Hao Sun
2025-11-06 12:52 ` [PATCH RFC 02/17] bpf: Add bcf_checker top-level workflow Hao Sun
2025-11-06 12:52 ` [PATCH RFC 03/17] bpf: Add UAPI fields for BCF proof interaction Hao Sun
2025-11-06 12:52 ` [PATCH RFC 04/17] bpf: Add top-level workflow of bcf_refine() Hao Sun
2025-11-06 12:52 ` [PATCH RFC 05/17] bpf: Add top-level workflow of bcf_track() Hao Sun
2025-11-06 12:52 ` [PATCH RFC 06/17] bpf: Add bcf_match_path() to follow the path suffix Hao Sun
2025-11-06 12:52 ` [PATCH RFC 07/17] bpf: Add bcf_expr management and binding Hao Sun
2025-11-06 12:52 ` [PATCH RFC 08/17] bpf: Track mov and signed extension Hao Sun
2025-11-06 12:52 ` [PATCH RFC 09/17] bpf: Track alu operations in bcf_track() Hao Sun
2025-11-06 12:52 ` [PATCH RFC 10/17] bpf: Add bcf_alu() 32bits optimization Hao Sun
2025-11-06 12:52 ` [PATCH RFC 11/17] bpf: Track stack spill/fill in bcf_track() Hao Sun
2025-11-06 12:52 ` Hao Sun [this message]
2025-11-06 12:52 ` [PATCH RFC 13/17] bpf: Skip state pruning for the parent states Hao Sun
2025-11-06 12:52 ` [PATCH RFC 14/17] bpf: Add mem access bound refinement Hao Sun
2025-11-06 12:52 ` [PATCH RFC 15/17] bpf: Preserve verifier_env and request BCF Hao Sun
2025-11-06 12:52 ` [PATCH RFC 16/17] bpf: Resume verifier env and check proof Hao Sun
2025-11-06 12:52 ` [PATCH RFC 17/17] bpf: Enable bcf for priv users Hao Sun
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=20251106125255.1969938-13-hao.sun@inf.ethz.ch \
--to=sunhao.th@gmail.com \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=eddyz87@gmail.com \
--cc=hao.sun@inf.ethz.ch \
--cc=john.fastabend@gmail.com \
--cc=linux-kernel@vger.kernel.org \
--cc=martin.lau@linux.dev \
--cc=song@kernel.org \
--cc=yonghong.song@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;
as well as URLs for NNTP newsgroup(s).