xref: /linux/kernel/bpf/loops.c (revision 2fa579694ddf3a96454669c764ca0c06dea50817)
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 
8*2fa57969SEduard Zingerman static bool is_cfg_jump(struct bpf_insn *insn)
9*2fa57969SEduard Zingerman {
10*2fa57969SEduard Zingerman 	u8 class = BPF_CLASS(insn->code);
11*2fa57969SEduard Zingerman 	u8 opcode = BPF_OP(insn->code);
12*2fa57969SEduard Zingerman 
13*2fa57969SEduard Zingerman 	switch (class) {
14*2fa57969SEduard Zingerman 	case BPF_JMP:
15*2fa57969SEduard Zingerman 	case BPF_JMP32:
16*2fa57969SEduard Zingerman 		return opcode != BPF_CALL;
17*2fa57969SEduard Zingerman 	default:
18*2fa57969SEduard Zingerman 		return false;
19*2fa57969SEduard Zingerman 	}
20*2fa57969SEduard Zingerman }
21*2fa57969SEduard Zingerman 
226254fb69SEduard Zingerman static struct bpf_iarray **compute_predecessors(struct bpf_verifier_env *env)
236254fb69SEduard Zingerman {
24*2fa57969SEduard Zingerman 	struct bpf_insn_aux_data *aux = env->insn_aux_data;
256254fb69SEduard Zingerman 	struct bpf_iarray *succ, *preds, **result;
266254fb69SEduard Zingerman 	struct bpf_prog *prog = env->prog;
276254fb69SEduard Zingerman 	u32 *num_preds, i, s, len = prog->len;
286254fb69SEduard Zingerman 	struct bpf_insn *insn;
296254fb69SEduard Zingerman 	size_t sz;
306254fb69SEduard Zingerman 	void *tmp;
316254fb69SEduard Zingerman 
326254fb69SEduard Zingerman 	num_preds = kvcalloc(prog->len, sizeof(u32), GFP_KERNEL_ACCOUNT);
336254fb69SEduard Zingerman 	if (!num_preds)
346254fb69SEduard Zingerman 		return NULL;
356254fb69SEduard Zingerman 
366254fb69SEduard Zingerman 	/*
376254fb69SEduard Zingerman 	 * 'result' layout:
386254fb69SEduard Zingerman 	 *  - array of pointers (struct bpf_iarray *)[len]
396254fb69SEduard Zingerman 	 *  - struct bpf_iarray one after another
406254fb69SEduard Zingerman 	 */
416254fb69SEduard Zingerman 	sz = size_mul(len, sizeof(*result) + sizeof(**result));
426254fb69SEduard Zingerman 	for (i = 0; i < len; i++) {
436254fb69SEduard Zingerman 		insn = env->prog->insnsi + i;
446254fb69SEduard Zingerman 		succ = bpf_insn_successors(env, i);
456254fb69SEduard Zingerman 		sz = size_add(sz, array_size(succ->cnt, sizeof(*succ->items)));
466254fb69SEduard Zingerman 		iarray_for_each(s, succ) {
476254fb69SEduard Zingerman 			num_preds[s]++;
486254fb69SEduard Zingerman 		}
496254fb69SEduard Zingerman 		if (bpf_is_ldimm64(insn))
506254fb69SEduard Zingerman 			i++;
516254fb69SEduard Zingerman 	}
526254fb69SEduard Zingerman 
536254fb69SEduard Zingerman 	result = kvzalloc(sz, GFP_KERNEL_ACCOUNT);
546254fb69SEduard Zingerman 	if (!result) {
556254fb69SEduard Zingerman 		kvfree(num_preds);
566254fb69SEduard Zingerman 		return NULL;
576254fb69SEduard Zingerman 	}
586254fb69SEduard Zingerman 
596254fb69SEduard Zingerman 	tmp = (void *)&result[len];
606254fb69SEduard Zingerman 	for (i = 0; i < len; i++) {
616254fb69SEduard Zingerman 		result[i] = tmp;
626254fb69SEduard Zingerman 		tmp += struct_size(result[i], items, num_preds[i]);
636254fb69SEduard Zingerman 	}
646254fb69SEduard Zingerman 
656254fb69SEduard Zingerman 	for (i = 0; i < len; i++) {
666254fb69SEduard Zingerman 		insn = env->prog->insnsi + i;
676254fb69SEduard Zingerman 		succ = bpf_insn_successors(env, i);
686254fb69SEduard Zingerman 		iarray_for_each(s, succ) {
696254fb69SEduard Zingerman 			preds = result[s];
706254fb69SEduard Zingerman 			preds->items[preds->cnt++] = i;
716254fb69SEduard Zingerman 		}
72*2fa57969SEduard Zingerman 		if (succ->cnt > 1 ||
73*2fa57969SEduard Zingerman 		    (succ->cnt == 1 && num_preds[succ->items[0]] > 1) ||
74*2fa57969SEduard Zingerman 		    is_cfg_jump(insn))
75*2fa57969SEduard Zingerman 			aux[i].bb_end = true;
766254fb69SEduard Zingerman 		if (bpf_is_ldimm64(insn))
776254fb69SEduard Zingerman 			i++;
786254fb69SEduard Zingerman 	}
796254fb69SEduard Zingerman 
806254fb69SEduard Zingerman 	kvfree(num_preds);
816254fb69SEduard Zingerman 	return result;
826254fb69SEduard Zingerman }
836254fb69SEduard Zingerman 
846254fb69SEduard Zingerman static int idoms_intersect(struct bpf_verifier_env *env, int a, int b)
856254fb69SEduard Zingerman {
866254fb69SEduard Zingerman 	int *postorder_nums = env->cfg.postorder_nums;
876254fb69SEduard Zingerman 	int *idoms = env->idoms;
886254fb69SEduard Zingerman 
896254fb69SEduard Zingerman 	while (a != b) {
906254fb69SEduard Zingerman 		while (postorder_nums[a] < postorder_nums[b]) {
916254fb69SEduard Zingerman 			a = idoms[a];
926254fb69SEduard Zingerman 		}
936254fb69SEduard Zingerman 		while (postorder_nums[b] < postorder_nums[a]) {
946254fb69SEduard Zingerman 			b = idoms[b];
956254fb69SEduard Zingerman 		}
966254fb69SEduard Zingerman 	}
976254fb69SEduard Zingerman 	return a;
986254fb69SEduard Zingerman }
996254fb69SEduard Zingerman 
1006254fb69SEduard Zingerman /* See "A Simple, Fast Dominance Algorithm" by Cooper et al. for details. */
1016254fb69SEduard Zingerman static int compute_subprog_idoms(struct bpf_verifier_env *env, struct bpf_iarray **preds,
1026254fb69SEduard Zingerman 				int subprog_idx)
1036254fb69SEduard Zingerman {
1046254fb69SEduard Zingerman 	struct bpf_subprog_info *subprog = &env->subprog_info[subprog_idx];
1056254fb69SEduard Zingerman 	int start = subprog->start;
1066254fb69SEduard Zingerman 	int po_first = subprog->postorder_start;
1076254fb69SEduard Zingerman 	int po_last = (subprog + 1)->postorder_start - 1;
1086254fb69SEduard Zingerman 	int *idoms = env->idoms;
1096254fb69SEduard Zingerman 	int po_num, pred;
1106254fb69SEduard Zingerman 	u32 work = 0;
1116254fb69SEduard Zingerman 	bool changed;
1126254fb69SEduard Zingerman 
1136254fb69SEduard Zingerman 	idoms[start] = start;
1146254fb69SEduard Zingerman 	changed = true;
1156254fb69SEduard Zingerman 	do {
1166254fb69SEduard Zingerman 		changed = false;
1176254fb69SEduard Zingerman 		/*
1186254fb69SEduard Zingerman 		 * Iterate in reverse postorder, skipping the subprog entry:
1196254fb69SEduard Zingerman 		 * insn_postorder[po_last] is the subprogram entry.
1206254fb69SEduard Zingerman 		 */
1216254fb69SEduard Zingerman 		for (po_num = po_last - 1; po_num >= po_first; po_num--) {
1226254fb69SEduard Zingerman 			int idx = env->cfg.insn_postorder[po_num];
1236254fb69SEduard Zingerman 			int new_idom = -1;
1246254fb69SEduard Zingerman 
1256254fb69SEduard Zingerman 			iarray_for_each(pred, preds[idx]) {
1266254fb69SEduard Zingerman 				if (++work % 1024 == 0) {
1276254fb69SEduard Zingerman 					if (signal_pending(current))
1286254fb69SEduard Zingerman 						return -EAGAIN;
1296254fb69SEduard Zingerman 					cond_resched();
1306254fb69SEduard Zingerman 				}
1316254fb69SEduard Zingerman 
1326254fb69SEduard Zingerman 				if (idoms[pred] == -1)
1336254fb69SEduard Zingerman 					continue;
1346254fb69SEduard Zingerman 				if (new_idom == -1)
1356254fb69SEduard Zingerman 					new_idom = pred;
1366254fb69SEduard Zingerman 				else
1376254fb69SEduard Zingerman 					new_idom = idoms_intersect(env, pred, new_idom);
1386254fb69SEduard Zingerman 			}
1396254fb69SEduard Zingerman 			if (new_idom != -1 && idoms[idx] != new_idom) {
1406254fb69SEduard Zingerman 				idoms[idx] = new_idom;
1416254fb69SEduard Zingerman 				changed = true;
1426254fb69SEduard Zingerman 			}
1436254fb69SEduard Zingerman 		}
1446254fb69SEduard Zingerman 	} while (changed);
1456254fb69SEduard Zingerman 	idoms[start] = -1;
1466254fb69SEduard Zingerman 	return 0;
1476254fb69SEduard Zingerman }
1486254fb69SEduard Zingerman 
1496254fb69SEduard Zingerman int bpf_compute_idoms(struct bpf_verifier_env *env)
1506254fb69SEduard Zingerman {
1516254fb69SEduard Zingerman 	struct bpf_iarray **preds;
1526254fb69SEduard Zingerman 	u32 len = env->prog->len;
1536254fb69SEduard Zingerman 	int *idoms, i, err = 0;
1546254fb69SEduard Zingerman 
1556254fb69SEduard Zingerman 	preds = compute_predecessors(env);
1566254fb69SEduard Zingerman 	if (!preds)
1576254fb69SEduard Zingerman 		return -ENOMEM;
1586254fb69SEduard Zingerman 
1596254fb69SEduard Zingerman 	idoms = kvcalloc(len, sizeof(*idoms), GFP_KERNEL_ACCOUNT);
1606254fb69SEduard Zingerman 	if (!idoms) {
1616254fb69SEduard Zingerman 		kvfree(preds);
1626254fb69SEduard Zingerman 		return -ENOMEM;
1636254fb69SEduard Zingerman 	}
1646254fb69SEduard Zingerman 
1656254fb69SEduard Zingerman 	env->idoms = idoms;
1666254fb69SEduard Zingerman 	for (i = 0; i < len; i++)
1676254fb69SEduard Zingerman 		idoms[i] = -1;
1686254fb69SEduard Zingerman 
1696254fb69SEduard Zingerman 	for (i = 0; i < env->subprog_cnt; i++) {
1706254fb69SEduard Zingerman 		err = compute_subprog_idoms(env, preds, i);
1716254fb69SEduard Zingerman 		if (err)
1726254fb69SEduard Zingerman 			break;
1736254fb69SEduard Zingerman 	}
1746254fb69SEduard Zingerman 
1756254fb69SEduard Zingerman 	kvfree(preds);
1766254fb69SEduard Zingerman 	return err;
1776254fb69SEduard Zingerman }
1789ae7f5e8SEduard Zingerman 
1799ae7f5e8SEduard Zingerman struct dfs_state {
1809ae7f5e8SEduard Zingerman 	u32 traversed:1;
1819ae7f5e8SEduard Zingerman 	u32 next_succ:31;
1829ae7f5e8SEduard Zingerman };
1839ae7f5e8SEduard Zingerman 
1849ae7f5e8SEduard Zingerman struct loops_dfs {
1859ae7f5e8SEduard Zingerman 	struct dfs_state *state;
1869ae7f5e8SEduard Zingerman 	int *dfs_pos;
1879ae7f5e8SEduard Zingerman 	int *stack;
1889ae7f5e8SEduard Zingerman };
1899ae7f5e8SEduard Zingerman 
1909ae7f5e8SEduard Zingerman static void mark_irreducible(struct bpf_verifier_env *env, int h)
1919ae7f5e8SEduard Zingerman {
1929ae7f5e8SEduard Zingerman 	env->insn_aux_data[h].loop->irreducible = true;
1939ae7f5e8SEduard Zingerman }
1949ae7f5e8SEduard Zingerman 
1959ae7f5e8SEduard Zingerman static void add_backedge(struct bpf_verifier_env *env, int from, int h)
1969ae7f5e8SEduard Zingerman {
1979ae7f5e8SEduard Zingerman 	struct bpf_insn_aux_data *aux = env->insn_aux_data;
1989ae7f5e8SEduard Zingerman 	struct bpf_loop *loop = aux[h].loop;
1999ae7f5e8SEduard Zingerman 	int cnt = loop->backedges_cnt;
2009ae7f5e8SEduard Zingerman 
2019ae7f5e8SEduard Zingerman 	if (cnt == MAX_BACKEDGES) {
2029ae7f5e8SEduard Zingerman 		loop->backedges_overflow = true;
2039ae7f5e8SEduard Zingerman 		return;
2049ae7f5e8SEduard Zingerman 	}
2059ae7f5e8SEduard Zingerman 	loop->backedges[cnt].from = from;
2069ae7f5e8SEduard Zingerman 	loop->backedges[cnt].latches_cnt = 0;
2079ae7f5e8SEduard Zingerman 	loop->backedges_cnt++;
2089ae7f5e8SEduard Zingerman }
2099ae7f5e8SEduard Zingerman 
2109ae7f5e8SEduard Zingerman static int add_exit(struct bpf_loop *loop, int from, int to)
2119ae7f5e8SEduard Zingerman {
2129ae7f5e8SEduard Zingerman 	if (loop->exits_overflow)
2139ae7f5e8SEduard Zingerman 		return 0;
2149ae7f5e8SEduard Zingerman 	if (loop->exits_cnt == MAX_LOOP_EXITS) {
2159ae7f5e8SEduard Zingerman 		loop->exits_overflow = true;
2169ae7f5e8SEduard Zingerman 		return 0;
2179ae7f5e8SEduard Zingerman 	}
2189ae7f5e8SEduard Zingerman 	if (!loop->exits) {
2199ae7f5e8SEduard Zingerman 		loop->exits = kvcalloc(MAX_LOOP_EXITS, sizeof(*loop->exits), GFP_KERNEL_ACCOUNT);
2209ae7f5e8SEduard Zingerman 		if (!loop->exits)
2219ae7f5e8SEduard Zingerman 			return -ENOMEM;
2229ae7f5e8SEduard Zingerman 	}
2239ae7f5e8SEduard Zingerman 	loop->exits[loop->exits_cnt] = (struct bpf_loop_exit) {
2249ae7f5e8SEduard Zingerman 		.from = from,
2259ae7f5e8SEduard Zingerman 		.to = to,
2269ae7f5e8SEduard Zingerman 	};
2279ae7f5e8SEduard Zingerman 	loop->exits_cnt++;
2289ae7f5e8SEduard Zingerman 	return 0;
2299ae7f5e8SEduard Zingerman }
2309ae7f5e8SEduard Zingerman 
2319ae7f5e8SEduard Zingerman static int mark_as_header(struct bpf_verifier_env *env, int h)
2329ae7f5e8SEduard Zingerman {
2339ae7f5e8SEduard Zingerman 	struct bpf_insn_aux_data *aux = env->insn_aux_data;
2349ae7f5e8SEduard Zingerman 
2359ae7f5e8SEduard Zingerman 	if (!aux[h].loop) {
2369ae7f5e8SEduard Zingerman 		aux[h].loop = kvzalloc_obj(struct bpf_loop, GFP_KERNEL_ACCOUNT);
2379ae7f5e8SEduard Zingerman 		if (!aux[h].loop)
2389ae7f5e8SEduard Zingerman 			return -ENOMEM;
2399ae7f5e8SEduard Zingerman 	}
2409ae7f5e8SEduard Zingerman 	return 0;
2419ae7f5e8SEduard Zingerman }
2429ae7f5e8SEduard Zingerman 
2439ae7f5e8SEduard Zingerman static int assign_header(struct bpf_verifier_env *env, struct loops_dfs *dfs, int n, int h)
2449ae7f5e8SEduard Zingerman {
2459ae7f5e8SEduard Zingerman 	struct bpf_insn_aux_data *aux = env->insn_aux_data;
2469ae7f5e8SEduard Zingerman 	int *dfs_pos = dfs->dfs_pos;
2479ae7f5e8SEduard Zingerman 	int err, nh;
2489ae7f5e8SEduard Zingerman 
2499ae7f5e8SEduard Zingerman 	err = mark_as_header(env, h);
2509ae7f5e8SEduard Zingerman 	if (err)
2519ae7f5e8SEduard Zingerman 		return err;
2529ae7f5e8SEduard Zingerman 
2539ae7f5e8SEduard Zingerman 	/* Don't encode self-loops, otherwise can't reflect loops nesting structure. */
2549ae7f5e8SEduard Zingerman 	if (n == h)
2559ae7f5e8SEduard Zingerman 		return 0;
2569ae7f5e8SEduard Zingerman 
2579ae7f5e8SEduard Zingerman 	/* Make sure that loop headers up the chain are sorted by dfs_pos. */
2589ae7f5e8SEduard Zingerman 	while (aux[n].loop_header != -1) {
2599ae7f5e8SEduard Zingerman 		nh = aux[n].loop_header;
2609ae7f5e8SEduard Zingerman 		if (nh == h)
2619ae7f5e8SEduard Zingerman 			return 0;
2629ae7f5e8SEduard Zingerman 		if (dfs_pos[nh] < dfs_pos[h]) {
2639ae7f5e8SEduard Zingerman 			aux[n].loop_header = h;
2649ae7f5e8SEduard Zingerman 			n = h;
2659ae7f5e8SEduard Zingerman 			h = nh;
2669ae7f5e8SEduard Zingerman 		} else {
2679ae7f5e8SEduard Zingerman 			n = nh;
2689ae7f5e8SEduard Zingerman 		}
2699ae7f5e8SEduard Zingerman 	}
2709ae7f5e8SEduard Zingerman 	aux[n].loop_header = h;
2719ae7f5e8SEduard Zingerman 	return 0;
2729ae7f5e8SEduard Zingerman }
2739ae7f5e8SEduard Zingerman 
2749ae7f5e8SEduard Zingerman static bool is_cond_jmp_insn(struct bpf_insn *insn)
2759ae7f5e8SEduard Zingerman {
2769ae7f5e8SEduard Zingerman 	u8 class = BPF_CLASS(insn->code);
2779ae7f5e8SEduard Zingerman 	u8 opcode = BPF_OP(insn->code);
2789ae7f5e8SEduard Zingerman 
2799ae7f5e8SEduard Zingerman 	if (class != BPF_JMP && class != BPF_JMP32)
2809ae7f5e8SEduard Zingerman 		return false;
2819ae7f5e8SEduard Zingerman 
2829ae7f5e8SEduard Zingerman 	switch (opcode) {
2839ae7f5e8SEduard Zingerman 	case BPF_JEQ:
2849ae7f5e8SEduard Zingerman 	case BPF_JGE:
2859ae7f5e8SEduard Zingerman 	case BPF_JGT:
2869ae7f5e8SEduard Zingerman 	case BPF_JLE:
2879ae7f5e8SEduard Zingerman 	case BPF_JLT:
2889ae7f5e8SEduard Zingerman 	case BPF_JNE:
2899ae7f5e8SEduard Zingerman 	case BPF_JSET:
2909ae7f5e8SEduard Zingerman 	case BPF_JSGE:
2919ae7f5e8SEduard Zingerman 	case BPF_JSGT:
2929ae7f5e8SEduard Zingerman 	case BPF_JSLE:
2939ae7f5e8SEduard Zingerman 	case BPF_JSLT:
2949ae7f5e8SEduard Zingerman 		return true;
2959ae7f5e8SEduard Zingerman 	default:
2969ae7f5e8SEduard Zingerman 		return false;
2979ae7f5e8SEduard Zingerman 	}
2989ae7f5e8SEduard Zingerman }
2999ae7f5e8SEduard Zingerman 
3009ae7f5e8SEduard Zingerman int bpf_loop_at_index(struct bpf_verifier_env *env, u32 idx)
3019ae7f5e8SEduard Zingerman {
3029ae7f5e8SEduard Zingerman 	struct bpf_insn_aux_data *aux = env->insn_aux_data;
3039ae7f5e8SEduard Zingerman 
3049ae7f5e8SEduard Zingerman 	return aux[idx].loop ? idx : aux[idx].loop_header;
3059ae7f5e8SEduard Zingerman }
3069ae7f5e8SEduard Zingerman 
3079ae7f5e8SEduard Zingerman /*
3089ae7f5e8SEduard Zingerman  * Collect conditional jumps within n's loop that dominate 'n' and have
3099ae7f5e8SEduard Zingerman  * a successor outside the loop, nearest first.
3109ae7f5e8SEduard Zingerman  */
3119ae7f5e8SEduard Zingerman static void find_dominating_conditions(struct bpf_verifier_env *env, int n,
3129ae7f5e8SEduard Zingerman 				       struct bpf_backedge *backedge)
3139ae7f5e8SEduard Zingerman {
3149ae7f5e8SEduard Zingerman 	struct bpf_insn *insns = env->prog->insnsi;
3159ae7f5e8SEduard Zingerman 	bool t_exits_loop, f_exits_loop;
3169ae7f5e8SEduard Zingerman 	int n_loop, t_loop, f_loop;
3179ae7f5e8SEduard Zingerman 	int *idoms = env->idoms;
3189ae7f5e8SEduard Zingerman 
3199ae7f5e8SEduard Zingerman 	n_loop = bpf_loop_at_index(env, n);
3209ae7f5e8SEduard Zingerman 	if (idoms_intersect(env, n, n_loop) != n_loop)
3219ae7f5e8SEduard Zingerman 		return;
3229ae7f5e8SEduard Zingerman 	while (n >= 0) {
3239ae7f5e8SEduard Zingerman 		if (is_cond_jmp_insn(&insns[n]) && bpf_loop_at_index(env, n) == n_loop) {
3249ae7f5e8SEduard Zingerman 			t_loop = bpf_loop_at_index(env, n + insns[n].off + 1);
3259ae7f5e8SEduard Zingerman 			f_loop = bpf_loop_at_index(env, n + 1);
3269ae7f5e8SEduard Zingerman 			t_exits_loop = t_loop != n_loop && !bpf_is_nested_loop(env, t_loop, n_loop);
3279ae7f5e8SEduard Zingerman 			f_exits_loop = f_loop != n_loop && !bpf_is_nested_loop(env, f_loop, n_loop);
3289ae7f5e8SEduard Zingerman 			if (t_exits_loop || f_exits_loop) {
3299ae7f5e8SEduard Zingerman 				if (backedge->latches_cnt == MAX_LATCHES)
3309ae7f5e8SEduard Zingerman 					return;
3319ae7f5e8SEduard Zingerman 				backedge->latches[backedge->latches_cnt++] = n;
3329ae7f5e8SEduard Zingerman 			}
3339ae7f5e8SEduard Zingerman 		}
3349ae7f5e8SEduard Zingerman 		if (n == n_loop)
3359ae7f5e8SEduard Zingerman 			break;
3369ae7f5e8SEduard Zingerman 		n = idoms[n];
3379ae7f5e8SEduard Zingerman 	}
3389ae7f5e8SEduard Zingerman }
3399ae7f5e8SEduard Zingerman 
3409ae7f5e8SEduard Zingerman /*
3419ae7f5e8SEduard Zingerman  * As described in "A New Algorithm for Identifying Loops in Decompilation" by Wei et al,
3429ae7f5e8SEduard Zingerman  * adapted to be non-recursive.
3439ae7f5e8SEduard Zingerman  */
3449ae7f5e8SEduard Zingerman static int compute_loops_in_subprog(struct bpf_verifier_env *env, struct loops_dfs *dfs,
3459ae7f5e8SEduard Zingerman 				    int subprog_idx)
3469ae7f5e8SEduard Zingerman {
3479ae7f5e8SEduard Zingerman 	struct bpf_insn_aux_data *aux = env->insn_aux_data;
3489ae7f5e8SEduard Zingerman 	struct dfs_state *state = dfs->state;
3499ae7f5e8SEduard Zingerman 	int start = env->subprog_info[subprog_idx].start;
3509ae7f5e8SEduard Zingerman 	int *dfs_pos = dfs->dfs_pos;
3519ae7f5e8SEduard Zingerman 	int *stack = dfs->stack;
3529ae7f5e8SEduard Zingerman 	int s, h, err, cur, stack_sz;
3539ae7f5e8SEduard Zingerman 	struct bpf_iarray *succ;
3549ae7f5e8SEduard Zingerman 	u32 i;
3559ae7f5e8SEduard Zingerman 
3569ae7f5e8SEduard Zingerman 	stack[0] = start;
3579ae7f5e8SEduard Zingerman 	state[start].traversed = true;
3589ae7f5e8SEduard Zingerman 	state[start].next_succ = 0;
3599ae7f5e8SEduard Zingerman 	dfs_pos[start] = 1;
3609ae7f5e8SEduard Zingerman 	stack_sz = 1;
3619ae7f5e8SEduard Zingerman 	i = 0;
3629ae7f5e8SEduard Zingerman 	do {
3639ae7f5e8SEduard Zingerman 		/*
3649ae7f5e8SEduard Zingerman 		 * The algorithm should be very fast in practice,
3659ae7f5e8SEduard Zingerman 		 * guard against pathological inputs, just in case.
3669ae7f5e8SEduard Zingerman 		 */
3679ae7f5e8SEduard Zingerman 		if ((++i % 1024) == 0) {
3689ae7f5e8SEduard Zingerman 			if (signal_pending(current))
3699ae7f5e8SEduard Zingerman 				return -EAGAIN;
3709ae7f5e8SEduard Zingerman 			cond_resched();
3719ae7f5e8SEduard Zingerman 		}
3729ae7f5e8SEduard Zingerman 
3739ae7f5e8SEduard Zingerman 		cur = stack[stack_sz - 1];
3749ae7f5e8SEduard Zingerman 		succ = bpf_insn_successors(env, cur);
3759ae7f5e8SEduard Zingerman 		if (state[cur].next_succ == succ->cnt) {
3769ae7f5e8SEduard Zingerman 			dfs_pos[cur] = 0;
3779ae7f5e8SEduard Zingerman 			stack_sz--;
3789ae7f5e8SEduard Zingerman 			continue;
3799ae7f5e8SEduard Zingerman 		}
3809ae7f5e8SEduard Zingerman 		s = succ->items[state[cur].next_succ];
3819ae7f5e8SEduard Zingerman 		if (!state[s].traversed) {
3829ae7f5e8SEduard Zingerman 			/* Case A:  start -> ... -> cur -> s [unexplored] */
3839ae7f5e8SEduard Zingerman 			state[s].traversed = true;
3849ae7f5e8SEduard Zingerman 			state[s].next_succ = 0;
3859ae7f5e8SEduard Zingerman 			stack[stack_sz] = s;
3869ae7f5e8SEduard Zingerman 			dfs_pos[s] = stack_sz + 1;
3879ae7f5e8SEduard Zingerman 			stack_sz++;
3889ae7f5e8SEduard Zingerman 			continue;
3899ae7f5e8SEduard Zingerman 		}
3909ae7f5e8SEduard Zingerman 		/* 's' is fully explored at this point */
3919ae7f5e8SEduard Zingerman 		if (dfs_pos[s]) {
3929ae7f5e8SEduard Zingerman 			/*
3939ae7f5e8SEduard Zingerman 			 * start -> ... -> s -> cur --.
3949ae7f5e8SEduard Zingerman 			 *                 ^          |
3959ae7f5e8SEduard Zingerman 			 *                 '----------'
3969ae7f5e8SEduard Zingerman 			 * Case B: 's' is in the current DFS path.
3979ae7f5e8SEduard Zingerman 			 */
3989ae7f5e8SEduard Zingerman 			err = assign_header(env, dfs, cur, s);
3999ae7f5e8SEduard Zingerman 			if (err)
4009ae7f5e8SEduard Zingerman 				return err;
4019ae7f5e8SEduard Zingerman 			add_backedge(env, cur, s);
4029ae7f5e8SEduard Zingerman 		} else if (aux[s].loop_header == -1) {
4039ae7f5e8SEduard Zingerman 			/*
4049ae7f5e8SEduard Zingerman 			 * start -> ... -> ... -> s -> ... -> end
4059ae7f5e8SEduard Zingerman 			 *           |            ^
4069ae7f5e8SEduard Zingerman 			 *           '---> cur ---'
4079ae7f5e8SEduard Zingerman 			 * Case C: 's' is explored, not in the current DFS path,
4089ae7f5e8SEduard Zingerman 			 * and not a part of any loop.
4099ae7f5e8SEduard Zingerman 			 */
4109ae7f5e8SEduard Zingerman 		} else if (dfs_pos[aux[s].loop_header]) {
4119ae7f5e8SEduard Zingerman 			/*
4129ae7f5e8SEduard Zingerman 			 *                 .----------------------.
4139ae7f5e8SEduard Zingerman 			 *                 v                      |
4149ae7f5e8SEduard Zingerman 			 * start -> ... -> h -> ... -> ... -> s --'
4159ae7f5e8SEduard Zingerman 			 *                       |            ^
4169ae7f5e8SEduard Zingerman 			 *	                 '---> cur ---'
4179ae7f5e8SEduard Zingerman 			 * Case D: 's' is explored, not in current DFS path,
4189ae7f5e8SEduard Zingerman 			 * but its innermost loop header is.
4199ae7f5e8SEduard Zingerman 			 */
4209ae7f5e8SEduard Zingerman 			err = assign_header(env, dfs, cur, aux[s].loop_header);
4219ae7f5e8SEduard Zingerman 			if (err)
4229ae7f5e8SEduard Zingerman 				return err;
4239ae7f5e8SEduard Zingerman 		} else {
4249ae7f5e8SEduard Zingerman 			/*
4259ae7f5e8SEduard Zingerman 			 * case E: 's' is explored, not in current DFS path,
4269ae7f5e8SEduard Zingerman 			 * its innermost loop header is not in current DFS path,
4279ae7f5e8SEduard Zingerman 			 * hence 's' is another entry into the same loop.
4289ae7f5e8SEduard Zingerman 			 */
4299ae7f5e8SEduard Zingerman 			h = aux[s].loop_header;
4309ae7f5e8SEduard Zingerman 			mark_irreducible(env, h);
4319ae7f5e8SEduard Zingerman 			while (aux[h].loop_header != -1) {
4329ae7f5e8SEduard Zingerman 				h = aux[h].loop_header;
4339ae7f5e8SEduard Zingerman 				if (dfs_pos[h]) {
4349ae7f5e8SEduard Zingerman 					err = assign_header(env, dfs, cur, h);
4359ae7f5e8SEduard Zingerman 					if (err)
4369ae7f5e8SEduard Zingerman 						return err;
4379ae7f5e8SEduard Zingerman 					break;
4389ae7f5e8SEduard Zingerman 				}
4399ae7f5e8SEduard Zingerman 				mark_irreducible(env, h);
4409ae7f5e8SEduard Zingerman 			}
4419ae7f5e8SEduard Zingerman 		}
4429ae7f5e8SEduard Zingerman 		state[cur].next_succ++;
4439ae7f5e8SEduard Zingerman 	} while (stack_sz);
4449ae7f5e8SEduard Zingerman 
4459ae7f5e8SEduard Zingerman 	return 0;
4469ae7f5e8SEduard Zingerman }
4479ae7f5e8SEduard Zingerman 
4489ae7f5e8SEduard Zingerman bool bpf_is_nested_loop(struct bpf_verifier_env *env, int inner_header, int outer_header)
4499ae7f5e8SEduard Zingerman {
4509ae7f5e8SEduard Zingerman 	struct bpf_insn_aux_data *aux = env->insn_aux_data;
4519ae7f5e8SEduard Zingerman 	int idx;
4529ae7f5e8SEduard Zingerman 
4539ae7f5e8SEduard Zingerman 	for (idx = inner_header; idx >= 0; idx = aux[idx].loop_header)
4549ae7f5e8SEduard Zingerman 		if (aux[idx].loop_header == outer_header)
4559ae7f5e8SEduard Zingerman 			return true;
4569ae7f5e8SEduard Zingerman 
4579ae7f5e8SEduard Zingerman 	return false;
4589ae7f5e8SEduard Zingerman }
4599ae7f5e8SEduard Zingerman 
4609ae7f5e8SEduard Zingerman int bpf_compute_loops(struct bpf_verifier_env *env)
4619ae7f5e8SEduard Zingerman {
4629ae7f5e8SEduard Zingerman 	int i, j, k, s, t, iloop, sloop, err = 0, len = env->prog->len;
4639ae7f5e8SEduard Zingerman 	struct bpf_insn_aux_data *aux = env->insn_aux_data;
4649ae7f5e8SEduard Zingerman 	struct bpf_verifier_log *log = &env->log;
4659ae7f5e8SEduard Zingerman 	struct bpf_backedge *backedge;
4669ae7f5e8SEduard Zingerman 	struct loops_dfs dfs = {};
4679ae7f5e8SEduard Zingerman 	struct bpf_iarray *succ;
4689ae7f5e8SEduard Zingerman 	struct bpf_loop *loop;
4699ae7f5e8SEduard Zingerman 
4709ae7f5e8SEduard Zingerman 	dfs.dfs_pos = kvcalloc(len, sizeof(int), GFP_KERNEL_ACCOUNT);
4719ae7f5e8SEduard Zingerman 	dfs.state = kvcalloc(len, sizeof(struct dfs_state), GFP_KERNEL_ACCOUNT);
4729ae7f5e8SEduard Zingerman 	dfs.stack = kvcalloc(len, sizeof(int), GFP_KERNEL_ACCOUNT);
4739ae7f5e8SEduard Zingerman 	if (!dfs.dfs_pos || !dfs.state || !dfs.stack) {
4749ae7f5e8SEduard Zingerman 		err = -ENOMEM;
4759ae7f5e8SEduard Zingerman 		goto out;
4769ae7f5e8SEduard Zingerman 	}
4779ae7f5e8SEduard Zingerman 	for (i = 0; i < len; i++)
4789ae7f5e8SEduard Zingerman 		aux[i].loop_header = -1;
4799ae7f5e8SEduard Zingerman 	for (i = 0; i < env->subprog_cnt; i++) {
4809ae7f5e8SEduard Zingerman 		err = compute_loops_in_subprog(env, &dfs, i);
4819ae7f5e8SEduard Zingerman 		if (err)
4829ae7f5e8SEduard Zingerman 			goto out;
4839ae7f5e8SEduard Zingerman 	}
4849ae7f5e8SEduard Zingerman 	/* find latches */
4859ae7f5e8SEduard Zingerman 	for (i = 0; i < len; i++) {
4869ae7f5e8SEduard Zingerman 		loop = aux[i].loop;
4879ae7f5e8SEduard Zingerman 		if (!loop)
4889ae7f5e8SEduard Zingerman 			continue;
4899ae7f5e8SEduard Zingerman 		for (j = 0; j < loop->backedges_cnt; j++) {
4909ae7f5e8SEduard Zingerman 			backedge = &loop->backedges[j];
4919ae7f5e8SEduard Zingerman 			/*
4929ae7f5e8SEduard Zingerman 			 * In theory, the backedge->from and it's latch can reside in
4939ae7f5e8SEduard Zingerman 			 * an inner loop of `i`, e.g.:
4949ae7f5e8SEduard Zingerman 			 *
4959ae7f5e8SEduard Zingerman 			 *  1:  r7 += 1;
4969ae7f5e8SEduard Zingerman 			 *  2:  r6 += 1;
4979ae7f5e8SEduard Zingerman 			 *      if r7 == 2 goto 1b;
4989ae7f5e8SEduard Zingerman 			 *      if r6 < 2 goto 2b;
4999ae7f5e8SEduard Zingerman 			 *
5009ae7f5e8SEduard Zingerman 			 * For now, let's assume that latches are unknown for such cases.
5019ae7f5e8SEduard Zingerman 			 * (GCC/LLVM handle this by inserting artificial cfg nodes).
5029ae7f5e8SEduard Zingerman 			 */
5039ae7f5e8SEduard Zingerman 			if (bpf_loop_at_index(env, backedge->from) != i)
5049ae7f5e8SEduard Zingerman 				continue;
5059ae7f5e8SEduard Zingerman 			find_dominating_conditions(env, backedge->from, backedge);
5069ae7f5e8SEduard Zingerman 		}
5079ae7f5e8SEduard Zingerman 	}
5089ae7f5e8SEduard Zingerman 	/* find exits */
5099ae7f5e8SEduard Zingerman 	for (i = 0; i < len; i++) {
5109ae7f5e8SEduard Zingerman 		iloop = aux[i].loop ? i : aux[i].loop_header;
5119ae7f5e8SEduard Zingerman 		if (iloop < 0)
5129ae7f5e8SEduard Zingerman 			continue;
5139ae7f5e8SEduard Zingerman 		succ = bpf_insn_successors(env, i);
5149ae7f5e8SEduard Zingerman 		iarray_for_each(s, succ) {
5159ae7f5e8SEduard Zingerman 			/*
5169ae7f5e8SEduard Zingerman 			 * Nothing left to record once the innermost loop of 'i'
5179ae7f5e8SEduard Zingerman 			 * overflowed: the walk below would stop at it right away.
5189ae7f5e8SEduard Zingerman 			 */
5199ae7f5e8SEduard Zingerman 			if (aux[iloop].loop->exits_overflow)
5209ae7f5e8SEduard Zingerman 				break;
5219ae7f5e8SEduard Zingerman 			sloop = aux[s].loop ? s : aux[s].loop_header;
5229ae7f5e8SEduard Zingerman 			if (iloop == sloop)
5239ae7f5e8SEduard Zingerman 				continue;
5249ae7f5e8SEduard Zingerman 			if (bpf_is_nested_loop(env, sloop, iloop))
5259ae7f5e8SEduard Zingerman 				continue;
5269ae7f5e8SEduard Zingerman 			/*
5279ae7f5e8SEduard Zingerman 			 * At this point 'sloop' is either -1, an outer loop,
5289ae7f5e8SEduard Zingerman 			 * or a loop in another branch of the loop hierarchy.
5299ae7f5e8SEduard Zingerman 			 */
5309ae7f5e8SEduard Zingerman 			for (t = iloop; t >= 0 && t != sloop; t = aux[t].loop_header) {
5319ae7f5e8SEduard Zingerman 				/*
5329ae7f5e8SEduard Zingerman 				 * Account for the following configuration:
5339ae7f5e8SEduard Zingerman 				 *
5349ae7f5e8SEduard Zingerman 				 *   tloop {
5359ae7f5e8SEduard Zingerman 				 *     iloop {
5369ae7f5e8SEduard Zingerman 				 *       ... i: goto s;
5379ae7f5e8SEduard Zingerman 				 *     }
5389ae7f5e8SEduard Zingerman 				 *     sloop {
5399ae7f5e8SEduard Zingerman 				 *   s:
5409ae7f5e8SEduard Zingerman 				 *       ...
5419ae7f5e8SEduard Zingerman 				 *     }
5429ae7f5e8SEduard Zingerman 				 *   }
5439ae7f5e8SEduard Zingerman 				 */
5449ae7f5e8SEduard Zingerman 				if (bpf_is_nested_loop(env, sloop, t))
5459ae7f5e8SEduard Zingerman 					break;
5469ae7f5e8SEduard Zingerman 				/*
5479ae7f5e8SEduard Zingerman 				 * Record edges i -> s as exits from tloop when:
5489ae7f5e8SEduard Zingerman 				 *
5499ae7f5e8SEduard Zingerman 				 *   sloop {
5509ae7f5e8SEduard Zingerman 				 *     tloop {
5519ae7f5e8SEduard Zingerman 				 *       iloop {
5529ae7f5e8SEduard Zingerman 				 *         ... i: goto s;
5539ae7f5e8SEduard Zingerman 				 *       }
5549ae7f5e8SEduard Zingerman 				 *     }
5559ae7f5e8SEduard Zingerman 				 *  s: ...
5569ae7f5e8SEduard Zingerman 				 *   }
5579ae7f5e8SEduard Zingerman 				 */
5589ae7f5e8SEduard Zingerman 				/* Enclosing loops inherit the overflow, see below */
5599ae7f5e8SEduard Zingerman 				if (aux[t].loop->exits_overflow)
5609ae7f5e8SEduard Zingerman 					break;
5619ae7f5e8SEduard Zingerman 				err = add_exit(aux[t].loop, i, s);
5629ae7f5e8SEduard Zingerman 				if (err)
5639ae7f5e8SEduard Zingerman 					goto out;
5649ae7f5e8SEduard Zingerman 			}
5659ae7f5e8SEduard Zingerman 		}
5669ae7f5e8SEduard Zingerman 		if (bpf_is_ldimm64(env->prog->insnsi + i))
5679ae7f5e8SEduard Zingerman 			i++;
5689ae7f5e8SEduard Zingerman 	}
5699ae7f5e8SEduard Zingerman 	/* A nested loop with a truncated exit list can't be abstracted by SCEV */
5709ae7f5e8SEduard Zingerman 	for (i = 0; i < len; i++) {
5719ae7f5e8SEduard Zingerman 		loop = aux[i].loop;
5729ae7f5e8SEduard Zingerman 		if (!loop || !loop->exits_overflow)
5739ae7f5e8SEduard Zingerman 			continue;
5749ae7f5e8SEduard Zingerman 		for (t = aux[i].loop_header; t >= 0; t = aux[t].loop_header)
5759ae7f5e8SEduard Zingerman 			aux[t].loop->exits_overflow = true;
5769ae7f5e8SEduard Zingerman 	}
5779ae7f5e8SEduard Zingerman 
5789ae7f5e8SEduard Zingerman 	if (env->log.level & BPF_LOG_LEVEL2) {
5799ae7f5e8SEduard Zingerman 		for (i = 0; i < len; i++) {
5809ae7f5e8SEduard Zingerman 			loop = aux[i].loop;
5819ae7f5e8SEduard Zingerman 			if (!loop)
5829ae7f5e8SEduard Zingerman 				continue;
5839ae7f5e8SEduard Zingerman 			bpf_log(log, "loop at %d", i);
5849ae7f5e8SEduard Zingerman 			if (aux[i].loop_header >= 0)
5859ae7f5e8SEduard Zingerman 				bpf_log(log, ", nested in %d", aux[i].loop_header);
5869ae7f5e8SEduard Zingerman 			if (loop->irreducible)
5879ae7f5e8SEduard Zingerman 				bpf_log(log, ", irreducible");
5889ae7f5e8SEduard Zingerman 			if (loop->exits_overflow)
5899ae7f5e8SEduard Zingerman 				bpf_log(log, ", too many exits");
5909ae7f5e8SEduard Zingerman 			bpf_log(log, "\n");
5919ae7f5e8SEduard Zingerman 			for (j = 0; j < loop->backedges_cnt; j++) {
5929ae7f5e8SEduard Zingerman 				backedge = &loop->backedges[j];
5939ae7f5e8SEduard Zingerman 				bpf_log(log, "  backedge from %d", backedge->from);
5949ae7f5e8SEduard Zingerman 				for (k = 0; k < backedge->latches_cnt; k++) {
5959ae7f5e8SEduard Zingerman 					if (k == 0)
5969ae7f5e8SEduard Zingerman 						bpf_log(log, ", latch at ");
5979ae7f5e8SEduard Zingerman 					if (k == 1)
5989ae7f5e8SEduard Zingerman 						bpf_log(log, ", dominating exits at ");
5999ae7f5e8SEduard Zingerman 					if (k > 1)
6009ae7f5e8SEduard Zingerman 						bpf_log(log, ", ");
6019ae7f5e8SEduard Zingerman 					bpf_log(log, "%d", backedge->latches[k]);
6029ae7f5e8SEduard Zingerman 				}
6039ae7f5e8SEduard Zingerman 				bpf_log(log, "\n");
6049ae7f5e8SEduard Zingerman 			}
6059ae7f5e8SEduard Zingerman 			for (j = 0; j < loop->exits_cnt; j++)
6069ae7f5e8SEduard Zingerman 				bpf_log(log, "  exit from %d to %d\n",
6079ae7f5e8SEduard Zingerman 					loop->exits[j].from, loop->exits[j].to);
6089ae7f5e8SEduard Zingerman 		}
6099ae7f5e8SEduard Zingerman 	}
6109ae7f5e8SEduard Zingerman 
6119ae7f5e8SEduard Zingerman out:
6129ae7f5e8SEduard Zingerman 	kvfree(dfs.dfs_pos);
6139ae7f5e8SEduard Zingerman 	kvfree(dfs.stack);
6149ae7f5e8SEduard Zingerman 	kvfree(dfs.state);
6159ae7f5e8SEduard Zingerman 	return err;
6169ae7f5e8SEduard Zingerman }
617