@@ -24013,6 +24013,10 @@ static int compute_live_registers(struct bpf_verifier_env *env)
24013
24013
if (env->log.level & BPF_LOG_LEVEL2) {
24014
24014
verbose(env, "Live regs before insn:\n");
24015
24015
for (i = 0; i < insn_cnt; ++i) {
24016
+ if (env->insn_aux_data[i].scc)
24017
+ verbose(env, "%3d ", env->insn_aux_data[i].scc);
24018
+ else
24019
+ verbose(env, " ");
24016
24020
verbose(env, "%3d: ", i);
24017
24021
for (j = BPF_REG_0; j < BPF_REG_10; ++j)
24018
24022
if (insn_aux[i].live_regs_before & BIT(j))
@@ -24034,6 +24038,180 @@ static int compute_live_registers(struct bpf_verifier_env *env)
24034
24038
return err;
24035
24039
}
24036
24040
24041
+ /*
24042
+ * Compute strongly connected components (SCCs) on the CFG.
24043
+ * Assign an SCC number to each instruction, recorded in env->insn_aux[*].scc.
24044
+ * If instruction is a sole member of its SCC and there are no self edges,
24045
+ * assign it SCC number of zero.
24046
+ * Uses a non-recursive adaptation of Tarjan's algorithm for SCC computation.
24047
+ */
24048
+ static int compute_scc(struct bpf_verifier_env *env)
24049
+ {
24050
+ const u32 NOT_ON_STACK = U32_MAX;
24051
+
24052
+ struct bpf_insn_aux_data *aux = env->insn_aux_data;
24053
+ const u32 insn_cnt = env->prog->len;
24054
+ int stack_sz, dfs_sz, err = 0;
24055
+ u32 *stack, *pre, *low, *dfs;
24056
+ u32 succ_cnt, i, j, t, w;
24057
+ u32 next_preorder_num;
24058
+ u32 next_scc_id;
24059
+ bool assign_scc;
24060
+ u32 succ[2];
24061
+
24062
+ next_preorder_num = 1;
24063
+ next_scc_id = 1;
24064
+ /*
24065
+ * - 'stack' accumulates vertices in DFS order, see invariant comment below;
24066
+ * - 'pre[t] == p' => preorder number of vertex 't' is 'p';
24067
+ * - 'low[t] == n' => smallest preorder number of the vertex reachable from 't' is 'n';
24068
+ * - 'dfs' DFS traversal stack, used to emulate explicit recursion.
24069
+ */
24070
+ stack = kvcalloc(insn_cnt, sizeof(int), GFP_KERNEL);
24071
+ pre = kvcalloc(insn_cnt, sizeof(int), GFP_KERNEL);
24072
+ low = kvcalloc(insn_cnt, sizeof(int), GFP_KERNEL);
24073
+ dfs = kvcalloc(insn_cnt, sizeof(*dfs), GFP_KERNEL);
24074
+ if (!stack || !pre || !low || !dfs) {
24075
+ err = -ENOMEM;
24076
+ goto exit;
24077
+ }
24078
+ /*
24079
+ * References:
24080
+ * [1] R. Tarjan "Depth-First Search and Linear Graph Algorithms"
24081
+ * [2] D. J. Pearce "A Space-Efficient Algorithm for Finding Strongly Connected Components"
24082
+ *
24083
+ * The algorithm maintains the following invariant:
24084
+ * - suppose there is a path 'u' ~> 'v', such that 'pre[v] < pre[u]';
24085
+ * - then, vertex 'u' remains on stack while vertex 'v' is on stack.
24086
+ *
24087
+ * Consequently:
24088
+ * - If 'low[v] < pre[v]', there is a path from 'v' to some vertex 'u',
24089
+ * such that 'pre[u] == low[v]'; vertex 'u' is currently on the stack,
24090
+ * and thus there is an SCC (loop) containing both 'u' and 'v'.
24091
+ * - If 'low[v] == pre[v]', loops containing 'v' have been explored,
24092
+ * and 'v' can be considered the root of some SCC.
24093
+ *
24094
+ * Here is a pseudo-code for an explicitly recursive version of the algorithm:
24095
+ *
24096
+ * NOT_ON_STACK = insn_cnt + 1
24097
+ * pre = [0] * insn_cnt
24098
+ * low = [0] * insn_cnt
24099
+ * scc = [0] * insn_cnt
24100
+ * stack = []
24101
+ *
24102
+ * next_preorder_num = 1
24103
+ * next_scc_id = 1
24104
+ *
24105
+ * def recur(w):
24106
+ * nonlocal next_preorder_num
24107
+ * nonlocal next_scc_id
24108
+ *
24109
+ * pre[w] = next_preorder_num
24110
+ * low[w] = next_preorder_num
24111
+ * next_preorder_num += 1
24112
+ * stack.append(w)
24113
+ * for s in successors(w):
24114
+ * # Note: for classic algorithm the block below should look as:
24115
+ * #
24116
+ * # if pre[s] == 0:
24117
+ * # recur(s)
24118
+ * # low[w] = min(low[w], low[s])
24119
+ * # elif low[s] != NOT_ON_STACK:
24120
+ * # low[w] = min(low[w], pre[s])
24121
+ * #
24122
+ * # But replacing both 'min' instructions with 'low[w] = min(low[w], low[s])'
24123
+ * # does not break the invariant and makes itartive version of the algorithm
24124
+ * # simpler. See 'Algorithm #3' from [2].
24125
+ *
24126
+ * # 's' not yet visited
24127
+ * if pre[s] == 0:
24128
+ * recur(s)
24129
+ * # if 's' is on stack, pick lowest reachable preorder number from it;
24130
+ * # if 's' is not on stack 'low[s] == NOT_ON_STACK > low[w]',
24131
+ * # so 'min' would be a noop.
24132
+ * low[w] = min(low[w], low[s])
24133
+ *
24134
+ * if low[w] == pre[w]:
24135
+ * # 'w' is the root of an SCC, pop all vertices
24136
+ * # below 'w' on stack and assign same SCC to them.
24137
+ * while True:
24138
+ * t = stack.pop()
24139
+ * low[t] = NOT_ON_STACK
24140
+ * scc[t] = next_scc_id
24141
+ * if t == w:
24142
+ * break
24143
+ * next_scc_id += 1
24144
+ *
24145
+ * for i in range(0, insn_cnt):
24146
+ * if pre[i] == 0:
24147
+ * recur(i)
24148
+ *
24149
+ * Below implementation replaces explicit recusion with array 'dfs'.
24150
+ */
24151
+ for (i = 0; i < insn_cnt; i++) {
24152
+ if (pre[i])
24153
+ continue;
24154
+ stack_sz = 0;
24155
+ dfs_sz = 1;
24156
+ dfs[0] = i;
24157
+ dfs_continue:
24158
+ while (dfs_sz) {
24159
+ w = dfs[dfs_sz - 1];
24160
+ if (pre[w] == 0) {
24161
+ low[w] = next_preorder_num;
24162
+ pre[w] = next_preorder_num;
24163
+ next_preorder_num++;
24164
+ stack[stack_sz++] = w;
24165
+ }
24166
+ /* Visit 'w' successors */
24167
+ succ_cnt = insn_successors(env->prog, w, succ);
24168
+ for (j = 0; j < succ_cnt; ++j) {
24169
+ if (pre[succ[j]]) {
24170
+ low[w] = min(low[w], low[succ[j]]);
24171
+ } else {
24172
+ dfs[dfs_sz++] = succ[j];
24173
+ goto dfs_continue;
24174
+ }
24175
+ }
24176
+ /*
24177
+ * Preserve the invariant: if some vertex above in the stack
24178
+ * is reachable from 'w', keep 'w' on the stack.
24179
+ */
24180
+ if (low[w] < pre[w]) {
24181
+ dfs_sz--;
24182
+ goto dfs_continue;
24183
+ }
24184
+ /*
24185
+ * Assign SCC number only if component has two or more elements,
24186
+ * or if component has a self reference.
24187
+ */
24188
+ assign_scc = stack[stack_sz - 1] != w;
24189
+ for (j = 0; j < succ_cnt; ++j) {
24190
+ if (succ[j] == w) {
24191
+ assign_scc = true;
24192
+ break;
24193
+ }
24194
+ }
24195
+ /* Pop component elements from stack */
24196
+ do {
24197
+ t = stack[--stack_sz];
24198
+ low[t] = NOT_ON_STACK;
24199
+ if (assign_scc)
24200
+ aux[t].scc = next_scc_id;
24201
+ } while (t != w);
24202
+ if (assign_scc)
24203
+ next_scc_id++;
24204
+ dfs_sz--;
24205
+ }
24206
+ }
24207
+ exit:
24208
+ kvfree(stack);
24209
+ kvfree(pre);
24210
+ kvfree(low);
24211
+ kvfree(dfs);
24212
+ return err;
24213
+ }
24214
+
24037
24215
int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr, __u32 uattr_size)
24038
24216
{
24039
24217
u64 start_time = ktime_get_ns();
@@ -24155,6 +24333,10 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr, __u3
24155
24333
if (ret)
24156
24334
goto skip_full_check;
24157
24335
24336
+ ret = compute_scc(env);
24337
+ if (ret < 0)
24338
+ goto skip_full_check;
24339
+
24158
24340
ret = compute_live_registers(env);
24159
24341
if (ret < 0)
24160
24342
goto skip_full_check;
0 commit comments