xref: /linux/kernel/bpf/loops.c (revision 2fa579694ddf3a96454669c764ca0c06dea50817)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
3 
4 #include <linux/slab.h>
5 #include <linux/sched/signal.h>
6 #include <linux/bpf_verifier.h>
7 
8 static bool is_cfg_jump(struct bpf_insn *insn)
9 {
10 	u8 class = BPF_CLASS(insn->code);
11 	u8 opcode = BPF_OP(insn->code);
12 
13 	switch (class) {
14 	case BPF_JMP:
15 	case BPF_JMP32:
16 		return opcode != BPF_CALL;
17 	default:
18 		return false;
19 	}
20 }
21 
22 static struct bpf_iarray **compute_predecessors(struct bpf_verifier_env *env)
23 {
24 	struct bpf_insn_aux_data *aux = env->insn_aux_data;
25 	struct bpf_iarray *succ, *preds, **result;
26 	struct bpf_prog *prog = env->prog;
27 	u32 *num_preds, i, s, len = prog->len;
28 	struct bpf_insn *insn;
29 	size_t sz;
30 	void *tmp;
31 
32 	num_preds = kvcalloc(prog->len, sizeof(u32), GFP_KERNEL_ACCOUNT);
33 	if (!num_preds)
34 		return NULL;
35 
36 	/*
37 	 * 'result' layout:
38 	 *  - array of pointers (struct bpf_iarray *)[len]
39 	 *  - struct bpf_iarray one after another
40 	 */
41 	sz = size_mul(len, sizeof(*result) + sizeof(**result));
42 	for (i = 0; i < len; i++) {
43 		insn = env->prog->insnsi + i;
44 		succ = bpf_insn_successors(env, i);
45 		sz = size_add(sz, array_size(succ->cnt, sizeof(*succ->items)));
46 		iarray_for_each(s, succ) {
47 			num_preds[s]++;
48 		}
49 		if (bpf_is_ldimm64(insn))
50 			i++;
51 	}
52 
53 	result = kvzalloc(sz, GFP_KERNEL_ACCOUNT);
54 	if (!result) {
55 		kvfree(num_preds);
56 		return NULL;
57 	}
58 
59 	tmp = (void *)&result[len];
60 	for (i = 0; i < len; i++) {
61 		result[i] = tmp;
62 		tmp += struct_size(result[i], items, num_preds[i]);
63 	}
64 
65 	for (i = 0; i < len; i++) {
66 		insn = env->prog->insnsi + i;
67 		succ = bpf_insn_successors(env, i);
68 		iarray_for_each(s, succ) {
69 			preds = result[s];
70 			preds->items[preds->cnt++] = i;
71 		}
72 		if (succ->cnt > 1 ||
73 		    (succ->cnt == 1 && num_preds[succ->items[0]] > 1) ||
74 		    is_cfg_jump(insn))
75 			aux[i].bb_end = true;
76 		if (bpf_is_ldimm64(insn))
77 			i++;
78 	}
79 
80 	kvfree(num_preds);
81 	return result;
82 }
83 
84 static int idoms_intersect(struct bpf_verifier_env *env, int a, int b)
85 {
86 	int *postorder_nums = env->cfg.postorder_nums;
87 	int *idoms = env->idoms;
88 
89 	while (a != b) {
90 		while (postorder_nums[a] < postorder_nums[b]) {
91 			a = idoms[a];
92 		}
93 		while (postorder_nums[b] < postorder_nums[a]) {
94 			b = idoms[b];
95 		}
96 	}
97 	return a;
98 }
99 
100 /* See "A Simple, Fast Dominance Algorithm" by Cooper et al. for details. */
101 static int compute_subprog_idoms(struct bpf_verifier_env *env, struct bpf_iarray **preds,
102 				int subprog_idx)
103 {
104 	struct bpf_subprog_info *subprog = &env->subprog_info[subprog_idx];
105 	int start = subprog->start;
106 	int po_first = subprog->postorder_start;
107 	int po_last = (subprog + 1)->postorder_start - 1;
108 	int *idoms = env->idoms;
109 	int po_num, pred;
110 	u32 work = 0;
111 	bool changed;
112 
113 	idoms[start] = start;
114 	changed = true;
115 	do {
116 		changed = false;
117 		/*
118 		 * Iterate in reverse postorder, skipping the subprog entry:
119 		 * insn_postorder[po_last] is the subprogram entry.
120 		 */
121 		for (po_num = po_last - 1; po_num >= po_first; po_num--) {
122 			int idx = env->cfg.insn_postorder[po_num];
123 			int new_idom = -1;
124 
125 			iarray_for_each(pred, preds[idx]) {
126 				if (++work % 1024 == 0) {
127 					if (signal_pending(current))
128 						return -EAGAIN;
129 					cond_resched();
130 				}
131 
132 				if (idoms[pred] == -1)
133 					continue;
134 				if (new_idom == -1)
135 					new_idom = pred;
136 				else
137 					new_idom = idoms_intersect(env, pred, new_idom);
138 			}
139 			if (new_idom != -1 && idoms[idx] != new_idom) {
140 				idoms[idx] = new_idom;
141 				changed = true;
142 			}
143 		}
144 	} while (changed);
145 	idoms[start] = -1;
146 	return 0;
147 }
148 
149 int bpf_compute_idoms(struct bpf_verifier_env *env)
150 {
151 	struct bpf_iarray **preds;
152 	u32 len = env->prog->len;
153 	int *idoms, i, err = 0;
154 
155 	preds = compute_predecessors(env);
156 	if (!preds)
157 		return -ENOMEM;
158 
159 	idoms = kvcalloc(len, sizeof(*idoms), GFP_KERNEL_ACCOUNT);
160 	if (!idoms) {
161 		kvfree(preds);
162 		return -ENOMEM;
163 	}
164 
165 	env->idoms = idoms;
166 	for (i = 0; i < len; i++)
167 		idoms[i] = -1;
168 
169 	for (i = 0; i < env->subprog_cnt; i++) {
170 		err = compute_subprog_idoms(env, preds, i);
171 		if (err)
172 			break;
173 	}
174 
175 	kvfree(preds);
176 	return err;
177 }
178 
179 struct dfs_state {
180 	u32 traversed:1;
181 	u32 next_succ:31;
182 };
183 
184 struct loops_dfs {
185 	struct dfs_state *state;
186 	int *dfs_pos;
187 	int *stack;
188 };
189 
190 static void mark_irreducible(struct bpf_verifier_env *env, int h)
191 {
192 	env->insn_aux_data[h].loop->irreducible = true;
193 }
194 
195 static void add_backedge(struct bpf_verifier_env *env, int from, int h)
196 {
197 	struct bpf_insn_aux_data *aux = env->insn_aux_data;
198 	struct bpf_loop *loop = aux[h].loop;
199 	int cnt = loop->backedges_cnt;
200 
201 	if (cnt == MAX_BACKEDGES) {
202 		loop->backedges_overflow = true;
203 		return;
204 	}
205 	loop->backedges[cnt].from = from;
206 	loop->backedges[cnt].latches_cnt = 0;
207 	loop->backedges_cnt++;
208 }
209 
210 static int add_exit(struct bpf_loop *loop, int from, int to)
211 {
212 	if (loop->exits_overflow)
213 		return 0;
214 	if (loop->exits_cnt == MAX_LOOP_EXITS) {
215 		loop->exits_overflow = true;
216 		return 0;
217 	}
218 	if (!loop->exits) {
219 		loop->exits = kvcalloc(MAX_LOOP_EXITS, sizeof(*loop->exits), GFP_KERNEL_ACCOUNT);
220 		if (!loop->exits)
221 			return -ENOMEM;
222 	}
223 	loop->exits[loop->exits_cnt] = (struct bpf_loop_exit) {
224 		.from = from,
225 		.to = to,
226 	};
227 	loop->exits_cnt++;
228 	return 0;
229 }
230 
231 static int mark_as_header(struct bpf_verifier_env *env, int h)
232 {
233 	struct bpf_insn_aux_data *aux = env->insn_aux_data;
234 
235 	if (!aux[h].loop) {
236 		aux[h].loop = kvzalloc_obj(struct bpf_loop, GFP_KERNEL_ACCOUNT);
237 		if (!aux[h].loop)
238 			return -ENOMEM;
239 	}
240 	return 0;
241 }
242 
243 static int assign_header(struct bpf_verifier_env *env, struct loops_dfs *dfs, int n, int h)
244 {
245 	struct bpf_insn_aux_data *aux = env->insn_aux_data;
246 	int *dfs_pos = dfs->dfs_pos;
247 	int err, nh;
248 
249 	err = mark_as_header(env, h);
250 	if (err)
251 		return err;
252 
253 	/* Don't encode self-loops, otherwise can't reflect loops nesting structure. */
254 	if (n == h)
255 		return 0;
256 
257 	/* Make sure that loop headers up the chain are sorted by dfs_pos. */
258 	while (aux[n].loop_header != -1) {
259 		nh = aux[n].loop_header;
260 		if (nh == h)
261 			return 0;
262 		if (dfs_pos[nh] < dfs_pos[h]) {
263 			aux[n].loop_header = h;
264 			n = h;
265 			h = nh;
266 		} else {
267 			n = nh;
268 		}
269 	}
270 	aux[n].loop_header = h;
271 	return 0;
272 }
273 
274 static bool is_cond_jmp_insn(struct bpf_insn *insn)
275 {
276 	u8 class = BPF_CLASS(insn->code);
277 	u8 opcode = BPF_OP(insn->code);
278 
279 	if (class != BPF_JMP && class != BPF_JMP32)
280 		return false;
281 
282 	switch (opcode) {
283 	case BPF_JEQ:
284 	case BPF_JGE:
285 	case BPF_JGT:
286 	case BPF_JLE:
287 	case BPF_JLT:
288 	case BPF_JNE:
289 	case BPF_JSET:
290 	case BPF_JSGE:
291 	case BPF_JSGT:
292 	case BPF_JSLE:
293 	case BPF_JSLT:
294 		return true;
295 	default:
296 		return false;
297 	}
298 }
299 
300 int bpf_loop_at_index(struct bpf_verifier_env *env, u32 idx)
301 {
302 	struct bpf_insn_aux_data *aux = env->insn_aux_data;
303 
304 	return aux[idx].loop ? idx : aux[idx].loop_header;
305 }
306 
307 /*
308  * Collect conditional jumps within n's loop that dominate 'n' and have
309  * a successor outside the loop, nearest first.
310  */
311 static void find_dominating_conditions(struct bpf_verifier_env *env, int n,
312 				       struct bpf_backedge *backedge)
313 {
314 	struct bpf_insn *insns = env->prog->insnsi;
315 	bool t_exits_loop, f_exits_loop;
316 	int n_loop, t_loop, f_loop;
317 	int *idoms = env->idoms;
318 
319 	n_loop = bpf_loop_at_index(env, n);
320 	if (idoms_intersect(env, n, n_loop) != n_loop)
321 		return;
322 	while (n >= 0) {
323 		if (is_cond_jmp_insn(&insns[n]) && bpf_loop_at_index(env, n) == n_loop) {
324 			t_loop = bpf_loop_at_index(env, n + insns[n].off + 1);
325 			f_loop = bpf_loop_at_index(env, n + 1);
326 			t_exits_loop = t_loop != n_loop && !bpf_is_nested_loop(env, t_loop, n_loop);
327 			f_exits_loop = f_loop != n_loop && !bpf_is_nested_loop(env, f_loop, n_loop);
328 			if (t_exits_loop || f_exits_loop) {
329 				if (backedge->latches_cnt == MAX_LATCHES)
330 					return;
331 				backedge->latches[backedge->latches_cnt++] = n;
332 			}
333 		}
334 		if (n == n_loop)
335 			break;
336 		n = idoms[n];
337 	}
338 }
339 
340 /*
341  * As described in "A New Algorithm for Identifying Loops in Decompilation" by Wei et al,
342  * adapted to be non-recursive.
343  */
344 static int compute_loops_in_subprog(struct bpf_verifier_env *env, struct loops_dfs *dfs,
345 				    int subprog_idx)
346 {
347 	struct bpf_insn_aux_data *aux = env->insn_aux_data;
348 	struct dfs_state *state = dfs->state;
349 	int start = env->subprog_info[subprog_idx].start;
350 	int *dfs_pos = dfs->dfs_pos;
351 	int *stack = dfs->stack;
352 	int s, h, err, cur, stack_sz;
353 	struct bpf_iarray *succ;
354 	u32 i;
355 
356 	stack[0] = start;
357 	state[start].traversed = true;
358 	state[start].next_succ = 0;
359 	dfs_pos[start] = 1;
360 	stack_sz = 1;
361 	i = 0;
362 	do {
363 		/*
364 		 * The algorithm should be very fast in practice,
365 		 * guard against pathological inputs, just in case.
366 		 */
367 		if ((++i % 1024) == 0) {
368 			if (signal_pending(current))
369 				return -EAGAIN;
370 			cond_resched();
371 		}
372 
373 		cur = stack[stack_sz - 1];
374 		succ = bpf_insn_successors(env, cur);
375 		if (state[cur].next_succ == succ->cnt) {
376 			dfs_pos[cur] = 0;
377 			stack_sz--;
378 			continue;
379 		}
380 		s = succ->items[state[cur].next_succ];
381 		if (!state[s].traversed) {
382 			/* Case A:  start -> ... -> cur -> s [unexplored] */
383 			state[s].traversed = true;
384 			state[s].next_succ = 0;
385 			stack[stack_sz] = s;
386 			dfs_pos[s] = stack_sz + 1;
387 			stack_sz++;
388 			continue;
389 		}
390 		/* 's' is fully explored at this point */
391 		if (dfs_pos[s]) {
392 			/*
393 			 * start -> ... -> s -> cur --.
394 			 *                 ^          |
395 			 *                 '----------'
396 			 * Case B: 's' is in the current DFS path.
397 			 */
398 			err = assign_header(env, dfs, cur, s);
399 			if (err)
400 				return err;
401 			add_backedge(env, cur, s);
402 		} else if (aux[s].loop_header == -1) {
403 			/*
404 			 * start -> ... -> ... -> s -> ... -> end
405 			 *           |            ^
406 			 *           '---> cur ---'
407 			 * Case C: 's' is explored, not in the current DFS path,
408 			 * and not a part of any loop.
409 			 */
410 		} else if (dfs_pos[aux[s].loop_header]) {
411 			/*
412 			 *                 .----------------------.
413 			 *                 v                      |
414 			 * start -> ... -> h -> ... -> ... -> s --'
415 			 *                       |            ^
416 			 *	                 '---> cur ---'
417 			 * Case D: 's' is explored, not in current DFS path,
418 			 * but its innermost loop header is.
419 			 */
420 			err = assign_header(env, dfs, cur, aux[s].loop_header);
421 			if (err)
422 				return err;
423 		} else {
424 			/*
425 			 * case E: 's' is explored, not in current DFS path,
426 			 * its innermost loop header is not in current DFS path,
427 			 * hence 's' is another entry into the same loop.
428 			 */
429 			h = aux[s].loop_header;
430 			mark_irreducible(env, h);
431 			while (aux[h].loop_header != -1) {
432 				h = aux[h].loop_header;
433 				if (dfs_pos[h]) {
434 					err = assign_header(env, dfs, cur, h);
435 					if (err)
436 						return err;
437 					break;
438 				}
439 				mark_irreducible(env, h);
440 			}
441 		}
442 		state[cur].next_succ++;
443 	} while (stack_sz);
444 
445 	return 0;
446 }
447 
448 bool bpf_is_nested_loop(struct bpf_verifier_env *env, int inner_header, int outer_header)
449 {
450 	struct bpf_insn_aux_data *aux = env->insn_aux_data;
451 	int idx;
452 
453 	for (idx = inner_header; idx >= 0; idx = aux[idx].loop_header)
454 		if (aux[idx].loop_header == outer_header)
455 			return true;
456 
457 	return false;
458 }
459 
460 int bpf_compute_loops(struct bpf_verifier_env *env)
461 {
462 	int i, j, k, s, t, iloop, sloop, err = 0, len = env->prog->len;
463 	struct bpf_insn_aux_data *aux = env->insn_aux_data;
464 	struct bpf_verifier_log *log = &env->log;
465 	struct bpf_backedge *backedge;
466 	struct loops_dfs dfs = {};
467 	struct bpf_iarray *succ;
468 	struct bpf_loop *loop;
469 
470 	dfs.dfs_pos = kvcalloc(len, sizeof(int), GFP_KERNEL_ACCOUNT);
471 	dfs.state = kvcalloc(len, sizeof(struct dfs_state), GFP_KERNEL_ACCOUNT);
472 	dfs.stack = kvcalloc(len, sizeof(int), GFP_KERNEL_ACCOUNT);
473 	if (!dfs.dfs_pos || !dfs.state || !dfs.stack) {
474 		err = -ENOMEM;
475 		goto out;
476 	}
477 	for (i = 0; i < len; i++)
478 		aux[i].loop_header = -1;
479 	for (i = 0; i < env->subprog_cnt; i++) {
480 		err = compute_loops_in_subprog(env, &dfs, i);
481 		if (err)
482 			goto out;
483 	}
484 	/* find latches */
485 	for (i = 0; i < len; i++) {
486 		loop = aux[i].loop;
487 		if (!loop)
488 			continue;
489 		for (j = 0; j < loop->backedges_cnt; j++) {
490 			backedge = &loop->backedges[j];
491 			/*
492 			 * In theory, the backedge->from and it's latch can reside in
493 			 * an inner loop of `i`, e.g.:
494 			 *
495 			 *  1:  r7 += 1;
496 			 *  2:  r6 += 1;
497 			 *      if r7 == 2 goto 1b;
498 			 *      if r6 < 2 goto 2b;
499 			 *
500 			 * For now, let's assume that latches are unknown for such cases.
501 			 * (GCC/LLVM handle this by inserting artificial cfg nodes).
502 			 */
503 			if (bpf_loop_at_index(env, backedge->from) != i)
504 				continue;
505 			find_dominating_conditions(env, backedge->from, backedge);
506 		}
507 	}
508 	/* find exits */
509 	for (i = 0; i < len; i++) {
510 		iloop = aux[i].loop ? i : aux[i].loop_header;
511 		if (iloop < 0)
512 			continue;
513 		succ = bpf_insn_successors(env, i);
514 		iarray_for_each(s, succ) {
515 			/*
516 			 * Nothing left to record once the innermost loop of 'i'
517 			 * overflowed: the walk below would stop at it right away.
518 			 */
519 			if (aux[iloop].loop->exits_overflow)
520 				break;
521 			sloop = aux[s].loop ? s : aux[s].loop_header;
522 			if (iloop == sloop)
523 				continue;
524 			if (bpf_is_nested_loop(env, sloop, iloop))
525 				continue;
526 			/*
527 			 * At this point 'sloop' is either -1, an outer loop,
528 			 * or a loop in another branch of the loop hierarchy.
529 			 */
530 			for (t = iloop; t >= 0 && t != sloop; t = aux[t].loop_header) {
531 				/*
532 				 * Account for the following configuration:
533 				 *
534 				 *   tloop {
535 				 *     iloop {
536 				 *       ... i: goto s;
537 				 *     }
538 				 *     sloop {
539 				 *   s:
540 				 *       ...
541 				 *     }
542 				 *   }
543 				 */
544 				if (bpf_is_nested_loop(env, sloop, t))
545 					break;
546 				/*
547 				 * Record edges i -> s as exits from tloop when:
548 				 *
549 				 *   sloop {
550 				 *     tloop {
551 				 *       iloop {
552 				 *         ... i: goto s;
553 				 *       }
554 				 *     }
555 				 *  s: ...
556 				 *   }
557 				 */
558 				/* Enclosing loops inherit the overflow, see below */
559 				if (aux[t].loop->exits_overflow)
560 					break;
561 				err = add_exit(aux[t].loop, i, s);
562 				if (err)
563 					goto out;
564 			}
565 		}
566 		if (bpf_is_ldimm64(env->prog->insnsi + i))
567 			i++;
568 	}
569 	/* A nested loop with a truncated exit list can't be abstracted by SCEV */
570 	for (i = 0; i < len; i++) {
571 		loop = aux[i].loop;
572 		if (!loop || !loop->exits_overflow)
573 			continue;
574 		for (t = aux[i].loop_header; t >= 0; t = aux[t].loop_header)
575 			aux[t].loop->exits_overflow = true;
576 	}
577 
578 	if (env->log.level & BPF_LOG_LEVEL2) {
579 		for (i = 0; i < len; i++) {
580 			loop = aux[i].loop;
581 			if (!loop)
582 				continue;
583 			bpf_log(log, "loop at %d", i);
584 			if (aux[i].loop_header >= 0)
585 				bpf_log(log, ", nested in %d", aux[i].loop_header);
586 			if (loop->irreducible)
587 				bpf_log(log, ", irreducible");
588 			if (loop->exits_overflow)
589 				bpf_log(log, ", too many exits");
590 			bpf_log(log, "\n");
591 			for (j = 0; j < loop->backedges_cnt; j++) {
592 				backedge = &loop->backedges[j];
593 				bpf_log(log, "  backedge from %d", backedge->from);
594 				for (k = 0; k < backedge->latches_cnt; k++) {
595 					if (k == 0)
596 						bpf_log(log, ", latch at ");
597 					if (k == 1)
598 						bpf_log(log, ", dominating exits at ");
599 					if (k > 1)
600 						bpf_log(log, ", ");
601 					bpf_log(log, "%d", backedge->latches[k]);
602 				}
603 				bpf_log(log, "\n");
604 			}
605 			for (j = 0; j < loop->exits_cnt; j++)
606 				bpf_log(log, "  exit from %d to %d\n",
607 					loop->exits[j].from, loop->exits[j].to);
608 		}
609 	}
610 
611 out:
612 	kvfree(dfs.dfs_pos);
613 	kvfree(dfs.stack);
614 	kvfree(dfs.state);
615 	return err;
616 }
617