Skip to content

Commit 4d2705e

Browse files
committed
bpf: remove {update,get}_loop_entry functions
Previous patch switched read and precision tracking logic for iterator-based loops from loops tracking on state graph to loops tracking on control flow graph. This patch removes {update,get}_loop_entry functions used for state graph loops tracking. Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
1 parent 49f23f4 commit 4d2705e

File tree

2 files changed

+2
-191
lines changed

2 files changed

+2
-191
lines changed

include/linux/bpf_verifier.h

-15
Original file line numberDiff line numberDiff line change
@@ -445,16 +445,6 @@ struct bpf_verifier_state {
445445
/* first and last insn idx of this verifier state */
446446
u32 first_insn_idx;
447447
u32 last_insn_idx;
448-
/* If this state is a part of states loop this field points to some
449-
* parent of this state such that:
450-
* - it is also a member of the same states loop;
451-
* - DFS states traversal starting from initial state visits loop_entry
452-
* state before this state.
453-
* Used to compute topmost loop entry for state loops.
454-
* State loops might appear because of open coded iterators logic.
455-
* See get_loop_entry() for more information.
456-
*/
457-
struct bpf_verifier_state *loop_entry;
458448
/* Sub-range of env->insn_hist[] corresponding to this state's
459449
* instruction history.
460450
* Backtracking is using it to go from last to first.
@@ -466,11 +456,6 @@ struct bpf_verifier_state {
466456
u32 dfs_depth;
467457
u32 callback_unroll_depth;
468458
u32 may_goto_depth;
469-
/* If this state was ever pointed-to by other state's loop_entry field
470-
* this flag would be set to true. Used to avoid freeing such states
471-
* while they are still in use.
472-
*/
473-
u32 used_as_loop_entry;
474459
/*
475460
* If this state is a checkpoint at insn_idx that belongs to an SCC,
476461
* record the SCC epoch at the time of checkpoint creation.

kernel/bpf/verifier.c

+2-176
Original file line numberDiff line numberDiff line change
@@ -1672,7 +1672,7 @@ static void free_verifier_state(struct bpf_verifier_state *state,
16721672
kfree(state);
16731673
}
16741674

1675-
/* struct bpf_verifier_state->{parent,loop_entry} refer to states
1675+
/* struct bpf_verifier_state->parent refers to states
16761676
* that are in either of env->{expored_states,free_list}.
16771677
* In both cases the state is contained in struct bpf_verifier_state_list.
16781678
*/
@@ -1683,36 +1683,18 @@ static struct bpf_verifier_state_list *state_parent_as_list(struct bpf_verifier_
16831683
return NULL;
16841684
}
16851685

1686-
static struct bpf_verifier_state_list *state_loop_entry_as_list(struct bpf_verifier_state *st)
1687-
{
1688-
if (st->loop_entry)
1689-
return container_of(st->loop_entry, struct bpf_verifier_state_list, state);
1690-
return NULL;
1691-
}
1692-
16931686
/* A state can be freed if it is no longer referenced:
16941687
* - is in the env->free_list;
16951688
* - has no children states;
1696-
* - is not used as loop_entry.
1697-
*
1698-
* Freeing a state can make it's loop_entry free-able.
16991689
*/
17001690
static void maybe_free_verifier_state(struct bpf_verifier_env *env,
17011691
struct bpf_verifier_state_list *sl)
17021692
{
1703-
struct bpf_verifier_state_list *loop_entry_sl;
1704-
1705-
while (sl && sl->in_free_list &&
1706-
sl->state.branches == 0 &&
1707-
sl->state.used_as_loop_entry == 0) {
1708-
loop_entry_sl = state_loop_entry_as_list(&sl->state);
1709-
if (loop_entry_sl)
1710-
loop_entry_sl->state.used_as_loop_entry--;
1693+
if (sl->in_free_list && sl->state.branches == 0) {
17111694
list_del(&sl->node);
17121695
free_verifier_state(&sl->state, false);
17131696
kfree(sl);
17141697
env->free_list_size--;
1715-
sl = loop_entry_sl;
17161698
}
17171699
}
17181700

@@ -1753,9 +1735,7 @@ static int copy_verifier_state(struct bpf_verifier_state *dst_state,
17531735
dst_state->insn_hist_end = src->insn_hist_end;
17541736
dst_state->dfs_depth = src->dfs_depth;
17551737
dst_state->callback_unroll_depth = src->callback_unroll_depth;
1756-
dst_state->used_as_loop_entry = src->used_as_loop_entry;
17571738
dst_state->may_goto_depth = src->may_goto_depth;
1758-
dst_state->loop_entry = src->loop_entry;
17591739
dst_state->scc_epoch = src->scc_epoch;
17601740
for (i = 0; i <= src->curframe; i++) {
17611741
dst = dst_state->frame[i];
@@ -1858,160 +1838,6 @@ static void mark_state_loops_possible(struct bpf_verifier_env *env,
18581838
scc_info->state_loops_possible = 1;
18591839
}
18601840

1861-
/* Open coded iterators allow back-edges in the state graph in order to
1862-
* check unbounded loops that iterators.
1863-
*
1864-
* In is_state_visited() it is necessary to know if explored states are
1865-
* part of some loops in order to decide whether non-exact states
1866-
* comparison could be used:
1867-
* - non-exact states comparison establishes sub-state relation and uses
1868-
* read and precision marks to do so, these marks are propagated from
1869-
* children states and thus are not guaranteed to be final in a loop;
1870-
* - exact states comparison just checks if current and explored states
1871-
* are identical (and thus form a back-edge).
1872-
*
1873-
* Paper "A New Algorithm for Identifying Loops in Decompilation"
1874-
* by Tao Wei, Jian Mao, Wei Zou and Yu Chen [1] presents a convenient
1875-
* algorithm for loop structure detection and gives an overview of
1876-
* relevant terminology. It also has helpful illustrations.
1877-
*
1878-
* [1] https://api.semanticscholar.org/CorpusID:15784067
1879-
*
1880-
* We use a similar algorithm but because loop nested structure is
1881-
* irrelevant for verifier ours is significantly simpler and resembles
1882-
* strongly connected components algorithm from Sedgewick's textbook.
1883-
*
1884-
* Define topmost loop entry as a first node of the loop traversed in a
1885-
* depth first search starting from initial state. The goal of the loop
1886-
* tracking algorithm is to associate topmost loop entries with states
1887-
* derived from these entries.
1888-
*
1889-
* For each step in the DFS states traversal algorithm needs to identify
1890-
* the following situations:
1891-
*
1892-
* initial initial initial
1893-
* | | |
1894-
* V V V
1895-
* ... ... .---------> hdr
1896-
* | | | |
1897-
* V V | V
1898-
* cur .-> succ | .------...
1899-
* | | | | | |
1900-
* V | V | V V
1901-
* succ '-- cur | ... ...
1902-
* | | |
1903-
* | V V
1904-
* | succ <- cur
1905-
* | |
1906-
* | V
1907-
* | ...
1908-
* | |
1909-
* '----'
1910-
*
1911-
* (A) successor state of cur (B) successor state of cur or it's entry
1912-
* not yet traversed are in current DFS path, thus cur and succ
1913-
* are members of the same outermost loop
1914-
*
1915-
* initial initial
1916-
* | |
1917-
* V V
1918-
* ... ...
1919-
* | |
1920-
* V V
1921-
* .------... .------...
1922-
* | | | |
1923-
* V V V V
1924-
* .-> hdr ... ... ...
1925-
* | | | | |
1926-
* | V V V V
1927-
* | succ <- cur succ <- cur
1928-
* | | |
1929-
* | V V
1930-
* | ... ...
1931-
* | | |
1932-
* '----' exit
1933-
*
1934-
* (C) successor state of cur is a part of some loop but this loop
1935-
* does not include cur or successor state is not in a loop at all.
1936-
*
1937-
* Algorithm could be described as the following python code:
1938-
*
1939-
* traversed = set() # Set of traversed nodes
1940-
* entries = {} # Mapping from node to loop entry
1941-
* depths = {} # Depth level assigned to graph node
1942-
* path = set() # Current DFS path
1943-
*
1944-
* # Find outermost loop entry known for n
1945-
* def get_loop_entry(n):
1946-
* h = entries.get(n, None)
1947-
* while h in entries:
1948-
* h = entries[h]
1949-
* return h
1950-
*
1951-
* # Update n's loop entry if h comes before n in current DFS path.
1952-
* def update_loop_entry(n, h):
1953-
* if h in path and depths[entries.get(n, n)] < depths[n]:
1954-
* entries[n] = h1
1955-
*
1956-
* def dfs(n, depth):
1957-
* traversed.add(n)
1958-
* path.add(n)
1959-
* depths[n] = depth
1960-
* for succ in G.successors(n):
1961-
* if succ not in traversed:
1962-
* # Case A: explore succ and update cur's loop entry
1963-
* # only if succ's entry is in current DFS path.
1964-
* dfs(succ, depth + 1)
1965-
* h = entries.get(succ, None)
1966-
* update_loop_entry(n, h)
1967-
* else:
1968-
* # Case B or C depending on `h1 in path` check in update_loop_entry().
1969-
* update_loop_entry(n, succ)
1970-
* path.remove(n)
1971-
*
1972-
* To adapt this algorithm for use with verifier:
1973-
* - use st->branch == 0 as a signal that DFS of succ had been finished
1974-
* and cur's loop entry has to be updated (case A), handle this in
1975-
* update_branch_counts();
1976-
* - use st->branch > 0 as a signal that st is in the current DFS path;
1977-
* - handle cases B and C in is_state_visited().
1978-
*/
1979-
static struct bpf_verifier_state *get_loop_entry(struct bpf_verifier_env *env,
1980-
struct bpf_verifier_state *st)
1981-
{
1982-
struct bpf_verifier_state *topmost = st->loop_entry;
1983-
u32 steps = 0;
1984-
1985-
while (topmost && topmost->loop_entry) {
1986-
if (steps++ > st->dfs_depth) {
1987-
WARN_ONCE(true, "verifier bug: infinite loop in get_loop_entry\n");
1988-
verbose(env, "verifier bug: infinite loop in get_loop_entry()\n");
1989-
return ERR_PTR(-EFAULT);
1990-
}
1991-
topmost = topmost->loop_entry;
1992-
}
1993-
return topmost;
1994-
}
1995-
1996-
static void update_loop_entry(struct bpf_verifier_env *env,
1997-
struct bpf_verifier_state *cur, struct bpf_verifier_state *hdr)
1998-
{
1999-
/* The hdr->branches check decides between cases B and C in
2000-
* comment for get_loop_entry(). If hdr->branches == 0 then
2001-
* head's topmost loop entry is not in current DFS path,
2002-
* hence 'cur' and 'hdr' are not in the same loop and there is
2003-
* no need to update cur->loop_entry.
2004-
*/
2005-
if (hdr->branches && hdr->dfs_depth < (cur->loop_entry ?: cur)->dfs_depth) {
2006-
if (cur->loop_entry) {
2007-
cur->loop_entry->used_as_loop_entry--;
2008-
maybe_free_verifier_state(env, state_loop_entry_as_list(cur));
2009-
}
2010-
cur->loop_entry = hdr;
2011-
hdr->used_as_loop_entry++;
2012-
}
2013-
}
2014-
20151841
static void update_branch_counts(struct bpf_verifier_env *env, struct bpf_verifier_state *st)
20161842
{
20171843
struct bpf_verifier_state_list *sl = NULL, *parent_sl;

0 commit comments

Comments
 (0)