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