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