mirror of
https://github.com/torvalds/linux.git
synced 2026-06-02 11:33:28 +02:00
bpf: remove {update,get}_loop_entry functions
The previous patch switched read and precision tracking for iterator-based loops from state-graph-based loop tracking to control-flow-graph-based loop tracking. This patch removes the now-unused `update_loop_entry()` and `get_loop_entry()` functions, which were part of the state-graph-based logic. Signed-off-by: Eduard Zingerman <eddyz87@gmail.com> Link: https://lore.kernel.org/r/20250611200836.4135542-9-eddyz87@gmail.com Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
parent
c9e31900b5
commit
0e0da5f901
|
|
@ -449,16 +449,6 @@ struct bpf_verifier_state {
|
|||
/* first and last insn idx of this verifier state */
|
||||
u32 first_insn_idx;
|
||||
u32 last_insn_idx;
|
||||
/* If this state is a part of states loop this field points to some
|
||||
* parent of this state such that:
|
||||
* - it is also a member of the same states loop;
|
||||
* - DFS states traversal starting from initial state visits loop_entry
|
||||
* state before this state.
|
||||
* Used to compute topmost loop entry for state loops.
|
||||
* State loops might appear because of open coded iterators logic.
|
||||
* 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.
|
||||
*/
|
||||
|
|
@ -473,11 +463,6 @@ struct bpf_verifier_state {
|
|||
u32 dfs_depth;
|
||||
u32 callback_unroll_depth;
|
||||
u32 may_goto_depth;
|
||||
/* If this state was ever pointed-to by other state's loop_entry field
|
||||
* this flag would be set to true. Used to avoid freeing such states
|
||||
* while they are still in use.
|
||||
*/
|
||||
u32 used_as_loop_entry;
|
||||
};
|
||||
|
||||
#define bpf_get_spilled_reg(slot, frame, mask) \
|
||||
|
|
|
|||
|
|
@ -1682,7 +1682,7 @@ static void free_verifier_state(struct bpf_verifier_state *state,
|
|||
kfree(state);
|
||||
}
|
||||
|
||||
/* struct bpf_verifier_state->{parent,loop_entry} refer to states
|
||||
/* struct bpf_verifier_state->parent refers to states
|
||||
* that are in either of env->{expored_states,free_list}.
|
||||
* In both cases the state is contained in struct bpf_verifier_state_list.
|
||||
*/
|
||||
|
|
@ -1693,22 +1693,12 @@ static struct bpf_verifier_state_list *state_parent_as_list(struct bpf_verifier_
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static struct bpf_verifier_state_list *state_loop_entry_as_list(struct bpf_verifier_state *st)
|
||||
{
|
||||
if (st->loop_entry)
|
||||
return container_of(st->loop_entry, struct bpf_verifier_state_list, state);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool incomplete_read_marks(struct bpf_verifier_env *env,
|
||||
struct bpf_verifier_state *st);
|
||||
|
||||
/* A state can be freed if it is no longer referenced:
|
||||
* - is in the env->free_list;
|
||||
* - has no children states;
|
||||
* - is not used as loop_entry.
|
||||
*
|
||||
* Freeing a state can make it's loop_entry free-able.
|
||||
*/
|
||||
static void maybe_free_verifier_state(struct bpf_verifier_env *env,
|
||||
struct bpf_verifier_state_list *sl)
|
||||
|
|
@ -1765,9 +1755,7 @@ static int copy_verifier_state(struct bpf_verifier_state *dst_state,
|
|||
dst_state->last_insn_idx = src->last_insn_idx;
|
||||
dst_state->dfs_depth = src->dfs_depth;
|
||||
dst_state->callback_unroll_depth = src->callback_unroll_depth;
|
||||
dst_state->used_as_loop_entry = src->used_as_loop_entry;
|
||||
dst_state->may_goto_depth = src->may_goto_depth;
|
||||
dst_state->loop_entry = src->loop_entry;
|
||||
dst_state->equal_state = src->equal_state;
|
||||
for (i = 0; i <= src->curframe; i++) {
|
||||
dst = dst_state->frame[i];
|
||||
|
|
@ -1811,157 +1799,6 @@ static bool same_callsites(struct bpf_verifier_state *a, struct bpf_verifier_sta
|
|||
return true;
|
||||
}
|
||||
|
||||
/* Open coded iterators allow back-edges in the state graph in order to
|
||||
* check unbounded loops that iterators.
|
||||
*
|
||||
* In is_state_visited() it is necessary to know if explored states are
|
||||
* part of some loops in order to decide whether non-exact states
|
||||
* comparison could be used:
|
||||
* - non-exact states comparison establishes sub-state relation and uses
|
||||
* read and precision marks to do so, these marks are propagated from
|
||||
* children states and thus are not guaranteed to be final in a loop;
|
||||
* - exact states comparison just checks if current and explored states
|
||||
* are identical (and thus form a back-edge).
|
||||
*
|
||||
* Paper "A New Algorithm for Identifying Loops in Decompilation"
|
||||
* by Tao Wei, Jian Mao, Wei Zou and Yu Chen [1] presents a convenient
|
||||
* algorithm for loop structure detection and gives an overview of
|
||||
* relevant terminology. It also has helpful illustrations.
|
||||
*
|
||||
* [1] https://api.semanticscholar.org/CorpusID:15784067
|
||||
*
|
||||
* We use a similar algorithm but because loop nested structure is
|
||||
* irrelevant for verifier ours is significantly simpler and resembles
|
||||
* strongly connected components algorithm from Sedgewick's textbook.
|
||||
*
|
||||
* Define topmost loop entry as a first node of the loop traversed in a
|
||||
* depth first search starting from initial state. The goal of the loop
|
||||
* tracking algorithm is to associate topmost loop entries with states
|
||||
* derived from these entries.
|
||||
*
|
||||
* For each step in the DFS states traversal algorithm needs to identify
|
||||
* the following situations:
|
||||
*
|
||||
* initial initial initial
|
||||
* | | |
|
||||
* V V V
|
||||
* ... ... .---------> hdr
|
||||
* | | | |
|
||||
* V V | V
|
||||
* cur .-> succ | .------...
|
||||
* | | | | | |
|
||||
* V | V | V V
|
||||
* succ '-- cur | ... ...
|
||||
* | | |
|
||||
* | V V
|
||||
* | succ <- cur
|
||||
* | |
|
||||
* | V
|
||||
* | ...
|
||||
* | |
|
||||
* '----'
|
||||
*
|
||||
* (A) successor state of cur (B) successor state of cur or it's entry
|
||||
* not yet traversed are in current DFS path, thus cur and succ
|
||||
* are members of the same outermost loop
|
||||
*
|
||||
* initial initial
|
||||
* | |
|
||||
* V V
|
||||
* ... ...
|
||||
* | |
|
||||
* V V
|
||||
* .------... .------...
|
||||
* | | | |
|
||||
* V V V V
|
||||
* .-> hdr ... ... ...
|
||||
* | | | | |
|
||||
* | V V V V
|
||||
* | succ <- cur succ <- cur
|
||||
* | | |
|
||||
* | V V
|
||||
* | ... ...
|
||||
* | | |
|
||||
* '----' exit
|
||||
*
|
||||
* (C) successor state of cur is a part of some loop but this loop
|
||||
* does not include cur or successor state is not in a loop at all.
|
||||
*
|
||||
* Algorithm could be described as the following python code:
|
||||
*
|
||||
* traversed = set() # Set of traversed nodes
|
||||
* entries = {} # Mapping from node to loop entry
|
||||
* depths = {} # Depth level assigned to graph node
|
||||
* path = set() # Current DFS path
|
||||
*
|
||||
* # Find outermost loop entry known for n
|
||||
* def get_loop_entry(n):
|
||||
* h = entries.get(n, None)
|
||||
* while h in entries:
|
||||
* h = entries[h]
|
||||
* return h
|
||||
*
|
||||
* # Update n's loop entry if h comes before n in current DFS path.
|
||||
* def update_loop_entry(n, h):
|
||||
* if h in path and depths[entries.get(n, n)] < depths[n]:
|
||||
* entries[n] = h1
|
||||
*
|
||||
* def dfs(n, depth):
|
||||
* traversed.add(n)
|
||||
* path.add(n)
|
||||
* depths[n] = depth
|
||||
* for succ in G.successors(n):
|
||||
* if succ not in traversed:
|
||||
* # Case A: explore succ and update cur's loop entry
|
||||
* # only if succ's entry is in current DFS path.
|
||||
* dfs(succ, depth + 1)
|
||||
* h = entries.get(succ, None)
|
||||
* update_loop_entry(n, h)
|
||||
* else:
|
||||
* # Case B or C depending on `h1 in path` check in update_loop_entry().
|
||||
* update_loop_entry(n, succ)
|
||||
* path.remove(n)
|
||||
*
|
||||
* To adapt this algorithm for use with verifier:
|
||||
* - use st->branch == 0 as a signal that DFS of succ had been finished
|
||||
* and cur's loop entry has to be updated (case A), handle this in
|
||||
* update_branch_counts();
|
||||
* - use st->branch > 0 as a signal that st is in the current DFS path;
|
||||
* - handle cases B and C in is_state_visited().
|
||||
*/
|
||||
static struct bpf_verifier_state *get_loop_entry(struct bpf_verifier_env *env,
|
||||
struct bpf_verifier_state *st)
|
||||
{
|
||||
struct bpf_verifier_state *topmost = st->loop_entry;
|
||||
u32 steps = 0;
|
||||
|
||||
while (topmost && topmost->loop_entry) {
|
||||
if (verifier_bug_if(steps++ > st->dfs_depth, env, "infinite loop"))
|
||||
return ERR_PTR(-EFAULT);
|
||||
topmost = topmost->loop_entry;
|
||||
}
|
||||
return topmost;
|
||||
}
|
||||
|
||||
static void update_loop_entry(struct bpf_verifier_env *env,
|
||||
struct bpf_verifier_state *cur, struct bpf_verifier_state *hdr)
|
||||
{
|
||||
/* The hdr->branches check decides between cases B and C in
|
||||
* comment for get_loop_entry(). If hdr->branches == 0 then
|
||||
* head's topmost loop entry is not in current DFS path,
|
||||
* hence 'cur' and 'hdr' are not in the same loop and there is
|
||||
* no need to update cur->loop_entry.
|
||||
*/
|
||||
if (hdr->branches && hdr->dfs_depth < (cur->loop_entry ?: cur)->dfs_depth) {
|
||||
if (cur->loop_entry) {
|
||||
cur->loop_entry->used_as_loop_entry--;
|
||||
maybe_free_verifier_state(env, state_loop_entry_as_list(cur));
|
||||
}
|
||||
cur->loop_entry = hdr;
|
||||
hdr->used_as_loop_entry++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return IP for a given frame in a call stack */
|
||||
static u32 frame_insn_idx(struct bpf_verifier_state *st, u32 frame)
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user