xref: /linux/kernel/bpf/loops.c (revision 9ae7f5e8bf1d8be74797f937cdbeb730178a8c3d)
16254fb69SEduard Zingerman // SPDX-License-Identifier: GPL-2.0-only
26254fb69SEduard Zingerman /* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
36254fb69SEduard Zingerman 
46254fb69SEduard Zingerman #include <linux/slab.h>
56254fb69SEduard Zingerman #include <linux/sched/signal.h>
66254fb69SEduard Zingerman #include <linux/bpf_verifier.h>
76254fb69SEduard Zingerman 
86254fb69SEduard Zingerman static struct bpf_iarray **compute_predecessors(struct bpf_verifier_env *env)
96254fb69SEduard Zingerman {
106254fb69SEduard Zingerman 	struct bpf_iarray *succ, *preds, **result;
116254fb69SEduard Zingerman 	struct bpf_prog *prog = env->prog;
126254fb69SEduard Zingerman 	u32 *num_preds, i, s, len = prog->len;
136254fb69SEduard Zingerman 	struct bpf_insn *insn;
146254fb69SEduard Zingerman 	size_t sz;
156254fb69SEduard Zingerman 	void *tmp;
166254fb69SEduard Zingerman 
176254fb69SEduard Zingerman 	num_preds = kvcalloc(prog->len, sizeof(u32), GFP_KERNEL_ACCOUNT);
186254fb69SEduard Zingerman 	if (!num_preds)
196254fb69SEduard Zingerman 		return NULL;
206254fb69SEduard Zingerman 
216254fb69SEduard Zingerman 	/*
226254fb69SEduard Zingerman 	 * 'result' layout:
236254fb69SEduard Zingerman 	 *  - array of pointers (struct bpf_iarray *)[len]
246254fb69SEduard Zingerman 	 *  - struct bpf_iarray one after another
256254fb69SEduard Zingerman 	 */
266254fb69SEduard Zingerman 	sz = size_mul(len, sizeof(*result) + sizeof(**result));
276254fb69SEduard Zingerman 	for (i = 0; i < len; i++) {
286254fb69SEduard Zingerman 		insn = env->prog->insnsi + i;
296254fb69SEduard Zingerman 		succ = bpf_insn_successors(env, i);
306254fb69SEduard Zingerman 		sz = size_add(sz, array_size(succ->cnt, sizeof(*succ->items)));
316254fb69SEduard Zingerman 		iarray_for_each(s, succ) {
326254fb69SEduard Zingerman 			num_preds[s]++;
336254fb69SEduard Zingerman 		}
346254fb69SEduard Zingerman 		if (bpf_is_ldimm64(insn))
356254fb69SEduard Zingerman 			i++;
366254fb69SEduard Zingerman 	}
376254fb69SEduard Zingerman 
386254fb69SEduard Zingerman 	result = kvzalloc(sz, GFP_KERNEL_ACCOUNT);
396254fb69SEduard Zingerman 	if (!result) {
406254fb69SEduard Zingerman 		kvfree(num_preds);
416254fb69SEduard Zingerman 		return NULL;
426254fb69SEduard Zingerman 	}
436254fb69SEduard Zingerman 
446254fb69SEduard Zingerman 	tmp = (void *)&result[len];
456254fb69SEduard Zingerman 	for (i = 0; i < len; i++) {
466254fb69SEduard Zingerman 		result[i] = tmp;
476254fb69SEduard Zingerman 		tmp += struct_size(result[i], items, num_preds[i]);
486254fb69SEduard Zingerman 	}
496254fb69SEduard Zingerman 
506254fb69SEduard Zingerman 	for (i = 0; i < len; i++) {
516254fb69SEduard Zingerman 		insn = env->prog->insnsi + i;
526254fb69SEduard Zingerman 		succ = bpf_insn_successors(env, i);
536254fb69SEduard Zingerman 		iarray_for_each(s, succ) {
546254fb69SEduard Zingerman 			preds = result[s];
556254fb69SEduard Zingerman 			preds->items[preds->cnt++] = i;
566254fb69SEduard Zingerman 		}
576254fb69SEduard Zingerman 		if (bpf_is_ldimm64(insn))
586254fb69SEduard Zingerman 			i++;
596254fb69SEduard Zingerman 	}
606254fb69SEduard Zingerman 
616254fb69SEduard Zingerman 	kvfree(num_preds);
626254fb69SEduard Zingerman 	return result;
636254fb69SEduard Zingerman }
646254fb69SEduard Zingerman 
656254fb69SEduard Zingerman static int idoms_intersect(struct bpf_verifier_env *env, int a, int b)
666254fb69SEduard Zingerman {
676254fb69SEduard Zingerman 	int *postorder_nums = env->cfg.postorder_nums;
686254fb69SEduard Zingerman 	int *idoms = env->idoms;
696254fb69SEduard Zingerman 
706254fb69SEduard Zingerman 	while (a != b) {
716254fb69SEduard Zingerman 		while (postorder_nums[a] < postorder_nums[b]) {
726254fb69SEduard Zingerman 			a = idoms[a];
736254fb69SEduard Zingerman 		}
746254fb69SEduard Zingerman 		while (postorder_nums[b] < postorder_nums[a]) {
756254fb69SEduard Zingerman 			b = idoms[b];
766254fb69SEduard Zingerman 		}
776254fb69SEduard Zingerman 	}
786254fb69SEduard Zingerman 	return a;
796254fb69SEduard Zingerman }
806254fb69SEduard Zingerman 
816254fb69SEduard Zingerman /* See "A Simple, Fast Dominance Algorithm" by Cooper et al. for details. */
826254fb69SEduard Zingerman static int compute_subprog_idoms(struct bpf_verifier_env *env, struct bpf_iarray **preds,
836254fb69SEduard Zingerman 				int subprog_idx)
846254fb69SEduard Zingerman {
856254fb69SEduard Zingerman 	struct bpf_subprog_info *subprog = &env->subprog_info[subprog_idx];
866254fb69SEduard Zingerman 	int start = subprog->start;
876254fb69SEduard Zingerman 	int po_first = subprog->postorder_start;
886254fb69SEduard Zingerman 	int po_last = (subprog + 1)->postorder_start - 1;
896254fb69SEduard Zingerman 	int *idoms = env->idoms;
906254fb69SEduard Zingerman 	int po_num, pred;
916254fb69SEduard Zingerman 	u32 work = 0;
926254fb69SEduard Zingerman 	bool changed;
936254fb69SEduard Zingerman 
946254fb69SEduard Zingerman 	idoms[start] = start;
956254fb69SEduard Zingerman 	changed = true;
966254fb69SEduard Zingerman 	do {
976254fb69SEduard Zingerman 		changed = false;
986254fb69SEduard Zingerman 		/*
996254fb69SEduard Zingerman 		 * Iterate in reverse postorder, skipping the subprog entry:
1006254fb69SEduard Zingerman 		 * insn_postorder[po_last] is the subprogram entry.
1016254fb69SEduard Zingerman 		 */
1026254fb69SEduard Zingerman 		for (po_num = po_last - 1; po_num >= po_first; po_num--) {
1036254fb69SEduard Zingerman 			int idx = env->cfg.insn_postorder[po_num];
1046254fb69SEduard Zingerman 			int new_idom = -1;
1056254fb69SEduard Zingerman 
1066254fb69SEduard Zingerman 			iarray_for_each(pred, preds[idx]) {
1076254fb69SEduard Zingerman 				if (++work % 1024 == 0) {
1086254fb69SEduard Zingerman 					if (signal_pending(current))
1096254fb69SEduard Zingerman 						return -EAGAIN;
1106254fb69SEduard Zingerman 					cond_resched();
1116254fb69SEduard Zingerman 				}
1126254fb69SEduard Zingerman 
1136254fb69SEduard Zingerman 				if (idoms[pred] == -1)
1146254fb69SEduard Zingerman 					continue;
1156254fb69SEduard Zingerman 				if (new_idom == -1)
1166254fb69SEduard Zingerman 					new_idom = pred;
1176254fb69SEduard Zingerman 				else
1186254fb69SEduard Zingerman 					new_idom = idoms_intersect(env, pred, new_idom);
1196254fb69SEduard Zingerman 			}
1206254fb69SEduard Zingerman 			if (new_idom != -1 && idoms[idx] != new_idom) {
1216254fb69SEduard Zingerman 				idoms[idx] = new_idom;
1226254fb69SEduard Zingerman 				changed = true;
1236254fb69SEduard Zingerman 			}
1246254fb69SEduard Zingerman 		}
1256254fb69SEduard Zingerman 	} while (changed);
1266254fb69SEduard Zingerman 	idoms[start] = -1;
1276254fb69SEduard Zingerman 	return 0;
1286254fb69SEduard Zingerman }
1296254fb69SEduard Zingerman 
1306254fb69SEduard Zingerman int bpf_compute_idoms(struct bpf_verifier_env *env)
1316254fb69SEduard Zingerman {
1326254fb69SEduard Zingerman 	struct bpf_iarray **preds;
1336254fb69SEduard Zingerman 	u32 len = env->prog->len;
1346254fb69SEduard Zingerman 	int *idoms, i, err = 0;
1356254fb69SEduard Zingerman 
1366254fb69SEduard Zingerman 	preds = compute_predecessors(env);
1376254fb69SEduard Zingerman 	if (!preds)
1386254fb69SEduard Zingerman 		return -ENOMEM;
1396254fb69SEduard Zingerman 
1406254fb69SEduard Zingerman 	idoms = kvcalloc(len, sizeof(*idoms), GFP_KERNEL_ACCOUNT);
1416254fb69SEduard Zingerman 	if (!idoms) {
1426254fb69SEduard Zingerman 		kvfree(preds);
1436254fb69SEduard Zingerman 		return -ENOMEM;
1446254fb69SEduard Zingerman 	}
1456254fb69SEduard Zingerman 
1466254fb69SEduard Zingerman 	env->idoms = idoms;
1476254fb69SEduard Zingerman 	for (i = 0; i < len; i++)
1486254fb69SEduard Zingerman 		idoms[i] = -1;
1496254fb69SEduard Zingerman 
1506254fb69SEduard Zingerman 	for (i = 0; i < env->subprog_cnt; i++) {
1516254fb69SEduard Zingerman 		err = compute_subprog_idoms(env, preds, i);
1526254fb69SEduard Zingerman 		if (err)
1536254fb69SEduard Zingerman 			break;
1546254fb69SEduard Zingerman 	}
1556254fb69SEduard Zingerman 
1566254fb69SEduard Zingerman 	kvfree(preds);
1576254fb69SEduard Zingerman 	return err;
1586254fb69SEduard Zingerman }
159*9ae7f5e8SEduard Zingerman 
160*9ae7f5e8SEduard Zingerman struct dfs_state {
161*9ae7f5e8SEduard Zingerman 	u32 traversed:1;
162*9ae7f5e8SEduard Zingerman 	u32 next_succ:31;
163*9ae7f5e8SEduard Zingerman };
164*9ae7f5e8SEduard Zingerman 
165*9ae7f5e8SEduard Zingerman struct loops_dfs {
166*9ae7f5e8SEduard Zingerman 	struct dfs_state *state;
167*9ae7f5e8SEduard Zingerman 	int *dfs_pos;
168*9ae7f5e8SEduard Zingerman 	int *stack;
169*9ae7f5e8SEduard Zingerman };
170*9ae7f5e8SEduard Zingerman 
171*9ae7f5e8SEduard Zingerman static void mark_irreducible(struct bpf_verifier_env *env, int h)
172*9ae7f5e8SEduard Zingerman {
173*9ae7f5e8SEduard Zingerman 	env->insn_aux_data[h].loop->irreducible = true;
174*9ae7f5e8SEduard Zingerman }
175*9ae7f5e8SEduard Zingerman 
176*9ae7f5e8SEduard Zingerman static void add_backedge(struct bpf_verifier_env *env, int from, int h)
177*9ae7f5e8SEduard Zingerman {
178*9ae7f5e8SEduard Zingerman 	struct bpf_insn_aux_data *aux = env->insn_aux_data;
179*9ae7f5e8SEduard Zingerman 	struct bpf_loop *loop = aux[h].loop;
180*9ae7f5e8SEduard Zingerman 	int cnt = loop->backedges_cnt;
181*9ae7f5e8SEduard Zingerman 
182*9ae7f5e8SEduard Zingerman 	if (cnt == MAX_BACKEDGES) {
183*9ae7f5e8SEduard Zingerman 		loop->backedges_overflow = true;
184*9ae7f5e8SEduard Zingerman 		return;
185*9ae7f5e8SEduard Zingerman 	}
186*9ae7f5e8SEduard Zingerman 	loop->backedges[cnt].from = from;
187*9ae7f5e8SEduard Zingerman 	loop->backedges[cnt].latches_cnt = 0;
188*9ae7f5e8SEduard Zingerman 	loop->backedges_cnt++;
189*9ae7f5e8SEduard Zingerman }
190*9ae7f5e8SEduard Zingerman 
191*9ae7f5e8SEduard Zingerman static int add_exit(struct bpf_loop *loop, int from, int to)
192*9ae7f5e8SEduard Zingerman {
193*9ae7f5e8SEduard Zingerman 	if (loop->exits_overflow)
194*9ae7f5e8SEduard Zingerman 		return 0;
195*9ae7f5e8SEduard Zingerman 	if (loop->exits_cnt == MAX_LOOP_EXITS) {
196*9ae7f5e8SEduard Zingerman 		loop->exits_overflow = true;
197*9ae7f5e8SEduard Zingerman 		return 0;
198*9ae7f5e8SEduard Zingerman 	}
199*9ae7f5e8SEduard Zingerman 	if (!loop->exits) {
200*9ae7f5e8SEduard Zingerman 		loop->exits = kvcalloc(MAX_LOOP_EXITS, sizeof(*loop->exits), GFP_KERNEL_ACCOUNT);
201*9ae7f5e8SEduard Zingerman 		if (!loop->exits)
202*9ae7f5e8SEduard Zingerman 			return -ENOMEM;
203*9ae7f5e8SEduard Zingerman 	}
204*9ae7f5e8SEduard Zingerman 	loop->exits[loop->exits_cnt] = (struct bpf_loop_exit) {
205*9ae7f5e8SEduard Zingerman 		.from = from,
206*9ae7f5e8SEduard Zingerman 		.to = to,
207*9ae7f5e8SEduard Zingerman 	};
208*9ae7f5e8SEduard Zingerman 	loop->exits_cnt++;
209*9ae7f5e8SEduard Zingerman 	return 0;
210*9ae7f5e8SEduard Zingerman }
211*9ae7f5e8SEduard Zingerman 
212*9ae7f5e8SEduard Zingerman static int mark_as_header(struct bpf_verifier_env *env, int h)
213*9ae7f5e8SEduard Zingerman {
214*9ae7f5e8SEduard Zingerman 	struct bpf_insn_aux_data *aux = env->insn_aux_data;
215*9ae7f5e8SEduard Zingerman 
216*9ae7f5e8SEduard Zingerman 	if (!aux[h].loop) {
217*9ae7f5e8SEduard Zingerman 		aux[h].loop = kvzalloc_obj(struct bpf_loop, GFP_KERNEL_ACCOUNT);
218*9ae7f5e8SEduard Zingerman 		if (!aux[h].loop)
219*9ae7f5e8SEduard Zingerman 			return -ENOMEM;
220*9ae7f5e8SEduard Zingerman 	}
221*9ae7f5e8SEduard Zingerman 	return 0;
222*9ae7f5e8SEduard Zingerman }
223*9ae7f5e8SEduard Zingerman 
224*9ae7f5e8SEduard Zingerman static int assign_header(struct bpf_verifier_env *env, struct loops_dfs *dfs, int n, int h)
225*9ae7f5e8SEduard Zingerman {
226*9ae7f5e8SEduard Zingerman 	struct bpf_insn_aux_data *aux = env->insn_aux_data;
227*9ae7f5e8SEduard Zingerman 	int *dfs_pos = dfs->dfs_pos;
228*9ae7f5e8SEduard Zingerman 	int err, nh;
229*9ae7f5e8SEduard Zingerman 
230*9ae7f5e8SEduard Zingerman 	err = mark_as_header(env, h);
231*9ae7f5e8SEduard Zingerman 	if (err)
232*9ae7f5e8SEduard Zingerman 		return err;
233*9ae7f5e8SEduard Zingerman 
234*9ae7f5e8SEduard Zingerman 	/* Don't encode self-loops, otherwise can't reflect loops nesting structure. */
235*9ae7f5e8SEduard Zingerman 	if (n == h)
236*9ae7f5e8SEduard Zingerman 		return 0;
237*9ae7f5e8SEduard Zingerman 
238*9ae7f5e8SEduard Zingerman 	/* Make sure that loop headers up the chain are sorted by dfs_pos. */
239*9ae7f5e8SEduard Zingerman 	while (aux[n].loop_header != -1) {
240*9ae7f5e8SEduard Zingerman 		nh = aux[n].loop_header;
241*9ae7f5e8SEduard Zingerman 		if (nh == h)
242*9ae7f5e8SEduard Zingerman 			return 0;
243*9ae7f5e8SEduard Zingerman 		if (dfs_pos[nh] < dfs_pos[h]) {
244*9ae7f5e8SEduard Zingerman 			aux[n].loop_header = h;
245*9ae7f5e8SEduard Zingerman 			n = h;
246*9ae7f5e8SEduard Zingerman 			h = nh;
247*9ae7f5e8SEduard Zingerman 		} else {
248*9ae7f5e8SEduard Zingerman 			n = nh;
249*9ae7f5e8SEduard Zingerman 		}
250*9ae7f5e8SEduard Zingerman 	}
251*9ae7f5e8SEduard Zingerman 	aux[n].loop_header = h;
252*9ae7f5e8SEduard Zingerman 	return 0;
253*9ae7f5e8SEduard Zingerman }
254*9ae7f5e8SEduard Zingerman 
255*9ae7f5e8SEduard Zingerman static bool is_cond_jmp_insn(struct bpf_insn *insn)
256*9ae7f5e8SEduard Zingerman {
257*9ae7f5e8SEduard Zingerman 	u8 class = BPF_CLASS(insn->code);
258*9ae7f5e8SEduard Zingerman 	u8 opcode = BPF_OP(insn->code);
259*9ae7f5e8SEduard Zingerman 
260*9ae7f5e8SEduard Zingerman 	if (class != BPF_JMP && class != BPF_JMP32)
261*9ae7f5e8SEduard Zingerman 		return false;
262*9ae7f5e8SEduard Zingerman 
263*9ae7f5e8SEduard Zingerman 	switch (opcode) {
264*9ae7f5e8SEduard Zingerman 	case BPF_JEQ:
265*9ae7f5e8SEduard Zingerman 	case BPF_JGE:
266*9ae7f5e8SEduard Zingerman 	case BPF_JGT:
267*9ae7f5e8SEduard Zingerman 	case BPF_JLE:
268*9ae7f5e8SEduard Zingerman 	case BPF_JLT:
269*9ae7f5e8SEduard Zingerman 	case BPF_JNE:
270*9ae7f5e8SEduard Zingerman 	case BPF_JSET:
271*9ae7f5e8SEduard Zingerman 	case BPF_JSGE:
272*9ae7f5e8SEduard Zingerman 	case BPF_JSGT:
273*9ae7f5e8SEduard Zingerman 	case BPF_JSLE:
274*9ae7f5e8SEduard Zingerman 	case BPF_JSLT:
275*9ae7f5e8SEduard Zingerman 		return true;
276*9ae7f5e8SEduard Zingerman 	default:
277*9ae7f5e8SEduard Zingerman 		return false;
278*9ae7f5e8SEduard Zingerman 	}
279*9ae7f5e8SEduard Zingerman }
280*9ae7f5e8SEduard Zingerman 
281*9ae7f5e8SEduard Zingerman int bpf_loop_at_index(struct bpf_verifier_env *env, u32 idx)
282*9ae7f5e8SEduard Zingerman {
283*9ae7f5e8SEduard Zingerman 	struct bpf_insn_aux_data *aux = env->insn_aux_data;
284*9ae7f5e8SEduard Zingerman 
285*9ae7f5e8SEduard Zingerman 	return aux[idx].loop ? idx : aux[idx].loop_header;
286*9ae7f5e8SEduard Zingerman }
287*9ae7f5e8SEduard Zingerman 
288*9ae7f5e8SEduard Zingerman /*
289*9ae7f5e8SEduard Zingerman  * Collect conditional jumps within n's loop that dominate 'n' and have
290*9ae7f5e8SEduard Zingerman  * a successor outside the loop, nearest first.
291*9ae7f5e8SEduard Zingerman  */
292*9ae7f5e8SEduard Zingerman static void find_dominating_conditions(struct bpf_verifier_env *env, int n,
293*9ae7f5e8SEduard Zingerman 				       struct bpf_backedge *backedge)
294*9ae7f5e8SEduard Zingerman {
295*9ae7f5e8SEduard Zingerman 	struct bpf_insn *insns = env->prog->insnsi;
296*9ae7f5e8SEduard Zingerman 	bool t_exits_loop, f_exits_loop;
297*9ae7f5e8SEduard Zingerman 	int n_loop, t_loop, f_loop;
298*9ae7f5e8SEduard Zingerman 	int *idoms = env->idoms;
299*9ae7f5e8SEduard Zingerman 
300*9ae7f5e8SEduard Zingerman 	n_loop = bpf_loop_at_index(env, n);
301*9ae7f5e8SEduard Zingerman 	if (idoms_intersect(env, n, n_loop) != n_loop)
302*9ae7f5e8SEduard Zingerman 		return;
303*9ae7f5e8SEduard Zingerman 	while (n >= 0) {
304*9ae7f5e8SEduard Zingerman 		if (is_cond_jmp_insn(&insns[n]) && bpf_loop_at_index(env, n) == n_loop) {
305*9ae7f5e8SEduard Zingerman 			t_loop = bpf_loop_at_index(env, n + insns[n].off + 1);
306*9ae7f5e8SEduard Zingerman 			f_loop = bpf_loop_at_index(env, n + 1);
307*9ae7f5e8SEduard Zingerman 			t_exits_loop = t_loop != n_loop && !bpf_is_nested_loop(env, t_loop, n_loop);
308*9ae7f5e8SEduard Zingerman 			f_exits_loop = f_loop != n_loop && !bpf_is_nested_loop(env, f_loop, n_loop);
309*9ae7f5e8SEduard Zingerman 			if (t_exits_loop || f_exits_loop) {
310*9ae7f5e8SEduard Zingerman 				if (backedge->latches_cnt == MAX_LATCHES)
311*9ae7f5e8SEduard Zingerman 					return;
312*9ae7f5e8SEduard Zingerman 				backedge->latches[backedge->latches_cnt++] = n;
313*9ae7f5e8SEduard Zingerman 			}
314*9ae7f5e8SEduard Zingerman 		}
315*9ae7f5e8SEduard Zingerman 		if (n == n_loop)
316*9ae7f5e8SEduard Zingerman 			break;
317*9ae7f5e8SEduard Zingerman 		n = idoms[n];
318*9ae7f5e8SEduard Zingerman 	}
319*9ae7f5e8SEduard Zingerman }
320*9ae7f5e8SEduard Zingerman 
321*9ae7f5e8SEduard Zingerman /*
322*9ae7f5e8SEduard Zingerman  * As described in "A New Algorithm for Identifying Loops in Decompilation" by Wei et al,
323*9ae7f5e8SEduard Zingerman  * adapted to be non-recursive.
324*9ae7f5e8SEduard Zingerman  */
325*9ae7f5e8SEduard Zingerman static int compute_loops_in_subprog(struct bpf_verifier_env *env, struct loops_dfs *dfs,
326*9ae7f5e8SEduard Zingerman 				    int subprog_idx)
327*9ae7f5e8SEduard Zingerman {
328*9ae7f5e8SEduard Zingerman 	struct bpf_insn_aux_data *aux = env->insn_aux_data;
329*9ae7f5e8SEduard Zingerman 	struct dfs_state *state = dfs->state;
330*9ae7f5e8SEduard Zingerman 	int start = env->subprog_info[subprog_idx].start;
331*9ae7f5e8SEduard Zingerman 	int *dfs_pos = dfs->dfs_pos;
332*9ae7f5e8SEduard Zingerman 	int *stack = dfs->stack;
333*9ae7f5e8SEduard Zingerman 	int s, h, err, cur, stack_sz;
334*9ae7f5e8SEduard Zingerman 	struct bpf_iarray *succ;
335*9ae7f5e8SEduard Zingerman 	u32 i;
336*9ae7f5e8SEduard Zingerman 
337*9ae7f5e8SEduard Zingerman 	stack[0] = start;
338*9ae7f5e8SEduard Zingerman 	state[start].traversed = true;
339*9ae7f5e8SEduard Zingerman 	state[start].next_succ = 0;
340*9ae7f5e8SEduard Zingerman 	dfs_pos[start] = 1;
341*9ae7f5e8SEduard Zingerman 	stack_sz = 1;
342*9ae7f5e8SEduard Zingerman 	i = 0;
343*9ae7f5e8SEduard Zingerman 	do {
344*9ae7f5e8SEduard Zingerman 		/*
345*9ae7f5e8SEduard Zingerman 		 * The algorithm should be very fast in practice,
346*9ae7f5e8SEduard Zingerman 		 * guard against pathological inputs, just in case.
347*9ae7f5e8SEduard Zingerman 		 */
348*9ae7f5e8SEduard Zingerman 		if ((++i % 1024) == 0) {
349*9ae7f5e8SEduard Zingerman 			if (signal_pending(current))
350*9ae7f5e8SEduard Zingerman 				return -EAGAIN;
351*9ae7f5e8SEduard Zingerman 			cond_resched();
352*9ae7f5e8SEduard Zingerman 		}
353*9ae7f5e8SEduard Zingerman 
354*9ae7f5e8SEduard Zingerman 		cur = stack[stack_sz - 1];
355*9ae7f5e8SEduard Zingerman 		succ = bpf_insn_successors(env, cur);
356*9ae7f5e8SEduard Zingerman 		if (state[cur].next_succ == succ->cnt) {
357*9ae7f5e8SEduard Zingerman 			dfs_pos[cur] = 0;
358*9ae7f5e8SEduard Zingerman 			stack_sz--;
359*9ae7f5e8SEduard Zingerman 			continue;
360*9ae7f5e8SEduard Zingerman 		}
361*9ae7f5e8SEduard Zingerman 		s = succ->items[state[cur].next_succ];
362*9ae7f5e8SEduard Zingerman 		if (!state[s].traversed) {
363*9ae7f5e8SEduard Zingerman 			/* Case A:  start -> ... -> cur -> s [unexplored] */
364*9ae7f5e8SEduard Zingerman 			state[s].traversed = true;
365*9ae7f5e8SEduard Zingerman 			state[s].next_succ = 0;
366*9ae7f5e8SEduard Zingerman 			stack[stack_sz] = s;
367*9ae7f5e8SEduard Zingerman 			dfs_pos[s] = stack_sz + 1;
368*9ae7f5e8SEduard Zingerman 			stack_sz++;
369*9ae7f5e8SEduard Zingerman 			continue;
370*9ae7f5e8SEduard Zingerman 		}
371*9ae7f5e8SEduard Zingerman 		/* 's' is fully explored at this point */
372*9ae7f5e8SEduard Zingerman 		if (dfs_pos[s]) {
373*9ae7f5e8SEduard Zingerman 			/*
374*9ae7f5e8SEduard Zingerman 			 * start -> ... -> s -> cur --.
375*9ae7f5e8SEduard Zingerman 			 *                 ^          |
376*9ae7f5e8SEduard Zingerman 			 *                 '----------'
377*9ae7f5e8SEduard Zingerman 			 * Case B: 's' is in the current DFS path.
378*9ae7f5e8SEduard Zingerman 			 */
379*9ae7f5e8SEduard Zingerman 			err = assign_header(env, dfs, cur, s);
380*9ae7f5e8SEduard Zingerman 			if (err)
381*9ae7f5e8SEduard Zingerman 				return err;
382*9ae7f5e8SEduard Zingerman 			add_backedge(env, cur, s);
383*9ae7f5e8SEduard Zingerman 		} else if (aux[s].loop_header == -1) {
384*9ae7f5e8SEduard Zingerman 			/*
385*9ae7f5e8SEduard Zingerman 			 * start -> ... -> ... -> s -> ... -> end
386*9ae7f5e8SEduard Zingerman 			 *           |            ^
387*9ae7f5e8SEduard Zingerman 			 *           '---> cur ---'
388*9ae7f5e8SEduard Zingerman 			 * Case C: 's' is explored, not in the current DFS path,
389*9ae7f5e8SEduard Zingerman 			 * and not a part of any loop.
390*9ae7f5e8SEduard Zingerman 			 */
391*9ae7f5e8SEduard Zingerman 		} else if (dfs_pos[aux[s].loop_header]) {
392*9ae7f5e8SEduard Zingerman 			/*
393*9ae7f5e8SEduard Zingerman 			 *                 .----------------------.
394*9ae7f5e8SEduard Zingerman 			 *                 v                      |
395*9ae7f5e8SEduard Zingerman 			 * start -> ... -> h -> ... -> ... -> s --'
396*9ae7f5e8SEduard Zingerman 			 *                       |            ^
397*9ae7f5e8SEduard Zingerman 			 *	                 '---> cur ---'
398*9ae7f5e8SEduard Zingerman 			 * Case D: 's' is explored, not in current DFS path,
399*9ae7f5e8SEduard Zingerman 			 * but its innermost loop header is.
400*9ae7f5e8SEduard Zingerman 			 */
401*9ae7f5e8SEduard Zingerman 			err = assign_header(env, dfs, cur, aux[s].loop_header);
402*9ae7f5e8SEduard Zingerman 			if (err)
403*9ae7f5e8SEduard Zingerman 				return err;
404*9ae7f5e8SEduard Zingerman 		} else {
405*9ae7f5e8SEduard Zingerman 			/*
406*9ae7f5e8SEduard Zingerman 			 * case E: 's' is explored, not in current DFS path,
407*9ae7f5e8SEduard Zingerman 			 * its innermost loop header is not in current DFS path,
408*9ae7f5e8SEduard Zingerman 			 * hence 's' is another entry into the same loop.
409*9ae7f5e8SEduard Zingerman 			 */
410*9ae7f5e8SEduard Zingerman 			h = aux[s].loop_header;
411*9ae7f5e8SEduard Zingerman 			mark_irreducible(env, h);
412*9ae7f5e8SEduard Zingerman 			while (aux[h].loop_header != -1) {
413*9ae7f5e8SEduard Zingerman 				h = aux[h].loop_header;
414*9ae7f5e8SEduard Zingerman 				if (dfs_pos[h]) {
415*9ae7f5e8SEduard Zingerman 					err = assign_header(env, dfs, cur, h);
416*9ae7f5e8SEduard Zingerman 					if (err)
417*9ae7f5e8SEduard Zingerman 						return err;
418*9ae7f5e8SEduard Zingerman 					break;
419*9ae7f5e8SEduard Zingerman 				}
420*9ae7f5e8SEduard Zingerman 				mark_irreducible(env, h);
421*9ae7f5e8SEduard Zingerman 			}
422*9ae7f5e8SEduard Zingerman 		}
423*9ae7f5e8SEduard Zingerman 		state[cur].next_succ++;
424*9ae7f5e8SEduard Zingerman 	} while (stack_sz);
425*9ae7f5e8SEduard Zingerman 
426*9ae7f5e8SEduard Zingerman 	return 0;
427*9ae7f5e8SEduard Zingerman }
428*9ae7f5e8SEduard Zingerman 
429*9ae7f5e8SEduard Zingerman bool bpf_is_nested_loop(struct bpf_verifier_env *env, int inner_header, int outer_header)
430*9ae7f5e8SEduard Zingerman {
431*9ae7f5e8SEduard Zingerman 	struct bpf_insn_aux_data *aux = env->insn_aux_data;
432*9ae7f5e8SEduard Zingerman 	int idx;
433*9ae7f5e8SEduard Zingerman 
434*9ae7f5e8SEduard Zingerman 	for (idx = inner_header; idx >= 0; idx = aux[idx].loop_header)
435*9ae7f5e8SEduard Zingerman 		if (aux[idx].loop_header == outer_header)
436*9ae7f5e8SEduard Zingerman 			return true;
437*9ae7f5e8SEduard Zingerman 
438*9ae7f5e8SEduard Zingerman 	return false;
439*9ae7f5e8SEduard Zingerman }
440*9ae7f5e8SEduard Zingerman 
441*9ae7f5e8SEduard Zingerman int bpf_compute_loops(struct bpf_verifier_env *env)
442*9ae7f5e8SEduard Zingerman {
443*9ae7f5e8SEduard Zingerman 	int i, j, k, s, t, iloop, sloop, err = 0, len = env->prog->len;
444*9ae7f5e8SEduard Zingerman 	struct bpf_insn_aux_data *aux = env->insn_aux_data;
445*9ae7f5e8SEduard Zingerman 	struct bpf_verifier_log *log = &env->log;
446*9ae7f5e8SEduard Zingerman 	struct bpf_backedge *backedge;
447*9ae7f5e8SEduard Zingerman 	struct loops_dfs dfs = {};
448*9ae7f5e8SEduard Zingerman 	struct bpf_iarray *succ;
449*9ae7f5e8SEduard Zingerman 	struct bpf_loop *loop;
450*9ae7f5e8SEduard Zingerman 
451*9ae7f5e8SEduard Zingerman 	dfs.dfs_pos = kvcalloc(len, sizeof(int), GFP_KERNEL_ACCOUNT);
452*9ae7f5e8SEduard Zingerman 	dfs.state = kvcalloc(len, sizeof(struct dfs_state), GFP_KERNEL_ACCOUNT);
453*9ae7f5e8SEduard Zingerman 	dfs.stack = kvcalloc(len, sizeof(int), GFP_KERNEL_ACCOUNT);
454*9ae7f5e8SEduard Zingerman 	if (!dfs.dfs_pos || !dfs.state || !dfs.stack) {
455*9ae7f5e8SEduard Zingerman 		err = -ENOMEM;
456*9ae7f5e8SEduard Zingerman 		goto out;
457*9ae7f5e8SEduard Zingerman 	}
458*9ae7f5e8SEduard Zingerman 	for (i = 0; i < len; i++)
459*9ae7f5e8SEduard Zingerman 		aux[i].loop_header = -1;
460*9ae7f5e8SEduard Zingerman 	for (i = 0; i < env->subprog_cnt; i++) {
461*9ae7f5e8SEduard Zingerman 		err = compute_loops_in_subprog(env, &dfs, i);
462*9ae7f5e8SEduard Zingerman 		if (err)
463*9ae7f5e8SEduard Zingerman 			goto out;
464*9ae7f5e8SEduard Zingerman 	}
465*9ae7f5e8SEduard Zingerman 	/* find latches */
466*9ae7f5e8SEduard Zingerman 	for (i = 0; i < len; i++) {
467*9ae7f5e8SEduard Zingerman 		loop = aux[i].loop;
468*9ae7f5e8SEduard Zingerman 		if (!loop)
469*9ae7f5e8SEduard Zingerman 			continue;
470*9ae7f5e8SEduard Zingerman 		for (j = 0; j < loop->backedges_cnt; j++) {
471*9ae7f5e8SEduard Zingerman 			backedge = &loop->backedges[j];
472*9ae7f5e8SEduard Zingerman 			/*
473*9ae7f5e8SEduard Zingerman 			 * In theory, the backedge->from and it's latch can reside in
474*9ae7f5e8SEduard Zingerman 			 * an inner loop of `i`, e.g.:
475*9ae7f5e8SEduard Zingerman 			 *
476*9ae7f5e8SEduard Zingerman 			 *  1:  r7 += 1;
477*9ae7f5e8SEduard Zingerman 			 *  2:  r6 += 1;
478*9ae7f5e8SEduard Zingerman 			 *      if r7 == 2 goto 1b;
479*9ae7f5e8SEduard Zingerman 			 *      if r6 < 2 goto 2b;
480*9ae7f5e8SEduard Zingerman 			 *
481*9ae7f5e8SEduard Zingerman 			 * For now, let's assume that latches are unknown for such cases.
482*9ae7f5e8SEduard Zingerman 			 * (GCC/LLVM handle this by inserting artificial cfg nodes).
483*9ae7f5e8SEduard Zingerman 			 */
484*9ae7f5e8SEduard Zingerman 			if (bpf_loop_at_index(env, backedge->from) != i)
485*9ae7f5e8SEduard Zingerman 				continue;
486*9ae7f5e8SEduard Zingerman 			find_dominating_conditions(env, backedge->from, backedge);
487*9ae7f5e8SEduard Zingerman 		}
488*9ae7f5e8SEduard Zingerman 	}
489*9ae7f5e8SEduard Zingerman 	/* find exits */
490*9ae7f5e8SEduard Zingerman 	for (i = 0; i < len; i++) {
491*9ae7f5e8SEduard Zingerman 		iloop = aux[i].loop ? i : aux[i].loop_header;
492*9ae7f5e8SEduard Zingerman 		if (iloop < 0)
493*9ae7f5e8SEduard Zingerman 			continue;
494*9ae7f5e8SEduard Zingerman 		succ = bpf_insn_successors(env, i);
495*9ae7f5e8SEduard Zingerman 		iarray_for_each(s, succ) {
496*9ae7f5e8SEduard Zingerman 			/*
497*9ae7f5e8SEduard Zingerman 			 * Nothing left to record once the innermost loop of 'i'
498*9ae7f5e8SEduard Zingerman 			 * overflowed: the walk below would stop at it right away.
499*9ae7f5e8SEduard Zingerman 			 */
500*9ae7f5e8SEduard Zingerman 			if (aux[iloop].loop->exits_overflow)
501*9ae7f5e8SEduard Zingerman 				break;
502*9ae7f5e8SEduard Zingerman 			sloop = aux[s].loop ? s : aux[s].loop_header;
503*9ae7f5e8SEduard Zingerman 			if (iloop == sloop)
504*9ae7f5e8SEduard Zingerman 				continue;
505*9ae7f5e8SEduard Zingerman 			if (bpf_is_nested_loop(env, sloop, iloop))
506*9ae7f5e8SEduard Zingerman 				continue;
507*9ae7f5e8SEduard Zingerman 			/*
508*9ae7f5e8SEduard Zingerman 			 * At this point 'sloop' is either -1, an outer loop,
509*9ae7f5e8SEduard Zingerman 			 * or a loop in another branch of the loop hierarchy.
510*9ae7f5e8SEduard Zingerman 			 */
511*9ae7f5e8SEduard Zingerman 			for (t = iloop; t >= 0 && t != sloop; t = aux[t].loop_header) {
512*9ae7f5e8SEduard Zingerman 				/*
513*9ae7f5e8SEduard Zingerman 				 * Account for the following configuration:
514*9ae7f5e8SEduard Zingerman 				 *
515*9ae7f5e8SEduard Zingerman 				 *   tloop {
516*9ae7f5e8SEduard Zingerman 				 *     iloop {
517*9ae7f5e8SEduard Zingerman 				 *       ... i: goto s;
518*9ae7f5e8SEduard Zingerman 				 *     }
519*9ae7f5e8SEduard Zingerman 				 *     sloop {
520*9ae7f5e8SEduard Zingerman 				 *   s:
521*9ae7f5e8SEduard Zingerman 				 *       ...
522*9ae7f5e8SEduard Zingerman 				 *     }
523*9ae7f5e8SEduard Zingerman 				 *   }
524*9ae7f5e8SEduard Zingerman 				 */
525*9ae7f5e8SEduard Zingerman 				if (bpf_is_nested_loop(env, sloop, t))
526*9ae7f5e8SEduard Zingerman 					break;
527*9ae7f5e8SEduard Zingerman 				/*
528*9ae7f5e8SEduard Zingerman 				 * Record edges i -> s as exits from tloop when:
529*9ae7f5e8SEduard Zingerman 				 *
530*9ae7f5e8SEduard Zingerman 				 *   sloop {
531*9ae7f5e8SEduard Zingerman 				 *     tloop {
532*9ae7f5e8SEduard Zingerman 				 *       iloop {
533*9ae7f5e8SEduard Zingerman 				 *         ... i: goto s;
534*9ae7f5e8SEduard Zingerman 				 *       }
535*9ae7f5e8SEduard Zingerman 				 *     }
536*9ae7f5e8SEduard Zingerman 				 *  s: ...
537*9ae7f5e8SEduard Zingerman 				 *   }
538*9ae7f5e8SEduard Zingerman 				 */
539*9ae7f5e8SEduard Zingerman 				/* Enclosing loops inherit the overflow, see below */
540*9ae7f5e8SEduard Zingerman 				if (aux[t].loop->exits_overflow)
541*9ae7f5e8SEduard Zingerman 					break;
542*9ae7f5e8SEduard Zingerman 				err = add_exit(aux[t].loop, i, s);
543*9ae7f5e8SEduard Zingerman 				if (err)
544*9ae7f5e8SEduard Zingerman 					goto out;
545*9ae7f5e8SEduard Zingerman 			}
546*9ae7f5e8SEduard Zingerman 		}
547*9ae7f5e8SEduard Zingerman 		if (bpf_is_ldimm64(env->prog->insnsi + i))
548*9ae7f5e8SEduard Zingerman 			i++;
549*9ae7f5e8SEduard Zingerman 	}
550*9ae7f5e8SEduard Zingerman 	/* A nested loop with a truncated exit list can't be abstracted by SCEV */
551*9ae7f5e8SEduard Zingerman 	for (i = 0; i < len; i++) {
552*9ae7f5e8SEduard Zingerman 		loop = aux[i].loop;
553*9ae7f5e8SEduard Zingerman 		if (!loop || !loop->exits_overflow)
554*9ae7f5e8SEduard Zingerman 			continue;
555*9ae7f5e8SEduard Zingerman 		for (t = aux[i].loop_header; t >= 0; t = aux[t].loop_header)
556*9ae7f5e8SEduard Zingerman 			aux[t].loop->exits_overflow = true;
557*9ae7f5e8SEduard Zingerman 	}
558*9ae7f5e8SEduard Zingerman 
559*9ae7f5e8SEduard Zingerman 	if (env->log.level & BPF_LOG_LEVEL2) {
560*9ae7f5e8SEduard Zingerman 		for (i = 0; i < len; i++) {
561*9ae7f5e8SEduard Zingerman 			loop = aux[i].loop;
562*9ae7f5e8SEduard Zingerman 			if (!loop)
563*9ae7f5e8SEduard Zingerman 				continue;
564*9ae7f5e8SEduard Zingerman 			bpf_log(log, "loop at %d", i);
565*9ae7f5e8SEduard Zingerman 			if (aux[i].loop_header >= 0)
566*9ae7f5e8SEduard Zingerman 				bpf_log(log, ", nested in %d", aux[i].loop_header);
567*9ae7f5e8SEduard Zingerman 			if (loop->irreducible)
568*9ae7f5e8SEduard Zingerman 				bpf_log(log, ", irreducible");
569*9ae7f5e8SEduard Zingerman 			if (loop->exits_overflow)
570*9ae7f5e8SEduard Zingerman 				bpf_log(log, ", too many exits");
571*9ae7f5e8SEduard Zingerman 			bpf_log(log, "\n");
572*9ae7f5e8SEduard Zingerman 			for (j = 0; j < loop->backedges_cnt; j++) {
573*9ae7f5e8SEduard Zingerman 				backedge = &loop->backedges[j];
574*9ae7f5e8SEduard Zingerman 				bpf_log(log, "  backedge from %d", backedge->from);
575*9ae7f5e8SEduard Zingerman 				for (k = 0; k < backedge->latches_cnt; k++) {
576*9ae7f5e8SEduard Zingerman 					if (k == 0)
577*9ae7f5e8SEduard Zingerman 						bpf_log(log, ", latch at ");
578*9ae7f5e8SEduard Zingerman 					if (k == 1)
579*9ae7f5e8SEduard Zingerman 						bpf_log(log, ", dominating exits at ");
580*9ae7f5e8SEduard Zingerman 					if (k > 1)
581*9ae7f5e8SEduard Zingerman 						bpf_log(log, ", ");
582*9ae7f5e8SEduard Zingerman 					bpf_log(log, "%d", backedge->latches[k]);
583*9ae7f5e8SEduard Zingerman 				}
584*9ae7f5e8SEduard Zingerman 				bpf_log(log, "\n");
585*9ae7f5e8SEduard Zingerman 			}
586*9ae7f5e8SEduard Zingerman 			for (j = 0; j < loop->exits_cnt; j++)
587*9ae7f5e8SEduard Zingerman 				bpf_log(log, "  exit from %d to %d\n",
588*9ae7f5e8SEduard Zingerman 					loop->exits[j].from, loop->exits[j].to);
589*9ae7f5e8SEduard Zingerman 		}
590*9ae7f5e8SEduard Zingerman 	}
591*9ae7f5e8SEduard Zingerman 
592*9ae7f5e8SEduard Zingerman out:
593*9ae7f5e8SEduard Zingerman 	kvfree(dfs.dfs_pos);
594*9ae7f5e8SEduard Zingerman 	kvfree(dfs.stack);
595*9ae7f5e8SEduard Zingerman 	kvfree(dfs.state);
596*9ae7f5e8SEduard Zingerman 	return err;
597*9ae7f5e8SEduard Zingerman }
598