Commit c9e31900 authored by Eduard Zingerman's avatar Eduard Zingerman Committed by Alexei Starovoitov
Browse files

bpf: propagate read/precision marks over state graph backedges



Current loop_entry-based exact states comparison logic does not handle
the following case:

 .-> A --.  Assume the states are visited in the order A, B, C.
 |   |   |  Assume that state B reaches a state equivalent to state A.
 |   v   v  At this point, state C is not processed yet, so state A
 '-- B   C  has not received any read or precision marks from C.
            As a result, these marks won't be propagated to B.

If B has incomplete marks, it is unsafe to use it in states_equal()
checks.

This commit replaces the existing logic with the following:
- Strongly connected components (SCCs) are computed over the program's
  control flow graph (intraprocedurally).
- When a verifier state enters an SCC, that state is recorded as the
  SCC entry point.
- When a verifier state is found equivalent to another (e.g., B to A
  in the example), it is recorded as a states graph backedge.
  Backedges are accumulated per SCC.
- When an SCC entry state reaches `branches == 0`, read and precision
  marks are propagated through the backedges (e.g., from A to B, from
  C to A, and then again from A to B).

To support nested subprogram calls, the entry state and backedge list
are associated not with the SCC itself but with an object called
`bpf_scc_callchain`. A callchain is a tuple `(callsite*, scc_id)`,
where `callsite` is the index of a call instruction for each frame
except the last.

See the comments added in `is_state_visited()` and
`compute_scc_callchain()` for more details.

Fixes: 2a099282 ("bpf: correct loop detection for iterators convergence")
Signed-off-by: default avatarEduard Zingerman <eddyz87@gmail.com>
Link: https://lore.kernel.org/r/20250611200836.4135542-8-eddyz87@gmail.com


Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parent b5c677d8
Loading
Loading
Loading
Loading
+38 −0
Original line number Diff line number Diff line
@@ -459,6 +459,10 @@ struct bpf_verifier_state {
	 * See get_loop_entry() for more information.
	 */
	struct bpf_verifier_state *loop_entry;
	/* if this state is a backedge state then equal_state
	 * records cached state to which this state is equal.
	 */
	struct bpf_verifier_state *equal_state;
	/* jmp history recorded from first to last.
	 * backtracking is using it to go from last to first.
	 * For most states jmp_history_cnt is [0-3].
@@ -723,6 +727,37 @@ struct bpf_idset {
	u32 ids[BPF_ID_MAP_SIZE];
};

/* see verifier.c:compute_scc_callchain() */
struct bpf_scc_callchain {
	/* call sites from bpf_verifier_state->frame[*]->callsite leading to this SCC */
	u32 callsites[MAX_CALL_FRAMES - 1];
	/* last frame in a chain is identified by SCC id */
	u32 scc;
};

/* verifier state waiting for propagate_backedges() */
struct bpf_scc_backedge {
	struct bpf_scc_backedge *next;
	struct bpf_verifier_state state;
};

struct bpf_scc_visit {
	struct bpf_scc_callchain callchain;
	/* first state in current verification path that entered SCC
	 * identified by the callchain
	 */
	struct bpf_verifier_state *entry_state;
	struct bpf_scc_backedge *backedges; /* list of backedges */
};

/* An array of bpf_scc_visit structs sharing tht same bpf_scc_callchain->scc
 * but having different bpf_scc_callchain->callsites.
 */
struct bpf_scc_info {
	u32 num_visits;
	struct bpf_scc_visit visits[];
};

/* single container for all structs
 * one verifier_env per bpf_check() call
 */
@@ -819,6 +854,9 @@ struct bpf_verifier_env {
	char tmp_str_buf[TMP_STR_BUF_LEN];
	struct bpf_insn insn_buf[INSN_BUF_SIZE];
	struct bpf_insn epilogue_buf[INSN_BUF_SIZE];
	/* array of pointers to bpf_scc_info indexed by SCC id */
	struct bpf_scc_info **scc_info;
	u32 scc_cnt;
};

static inline struct bpf_func_info_aux *subprog_aux(struct bpf_verifier_env *env, int subprog)
+384 −68

File changed.

Preview size limit exceeded, changes collapsed.