@@ -1803,6 +1803,7 @@ static int copy_verifier_state(struct bpf_verifier_state *dst_state,
1803
1803
dst_state->first_insn_idx = src->first_insn_idx;
1804
1804
dst_state->last_insn_idx = src->last_insn_idx;
1805
1805
dst_state->dfs_depth = src->dfs_depth;
1806
+ dst_state->used_as_loop_entry = src->used_as_loop_entry;
1806
1807
for (i = 0; i <= src->curframe; i++) {
1807
1808
dst = dst_state->frame[i];
1808
1809
if (!dst) {
@@ -1845,11 +1846,176 @@ static bool same_callsites(struct bpf_verifier_state *a, struct bpf_verifier_sta
1845
1846
return true;
1846
1847
}
1847
1848
1849
+ /* Open coded iterators allow back-edges in the state graph in order to
1850
+ * check unbounded loops that iterators.
1851
+ *
1852
+ * In is_state_visited() it is necessary to know if explored states are
1853
+ * part of some loops in order to decide whether non-exact states
1854
+ * comparison could be used:
1855
+ * - non-exact states comparison establishes sub-state relation and uses
1856
+ * read and precision marks to do so, these marks are propagated from
1857
+ * children states and thus are not guaranteed to be final in a loop;
1858
+ * - exact states comparison just checks if current and explored states
1859
+ * are identical (and thus form a back-edge).
1860
+ *
1861
+ * Paper "A New Algorithm for Identifying Loops in Decompilation"
1862
+ * by Tao Wei, Jian Mao, Wei Zou and Yu Chen [1] presents a convenient
1863
+ * algorithm for loop structure detection and gives an overview of
1864
+ * relevant terminology. It also has helpful illustrations.
1865
+ *
1866
+ * [1] https://api.semanticscholar.org/CorpusID:15784067
1867
+ *
1868
+ * We use a similar algorithm but because loop nested structure is
1869
+ * irrelevant for verifier ours is significantly simpler and resembles
1870
+ * strongly connected components algorithm from Sedgewick's textbook.
1871
+ *
1872
+ * Define topmost loop entry as a first node of the loop traversed in a
1873
+ * depth first search starting from initial state. The goal of the loop
1874
+ * tracking algorithm is to associate topmost loop entries with states
1875
+ * derived from these entries.
1876
+ *
1877
+ * For each step in the DFS states traversal algorithm needs to identify
1878
+ * the following situations:
1879
+ *
1880
+ * initial initial initial
1881
+ * | | |
1882
+ * V V V
1883
+ * ... ... .---------> hdr
1884
+ * | | | |
1885
+ * V V | V
1886
+ * cur .-> succ | .------...
1887
+ * | | | | | |
1888
+ * V | V | V V
1889
+ * succ '-- cur | ... ...
1890
+ * | | |
1891
+ * | V V
1892
+ * | succ <- cur
1893
+ * | |
1894
+ * | V
1895
+ * | ...
1896
+ * | |
1897
+ * '----'
1898
+ *
1899
+ * (A) successor state of cur (B) successor state of cur or it's entry
1900
+ * not yet traversed are in current DFS path, thus cur and succ
1901
+ * are members of the same outermost loop
1902
+ *
1903
+ * initial initial
1904
+ * | |
1905
+ * V V
1906
+ * ... ...
1907
+ * | |
1908
+ * V V
1909
+ * .------... .------...
1910
+ * | | | |
1911
+ * V V V V
1912
+ * .-> hdr ... ... ...
1913
+ * | | | | |
1914
+ * | V V V V
1915
+ * | succ <- cur succ <- cur
1916
+ * | | |
1917
+ * | V V
1918
+ * | ... ...
1919
+ * | | |
1920
+ * '----' exit
1921
+ *
1922
+ * (C) successor state of cur is a part of some loop but this loop
1923
+ * does not include cur or successor state is not in a loop at all.
1924
+ *
1925
+ * Algorithm could be described as the following python code:
1926
+ *
1927
+ * traversed = set() # Set of traversed nodes
1928
+ * entries = {} # Mapping from node to loop entry
1929
+ * depths = {} # Depth level assigned to graph node
1930
+ * path = set() # Current DFS path
1931
+ *
1932
+ * # Find outermost loop entry known for n
1933
+ * def get_loop_entry(n):
1934
+ * h = entries.get(n, None)
1935
+ * while h in entries and entries[h] != h:
1936
+ * h = entries[h]
1937
+ * return h
1938
+ *
1939
+ * # Update n's loop entry if h's outermost entry comes
1940
+ * # before n's outermost entry in current DFS path.
1941
+ * def update_loop_entry(n, h):
1942
+ * n1 = get_loop_entry(n) or n
1943
+ * h1 = get_loop_entry(h) or h
1944
+ * if h1 in path and depths[h1] <= depths[n1]:
1945
+ * entries[n] = h1
1946
+ *
1947
+ * def dfs(n, depth):
1948
+ * traversed.add(n)
1949
+ * path.add(n)
1950
+ * depths[n] = depth
1951
+ * for succ in G.successors(n):
1952
+ * if succ not in traversed:
1953
+ * # Case A: explore succ and update cur's loop entry
1954
+ * # only if succ's entry is in current DFS path.
1955
+ * dfs(succ, depth + 1)
1956
+ * h = get_loop_entry(succ)
1957
+ * update_loop_entry(n, h)
1958
+ * else:
1959
+ * # Case B or C depending on `h1 in path` check in update_loop_entry().
1960
+ * update_loop_entry(n, succ)
1961
+ * path.remove(n)
1962
+ *
1963
+ * To adapt this algorithm for use with verifier:
1964
+ * - use st->branch == 0 as a signal that DFS of succ had been finished
1965
+ * and cur's loop entry has to be updated (case A), handle this in
1966
+ * update_branch_counts();
1967
+ * - use st->branch > 0 as a signal that st is in the current DFS path;
1968
+ * - handle cases B and C in is_state_visited();
1969
+ * - update topmost loop entry for intermediate states in get_loop_entry().
1970
+ */
1971
+ static struct bpf_verifier_state *get_loop_entry(struct bpf_verifier_state *st)
1972
+ {
1973
+ struct bpf_verifier_state *topmost = st->loop_entry, *old;
1974
+
1975
+ while (topmost && topmost->loop_entry && topmost != topmost->loop_entry)
1976
+ topmost = topmost->loop_entry;
1977
+ /* Update loop entries for intermediate states to avoid this
1978
+ * traversal in future get_loop_entry() calls.
1979
+ */
1980
+ while (st && st->loop_entry != topmost) {
1981
+ old = st->loop_entry;
1982
+ st->loop_entry = topmost;
1983
+ st = old;
1984
+ }
1985
+ return topmost;
1986
+ }
1987
+
1988
+ static void update_loop_entry(struct bpf_verifier_state *cur, struct bpf_verifier_state *hdr)
1989
+ {
1990
+ struct bpf_verifier_state *cur1, *hdr1;
1991
+
1992
+ cur1 = get_loop_entry(cur) ?: cur;
1993
+ hdr1 = get_loop_entry(hdr) ?: hdr;
1994
+ /* The head1->branches check decides between cases B and C in
1995
+ * comment for get_loop_entry(). If hdr1->branches == 0 then
1996
+ * head's topmost loop entry is not in current DFS path,
1997
+ * hence 'cur' and 'hdr' are not in the same loop and there is
1998
+ * no need to update cur->loop_entry.
1999
+ */
2000
+ if (hdr1->branches && hdr1->dfs_depth <= cur1->dfs_depth) {
2001
+ cur->loop_entry = hdr;
2002
+ hdr->used_as_loop_entry = true;
2003
+ }
2004
+ }
2005
+
1848
2006
static void update_branch_counts(struct bpf_verifier_env *env, struct bpf_verifier_state *st)
1849
2007
{
1850
2008
while (st) {
1851
2009
u32 br = --st->branches;
1852
2010
2011
+ /* br == 0 signals that DFS exploration for 'st' is finished,
2012
+ * thus it is necessary to update parent's loop entry if it
2013
+ * turned out that st is a part of some loop.
2014
+ * This is a part of 'case A' in get_loop_entry() comment.
2015
+ */
2016
+ if (br == 0 && st->parent && st->loop_entry)
2017
+ update_loop_entry(st->parent, st->loop_entry);
2018
+
1853
2019
/* WARN_ON(br > 1) technically makes sense here,
1854
2020
* but see comment in push_stack(), hence:
1855
2021
*/
@@ -16650,10 +16816,11 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx)
16650
16816
{
16651
16817
struct bpf_verifier_state_list *new_sl;
16652
16818
struct bpf_verifier_state_list *sl, **pprev;
16653
- struct bpf_verifier_state *cur = env->cur_state, *new;
16819
+ struct bpf_verifier_state *cur = env->cur_state, *new, *loop_entry ;
16654
16820
int i, j, n, err, states_cnt = 0;
16655
16821
bool force_new_state = env->test_state_freq || is_force_checkpoint(env, insn_idx);
16656
16822
bool add_new_state = force_new_state;
16823
+ bool force_exact;
16657
16824
16658
16825
/* bpf progs typically have pruning point every 4 instructions
16659
16826
* http://vger.kernel.org/bpfconf2019.html#session-1
@@ -16748,8 +16915,10 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx)
16748
16915
*/
16749
16916
spi = __get_spi(iter_reg->off + iter_reg->var_off.value);
16750
16917
iter_state = &func(env, iter_reg)->stack[spi].spilled_ptr;
16751
- if (iter_state->iter.state == BPF_ITER_STATE_ACTIVE)
16918
+ if (iter_state->iter.state == BPF_ITER_STATE_ACTIVE) {
16919
+ update_loop_entry(cur, &sl->state);
16752
16920
goto hit;
16921
+ }
16753
16922
}
16754
16923
goto skip_inf_loop_check;
16755
16924
}
@@ -16780,7 +16949,36 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx)
16780
16949
add_new_state = false;
16781
16950
goto miss;
16782
16951
}
16783
- if (states_equal(env, &sl->state, cur, false)) {
16952
+ /* If sl->state is a part of a loop and this loop's entry is a part of
16953
+ * current verification path then states have to be compared exactly.
16954
+ * 'force_exact' is needed to catch the following case:
16955
+ *
16956
+ * initial Here state 'succ' was processed first,
16957
+ * | it was eventually tracked to produce a
16958
+ * V state identical to 'hdr'.
16959
+ * .---------> hdr All branches from 'succ' had been explored
16960
+ * | | and thus 'succ' has its .branches == 0.
16961
+ * | V
16962
+ * | .------... Suppose states 'cur' and 'succ' correspond
16963
+ * | | | to the same instruction + callsites.
16964
+ * | V V In such case it is necessary to check
16965
+ * | ... ... if 'succ' and 'cur' are states_equal().
16966
+ * | | | If 'succ' and 'cur' are a part of the
16967
+ * | V V same loop exact flag has to be set.
16968
+ * | succ <- cur To check if that is the case, verify
16969
+ * | | if loop entry of 'succ' is in current
16970
+ * | V DFS path.
16971
+ * | ...
16972
+ * | |
16973
+ * '----'
16974
+ *
16975
+ * Additional details are in the comment before get_loop_entry().
16976
+ */
16977
+ loop_entry = get_loop_entry(&sl->state);
16978
+ force_exact = loop_entry && loop_entry->branches > 0;
16979
+ if (states_equal(env, &sl->state, cur, force_exact)) {
16980
+ if (force_exact)
16981
+ update_loop_entry(cur, loop_entry);
16784
16982
hit:
16785
16983
sl->hit_cnt++;
16786
16984
/* reached equivalent register/stack state,
@@ -16829,7 +17027,8 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx)
16829
17027
* speed up verification
16830
17028
*/
16831
17029
*pprev = sl->next;
16832
- if (sl->state.frame[0]->regs[0].live & REG_LIVE_DONE) {
17030
+ if (sl->state.frame[0]->regs[0].live & REG_LIVE_DONE &&
17031
+ !sl->state.used_as_loop_entry) {
16833
17032
u32 br = sl->state.branches;
16834
17033
16835
17034
WARN_ONCE(br,
0 commit comments