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