1*f8a8faceSAlexei Starovoitov // SPDX-License-Identifier: GPL-2.0-only 2*f8a8faceSAlexei Starovoitov /* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */ 3*f8a8faceSAlexei Starovoitov #include <linux/bpf.h> 4*f8a8faceSAlexei Starovoitov #include <linux/bpf_verifier.h> 5*f8a8faceSAlexei Starovoitov #include <linux/filter.h> 6*f8a8faceSAlexei Starovoitov #include <linux/sort.h> 7*f8a8faceSAlexei Starovoitov 8*f8a8faceSAlexei Starovoitov #define verbose(env, fmt, args...) bpf_verifier_log_write(env, fmt, ##args) 9*f8a8faceSAlexei Starovoitov 10*f8a8faceSAlexei Starovoitov /* non-recursive DFS pseudo code 11*f8a8faceSAlexei Starovoitov * 1 procedure DFS-iterative(G,v): 12*f8a8faceSAlexei Starovoitov * 2 label v as discovered 13*f8a8faceSAlexei Starovoitov * 3 let S be a stack 14*f8a8faceSAlexei Starovoitov * 4 S.push(v) 15*f8a8faceSAlexei Starovoitov * 5 while S is not empty 16*f8a8faceSAlexei Starovoitov * 6 t <- S.peek() 17*f8a8faceSAlexei Starovoitov * 7 if t is what we're looking for: 18*f8a8faceSAlexei Starovoitov * 8 return t 19*f8a8faceSAlexei Starovoitov * 9 for all edges e in G.adjacentEdges(t) do 20*f8a8faceSAlexei Starovoitov * 10 if edge e is already labelled 21*f8a8faceSAlexei Starovoitov * 11 continue with the next edge 22*f8a8faceSAlexei Starovoitov * 12 w <- G.adjacentVertex(t,e) 23*f8a8faceSAlexei Starovoitov * 13 if vertex w is not discovered and not explored 24*f8a8faceSAlexei Starovoitov * 14 label e as tree-edge 25*f8a8faceSAlexei Starovoitov * 15 label w as discovered 26*f8a8faceSAlexei Starovoitov * 16 S.push(w) 27*f8a8faceSAlexei Starovoitov * 17 continue at 5 28*f8a8faceSAlexei Starovoitov * 18 else if vertex w is discovered 29*f8a8faceSAlexei Starovoitov * 19 label e as back-edge 30*f8a8faceSAlexei Starovoitov * 20 else 31*f8a8faceSAlexei Starovoitov * 21 // vertex w is explored 32*f8a8faceSAlexei Starovoitov * 22 label e as forward- or cross-edge 33*f8a8faceSAlexei Starovoitov * 23 label t as explored 34*f8a8faceSAlexei Starovoitov * 24 S.pop() 35*f8a8faceSAlexei Starovoitov * 36*f8a8faceSAlexei Starovoitov * convention: 37*f8a8faceSAlexei Starovoitov * 0x10 - discovered 38*f8a8faceSAlexei Starovoitov * 0x11 - discovered and fall-through edge labelled 39*f8a8faceSAlexei Starovoitov * 0x12 - discovered and fall-through and branch edges labelled 40*f8a8faceSAlexei Starovoitov * 0x20 - explored 41*f8a8faceSAlexei Starovoitov */ 42*f8a8faceSAlexei Starovoitov 43*f8a8faceSAlexei Starovoitov enum { 44*f8a8faceSAlexei Starovoitov DISCOVERED = 0x10, 45*f8a8faceSAlexei Starovoitov EXPLORED = 0x20, 46*f8a8faceSAlexei Starovoitov FALLTHROUGH = 1, 47*f8a8faceSAlexei Starovoitov BRANCH = 2, 48*f8a8faceSAlexei Starovoitov }; 49*f8a8faceSAlexei Starovoitov 50*f8a8faceSAlexei Starovoitov 51*f8a8faceSAlexei Starovoitov static void mark_subprog_changes_pkt_data(struct bpf_verifier_env *env, int off) 52*f8a8faceSAlexei Starovoitov { 53*f8a8faceSAlexei Starovoitov struct bpf_subprog_info *subprog; 54*f8a8faceSAlexei Starovoitov 55*f8a8faceSAlexei Starovoitov subprog = bpf_find_containing_subprog(env, off); 56*f8a8faceSAlexei Starovoitov subprog->changes_pkt_data = true; 57*f8a8faceSAlexei Starovoitov } 58*f8a8faceSAlexei Starovoitov 59*f8a8faceSAlexei Starovoitov static void mark_subprog_might_sleep(struct bpf_verifier_env *env, int off) 60*f8a8faceSAlexei Starovoitov { 61*f8a8faceSAlexei Starovoitov struct bpf_subprog_info *subprog; 62*f8a8faceSAlexei Starovoitov 63*f8a8faceSAlexei Starovoitov subprog = bpf_find_containing_subprog(env, off); 64*f8a8faceSAlexei Starovoitov subprog->might_sleep = true; 65*f8a8faceSAlexei Starovoitov } 66*f8a8faceSAlexei Starovoitov 67*f8a8faceSAlexei Starovoitov /* 't' is an index of a call-site. 68*f8a8faceSAlexei Starovoitov * 'w' is a callee entry point. 69*f8a8faceSAlexei Starovoitov * Eventually this function would be called when env->cfg.insn_state[w] == EXPLORED. 70*f8a8faceSAlexei Starovoitov * Rely on DFS traversal order and absence of recursive calls to guarantee that 71*f8a8faceSAlexei Starovoitov * callee's change_pkt_data marks would be correct at that moment. 72*f8a8faceSAlexei Starovoitov */ 73*f8a8faceSAlexei Starovoitov static void merge_callee_effects(struct bpf_verifier_env *env, int t, int w) 74*f8a8faceSAlexei Starovoitov { 75*f8a8faceSAlexei Starovoitov struct bpf_subprog_info *caller, *callee; 76*f8a8faceSAlexei Starovoitov 77*f8a8faceSAlexei Starovoitov caller = bpf_find_containing_subprog(env, t); 78*f8a8faceSAlexei Starovoitov callee = bpf_find_containing_subprog(env, w); 79*f8a8faceSAlexei Starovoitov caller->changes_pkt_data |= callee->changes_pkt_data; 80*f8a8faceSAlexei Starovoitov caller->might_sleep |= callee->might_sleep; 81*f8a8faceSAlexei Starovoitov } 82*f8a8faceSAlexei Starovoitov 83*f8a8faceSAlexei Starovoitov enum { 84*f8a8faceSAlexei Starovoitov DONE_EXPLORING = 0, 85*f8a8faceSAlexei Starovoitov KEEP_EXPLORING = 1, 86*f8a8faceSAlexei Starovoitov }; 87*f8a8faceSAlexei Starovoitov 88*f8a8faceSAlexei Starovoitov /* t, w, e - match pseudo-code above: 89*f8a8faceSAlexei Starovoitov * t - index of current instruction 90*f8a8faceSAlexei Starovoitov * w - next instruction 91*f8a8faceSAlexei Starovoitov * e - edge 92*f8a8faceSAlexei Starovoitov */ 93*f8a8faceSAlexei Starovoitov static int push_insn(int t, int w, int e, struct bpf_verifier_env *env) 94*f8a8faceSAlexei Starovoitov { 95*f8a8faceSAlexei Starovoitov int *insn_stack = env->cfg.insn_stack; 96*f8a8faceSAlexei Starovoitov int *insn_state = env->cfg.insn_state; 97*f8a8faceSAlexei Starovoitov 98*f8a8faceSAlexei Starovoitov if (e == FALLTHROUGH && insn_state[t] >= (DISCOVERED | FALLTHROUGH)) 99*f8a8faceSAlexei Starovoitov return DONE_EXPLORING; 100*f8a8faceSAlexei Starovoitov 101*f8a8faceSAlexei Starovoitov if (e == BRANCH && insn_state[t] >= (DISCOVERED | BRANCH)) 102*f8a8faceSAlexei Starovoitov return DONE_EXPLORING; 103*f8a8faceSAlexei Starovoitov 104*f8a8faceSAlexei Starovoitov if (w < 0 || w >= env->prog->len) { 105*f8a8faceSAlexei Starovoitov verbose_linfo(env, t, "%d: ", t); 106*f8a8faceSAlexei Starovoitov verbose(env, "jump out of range from insn %d to %d\n", t, w); 107*f8a8faceSAlexei Starovoitov return -EINVAL; 108*f8a8faceSAlexei Starovoitov } 109*f8a8faceSAlexei Starovoitov 110*f8a8faceSAlexei Starovoitov if (e == BRANCH) { 111*f8a8faceSAlexei Starovoitov /* mark branch target for state pruning */ 112*f8a8faceSAlexei Starovoitov mark_prune_point(env, w); 113*f8a8faceSAlexei Starovoitov mark_jmp_point(env, w); 114*f8a8faceSAlexei Starovoitov } 115*f8a8faceSAlexei Starovoitov 116*f8a8faceSAlexei Starovoitov if (insn_state[w] == 0) { 117*f8a8faceSAlexei Starovoitov /* tree-edge */ 118*f8a8faceSAlexei Starovoitov insn_state[t] = DISCOVERED | e; 119*f8a8faceSAlexei Starovoitov insn_state[w] = DISCOVERED; 120*f8a8faceSAlexei Starovoitov if (env->cfg.cur_stack >= env->prog->len) 121*f8a8faceSAlexei Starovoitov return -E2BIG; 122*f8a8faceSAlexei Starovoitov insn_stack[env->cfg.cur_stack++] = w; 123*f8a8faceSAlexei Starovoitov return KEEP_EXPLORING; 124*f8a8faceSAlexei Starovoitov } else if ((insn_state[w] & 0xF0) == DISCOVERED) { 125*f8a8faceSAlexei Starovoitov if (env->bpf_capable) 126*f8a8faceSAlexei Starovoitov return DONE_EXPLORING; 127*f8a8faceSAlexei Starovoitov verbose_linfo(env, t, "%d: ", t); 128*f8a8faceSAlexei Starovoitov verbose_linfo(env, w, "%d: ", w); 129*f8a8faceSAlexei Starovoitov verbose(env, "back-edge from insn %d to %d\n", t, w); 130*f8a8faceSAlexei Starovoitov return -EINVAL; 131*f8a8faceSAlexei Starovoitov } else if (insn_state[w] == EXPLORED) { 132*f8a8faceSAlexei Starovoitov /* forward- or cross-edge */ 133*f8a8faceSAlexei Starovoitov insn_state[t] = DISCOVERED | e; 134*f8a8faceSAlexei Starovoitov } else { 135*f8a8faceSAlexei Starovoitov verifier_bug(env, "insn state internal bug"); 136*f8a8faceSAlexei Starovoitov return -EFAULT; 137*f8a8faceSAlexei Starovoitov } 138*f8a8faceSAlexei Starovoitov return DONE_EXPLORING; 139*f8a8faceSAlexei Starovoitov } 140*f8a8faceSAlexei Starovoitov 141*f8a8faceSAlexei Starovoitov static int visit_func_call_insn(int t, struct bpf_insn *insns, 142*f8a8faceSAlexei Starovoitov struct bpf_verifier_env *env, 143*f8a8faceSAlexei Starovoitov bool visit_callee) 144*f8a8faceSAlexei Starovoitov { 145*f8a8faceSAlexei Starovoitov int ret, insn_sz; 146*f8a8faceSAlexei Starovoitov int w; 147*f8a8faceSAlexei Starovoitov 148*f8a8faceSAlexei Starovoitov insn_sz = bpf_is_ldimm64(&insns[t]) ? 2 : 1; 149*f8a8faceSAlexei Starovoitov ret = push_insn(t, t + insn_sz, FALLTHROUGH, env); 150*f8a8faceSAlexei Starovoitov if (ret) 151*f8a8faceSAlexei Starovoitov return ret; 152*f8a8faceSAlexei Starovoitov 153*f8a8faceSAlexei Starovoitov mark_prune_point(env, t + insn_sz); 154*f8a8faceSAlexei Starovoitov /* when we exit from subprog, we need to record non-linear history */ 155*f8a8faceSAlexei Starovoitov mark_jmp_point(env, t + insn_sz); 156*f8a8faceSAlexei Starovoitov 157*f8a8faceSAlexei Starovoitov if (visit_callee) { 158*f8a8faceSAlexei Starovoitov w = t + insns[t].imm + 1; 159*f8a8faceSAlexei Starovoitov mark_prune_point(env, t); 160*f8a8faceSAlexei Starovoitov merge_callee_effects(env, t, w); 161*f8a8faceSAlexei Starovoitov ret = push_insn(t, w, BRANCH, env); 162*f8a8faceSAlexei Starovoitov } 163*f8a8faceSAlexei Starovoitov return ret; 164*f8a8faceSAlexei Starovoitov } 165*f8a8faceSAlexei Starovoitov 166*f8a8faceSAlexei Starovoitov struct bpf_iarray *bpf_iarray_realloc(struct bpf_iarray *old, size_t n_elem) 167*f8a8faceSAlexei Starovoitov { 168*f8a8faceSAlexei Starovoitov size_t new_size = sizeof(struct bpf_iarray) + n_elem * sizeof(old->items[0]); 169*f8a8faceSAlexei Starovoitov struct bpf_iarray *new; 170*f8a8faceSAlexei Starovoitov 171*f8a8faceSAlexei Starovoitov new = kvrealloc(old, new_size, GFP_KERNEL_ACCOUNT); 172*f8a8faceSAlexei Starovoitov if (!new) { 173*f8a8faceSAlexei Starovoitov /* this is what callers always want, so simplify the call site */ 174*f8a8faceSAlexei Starovoitov kvfree(old); 175*f8a8faceSAlexei Starovoitov return NULL; 176*f8a8faceSAlexei Starovoitov } 177*f8a8faceSAlexei Starovoitov 178*f8a8faceSAlexei Starovoitov new->cnt = n_elem; 179*f8a8faceSAlexei Starovoitov return new; 180*f8a8faceSAlexei Starovoitov } 181*f8a8faceSAlexei Starovoitov 182*f8a8faceSAlexei Starovoitov static int copy_insn_array(struct bpf_map *map, u32 start, u32 end, u32 *items) 183*f8a8faceSAlexei Starovoitov { 184*f8a8faceSAlexei Starovoitov struct bpf_insn_array_value *value; 185*f8a8faceSAlexei Starovoitov u32 i; 186*f8a8faceSAlexei Starovoitov 187*f8a8faceSAlexei Starovoitov for (i = start; i <= end; i++) { 188*f8a8faceSAlexei Starovoitov value = map->ops->map_lookup_elem(map, &i); 189*f8a8faceSAlexei Starovoitov /* 190*f8a8faceSAlexei Starovoitov * map_lookup_elem of an array map will never return an error, 191*f8a8faceSAlexei Starovoitov * but not checking it makes some static analysers to worry 192*f8a8faceSAlexei Starovoitov */ 193*f8a8faceSAlexei Starovoitov if (IS_ERR(value)) 194*f8a8faceSAlexei Starovoitov return PTR_ERR(value); 195*f8a8faceSAlexei Starovoitov else if (!value) 196*f8a8faceSAlexei Starovoitov return -EINVAL; 197*f8a8faceSAlexei Starovoitov items[i - start] = value->xlated_off; 198*f8a8faceSAlexei Starovoitov } 199*f8a8faceSAlexei Starovoitov return 0; 200*f8a8faceSAlexei Starovoitov } 201*f8a8faceSAlexei Starovoitov 202*f8a8faceSAlexei Starovoitov static int cmp_ptr_to_u32(const void *a, const void *b) 203*f8a8faceSAlexei Starovoitov { 204*f8a8faceSAlexei Starovoitov return *(u32 *)a - *(u32 *)b; 205*f8a8faceSAlexei Starovoitov } 206*f8a8faceSAlexei Starovoitov 207*f8a8faceSAlexei Starovoitov static int sort_insn_array_uniq(u32 *items, int cnt) 208*f8a8faceSAlexei Starovoitov { 209*f8a8faceSAlexei Starovoitov int unique = 1; 210*f8a8faceSAlexei Starovoitov int i; 211*f8a8faceSAlexei Starovoitov 212*f8a8faceSAlexei Starovoitov sort(items, cnt, sizeof(items[0]), cmp_ptr_to_u32, NULL); 213*f8a8faceSAlexei Starovoitov 214*f8a8faceSAlexei Starovoitov for (i = 1; i < cnt; i++) 215*f8a8faceSAlexei Starovoitov if (items[i] != items[unique - 1]) 216*f8a8faceSAlexei Starovoitov items[unique++] = items[i]; 217*f8a8faceSAlexei Starovoitov 218*f8a8faceSAlexei Starovoitov return unique; 219*f8a8faceSAlexei Starovoitov } 220*f8a8faceSAlexei Starovoitov 221*f8a8faceSAlexei Starovoitov /* 222*f8a8faceSAlexei Starovoitov * sort_unique({map[start], ..., map[end]}) into off 223*f8a8faceSAlexei Starovoitov */ 224*f8a8faceSAlexei Starovoitov int bpf_copy_insn_array_uniq(struct bpf_map *map, u32 start, u32 end, u32 *off) 225*f8a8faceSAlexei Starovoitov { 226*f8a8faceSAlexei Starovoitov u32 n = end - start + 1; 227*f8a8faceSAlexei Starovoitov int err; 228*f8a8faceSAlexei Starovoitov 229*f8a8faceSAlexei Starovoitov err = copy_insn_array(map, start, end, off); 230*f8a8faceSAlexei Starovoitov if (err) 231*f8a8faceSAlexei Starovoitov return err; 232*f8a8faceSAlexei Starovoitov 233*f8a8faceSAlexei Starovoitov return sort_insn_array_uniq(off, n); 234*f8a8faceSAlexei Starovoitov } 235*f8a8faceSAlexei Starovoitov 236*f8a8faceSAlexei Starovoitov /* 237*f8a8faceSAlexei Starovoitov * Copy all unique offsets from the map 238*f8a8faceSAlexei Starovoitov */ 239*f8a8faceSAlexei Starovoitov static struct bpf_iarray *jt_from_map(struct bpf_map *map) 240*f8a8faceSAlexei Starovoitov { 241*f8a8faceSAlexei Starovoitov struct bpf_iarray *jt; 242*f8a8faceSAlexei Starovoitov int err; 243*f8a8faceSAlexei Starovoitov int n; 244*f8a8faceSAlexei Starovoitov 245*f8a8faceSAlexei Starovoitov jt = bpf_iarray_realloc(NULL, map->max_entries); 246*f8a8faceSAlexei Starovoitov if (!jt) 247*f8a8faceSAlexei Starovoitov return ERR_PTR(-ENOMEM); 248*f8a8faceSAlexei Starovoitov 249*f8a8faceSAlexei Starovoitov n = bpf_copy_insn_array_uniq(map, 0, map->max_entries - 1, jt->items); 250*f8a8faceSAlexei Starovoitov if (n < 0) { 251*f8a8faceSAlexei Starovoitov err = n; 252*f8a8faceSAlexei Starovoitov goto err_free; 253*f8a8faceSAlexei Starovoitov } 254*f8a8faceSAlexei Starovoitov if (n == 0) { 255*f8a8faceSAlexei Starovoitov err = -EINVAL; 256*f8a8faceSAlexei Starovoitov goto err_free; 257*f8a8faceSAlexei Starovoitov } 258*f8a8faceSAlexei Starovoitov jt->cnt = n; 259*f8a8faceSAlexei Starovoitov return jt; 260*f8a8faceSAlexei Starovoitov 261*f8a8faceSAlexei Starovoitov err_free: 262*f8a8faceSAlexei Starovoitov kvfree(jt); 263*f8a8faceSAlexei Starovoitov return ERR_PTR(err); 264*f8a8faceSAlexei Starovoitov } 265*f8a8faceSAlexei Starovoitov 266*f8a8faceSAlexei Starovoitov /* 267*f8a8faceSAlexei Starovoitov * Find and collect all maps which fit in the subprog. Return the result as one 268*f8a8faceSAlexei Starovoitov * combined jump table in jt->items (allocated with kvcalloc) 269*f8a8faceSAlexei Starovoitov */ 270*f8a8faceSAlexei Starovoitov static struct bpf_iarray *jt_from_subprog(struct bpf_verifier_env *env, 271*f8a8faceSAlexei Starovoitov int subprog_start, int subprog_end) 272*f8a8faceSAlexei Starovoitov { 273*f8a8faceSAlexei Starovoitov struct bpf_iarray *jt = NULL; 274*f8a8faceSAlexei Starovoitov struct bpf_map *map; 275*f8a8faceSAlexei Starovoitov struct bpf_iarray *jt_cur; 276*f8a8faceSAlexei Starovoitov int i; 277*f8a8faceSAlexei Starovoitov 278*f8a8faceSAlexei Starovoitov for (i = 0; i < env->insn_array_map_cnt; i++) { 279*f8a8faceSAlexei Starovoitov /* 280*f8a8faceSAlexei Starovoitov * TODO (when needed): collect only jump tables, not static keys 281*f8a8faceSAlexei Starovoitov * or maps for indirect calls 282*f8a8faceSAlexei Starovoitov */ 283*f8a8faceSAlexei Starovoitov map = env->insn_array_maps[i]; 284*f8a8faceSAlexei Starovoitov 285*f8a8faceSAlexei Starovoitov jt_cur = jt_from_map(map); 286*f8a8faceSAlexei Starovoitov if (IS_ERR(jt_cur)) { 287*f8a8faceSAlexei Starovoitov kvfree(jt); 288*f8a8faceSAlexei Starovoitov return jt_cur; 289*f8a8faceSAlexei Starovoitov } 290*f8a8faceSAlexei Starovoitov 291*f8a8faceSAlexei Starovoitov /* 292*f8a8faceSAlexei Starovoitov * This is enough to check one element. The full table is 293*f8a8faceSAlexei Starovoitov * checked to fit inside the subprog later in create_jt() 294*f8a8faceSAlexei Starovoitov */ 295*f8a8faceSAlexei Starovoitov if (jt_cur->items[0] >= subprog_start && jt_cur->items[0] < subprog_end) { 296*f8a8faceSAlexei Starovoitov u32 old_cnt = jt ? jt->cnt : 0; 297*f8a8faceSAlexei Starovoitov jt = bpf_iarray_realloc(jt, old_cnt + jt_cur->cnt); 298*f8a8faceSAlexei Starovoitov if (!jt) { 299*f8a8faceSAlexei Starovoitov kvfree(jt_cur); 300*f8a8faceSAlexei Starovoitov return ERR_PTR(-ENOMEM); 301*f8a8faceSAlexei Starovoitov } 302*f8a8faceSAlexei Starovoitov memcpy(jt->items + old_cnt, jt_cur->items, jt_cur->cnt << 2); 303*f8a8faceSAlexei Starovoitov } 304*f8a8faceSAlexei Starovoitov 305*f8a8faceSAlexei Starovoitov kvfree(jt_cur); 306*f8a8faceSAlexei Starovoitov } 307*f8a8faceSAlexei Starovoitov 308*f8a8faceSAlexei Starovoitov if (!jt) { 309*f8a8faceSAlexei Starovoitov verbose(env, "no jump tables found for subprog starting at %u\n", subprog_start); 310*f8a8faceSAlexei Starovoitov return ERR_PTR(-EINVAL); 311*f8a8faceSAlexei Starovoitov } 312*f8a8faceSAlexei Starovoitov 313*f8a8faceSAlexei Starovoitov jt->cnt = sort_insn_array_uniq(jt->items, jt->cnt); 314*f8a8faceSAlexei Starovoitov return jt; 315*f8a8faceSAlexei Starovoitov } 316*f8a8faceSAlexei Starovoitov 317*f8a8faceSAlexei Starovoitov static struct bpf_iarray * 318*f8a8faceSAlexei Starovoitov create_jt(int t, struct bpf_verifier_env *env) 319*f8a8faceSAlexei Starovoitov { 320*f8a8faceSAlexei Starovoitov struct bpf_subprog_info *subprog; 321*f8a8faceSAlexei Starovoitov int subprog_start, subprog_end; 322*f8a8faceSAlexei Starovoitov struct bpf_iarray *jt; 323*f8a8faceSAlexei Starovoitov int i; 324*f8a8faceSAlexei Starovoitov 325*f8a8faceSAlexei Starovoitov subprog = bpf_find_containing_subprog(env, t); 326*f8a8faceSAlexei Starovoitov subprog_start = subprog->start; 327*f8a8faceSAlexei Starovoitov subprog_end = (subprog + 1)->start; 328*f8a8faceSAlexei Starovoitov jt = jt_from_subprog(env, subprog_start, subprog_end); 329*f8a8faceSAlexei Starovoitov if (IS_ERR(jt)) 330*f8a8faceSAlexei Starovoitov return jt; 331*f8a8faceSAlexei Starovoitov 332*f8a8faceSAlexei Starovoitov /* Check that the every element of the jump table fits within the given subprogram */ 333*f8a8faceSAlexei Starovoitov for (i = 0; i < jt->cnt; i++) { 334*f8a8faceSAlexei Starovoitov if (jt->items[i] < subprog_start || jt->items[i] >= subprog_end) { 335*f8a8faceSAlexei Starovoitov verbose(env, "jump table for insn %d points outside of the subprog [%u,%u]\n", 336*f8a8faceSAlexei Starovoitov t, subprog_start, subprog_end); 337*f8a8faceSAlexei Starovoitov kvfree(jt); 338*f8a8faceSAlexei Starovoitov return ERR_PTR(-EINVAL); 339*f8a8faceSAlexei Starovoitov } 340*f8a8faceSAlexei Starovoitov } 341*f8a8faceSAlexei Starovoitov 342*f8a8faceSAlexei Starovoitov return jt; 343*f8a8faceSAlexei Starovoitov } 344*f8a8faceSAlexei Starovoitov 345*f8a8faceSAlexei Starovoitov /* "conditional jump with N edges" */ 346*f8a8faceSAlexei Starovoitov static int visit_gotox_insn(int t, struct bpf_verifier_env *env) 347*f8a8faceSAlexei Starovoitov { 348*f8a8faceSAlexei Starovoitov int *insn_stack = env->cfg.insn_stack; 349*f8a8faceSAlexei Starovoitov int *insn_state = env->cfg.insn_state; 350*f8a8faceSAlexei Starovoitov bool keep_exploring = false; 351*f8a8faceSAlexei Starovoitov struct bpf_iarray *jt; 352*f8a8faceSAlexei Starovoitov int i, w; 353*f8a8faceSAlexei Starovoitov 354*f8a8faceSAlexei Starovoitov jt = env->insn_aux_data[t].jt; 355*f8a8faceSAlexei Starovoitov if (!jt) { 356*f8a8faceSAlexei Starovoitov jt = create_jt(t, env); 357*f8a8faceSAlexei Starovoitov if (IS_ERR(jt)) 358*f8a8faceSAlexei Starovoitov return PTR_ERR(jt); 359*f8a8faceSAlexei Starovoitov 360*f8a8faceSAlexei Starovoitov env->insn_aux_data[t].jt = jt; 361*f8a8faceSAlexei Starovoitov } 362*f8a8faceSAlexei Starovoitov 363*f8a8faceSAlexei Starovoitov mark_prune_point(env, t); 364*f8a8faceSAlexei Starovoitov for (i = 0; i < jt->cnt; i++) { 365*f8a8faceSAlexei Starovoitov w = jt->items[i]; 366*f8a8faceSAlexei Starovoitov if (w < 0 || w >= env->prog->len) { 367*f8a8faceSAlexei Starovoitov verbose(env, "indirect jump out of range from insn %d to %d\n", t, w); 368*f8a8faceSAlexei Starovoitov return -EINVAL; 369*f8a8faceSAlexei Starovoitov } 370*f8a8faceSAlexei Starovoitov 371*f8a8faceSAlexei Starovoitov mark_jmp_point(env, w); 372*f8a8faceSAlexei Starovoitov 373*f8a8faceSAlexei Starovoitov /* EXPLORED || DISCOVERED */ 374*f8a8faceSAlexei Starovoitov if (insn_state[w]) 375*f8a8faceSAlexei Starovoitov continue; 376*f8a8faceSAlexei Starovoitov 377*f8a8faceSAlexei Starovoitov if (env->cfg.cur_stack >= env->prog->len) 378*f8a8faceSAlexei Starovoitov return -E2BIG; 379*f8a8faceSAlexei Starovoitov 380*f8a8faceSAlexei Starovoitov insn_stack[env->cfg.cur_stack++] = w; 381*f8a8faceSAlexei Starovoitov insn_state[w] |= DISCOVERED; 382*f8a8faceSAlexei Starovoitov keep_exploring = true; 383*f8a8faceSAlexei Starovoitov } 384*f8a8faceSAlexei Starovoitov 385*f8a8faceSAlexei Starovoitov return keep_exploring ? KEEP_EXPLORING : DONE_EXPLORING; 386*f8a8faceSAlexei Starovoitov } 387*f8a8faceSAlexei Starovoitov 388*f8a8faceSAlexei Starovoitov /* 389*f8a8faceSAlexei Starovoitov * Instructions that can abnormally return from a subprog (tail_call 390*f8a8faceSAlexei Starovoitov * upon success, ld_{abs,ind} upon load failure) have a hidden exit 391*f8a8faceSAlexei Starovoitov * that the verifier must account for. 392*f8a8faceSAlexei Starovoitov */ 393*f8a8faceSAlexei Starovoitov static int visit_abnormal_return_insn(struct bpf_verifier_env *env, int t) 394*f8a8faceSAlexei Starovoitov { 395*f8a8faceSAlexei Starovoitov struct bpf_subprog_info *subprog; 396*f8a8faceSAlexei Starovoitov struct bpf_iarray *jt; 397*f8a8faceSAlexei Starovoitov 398*f8a8faceSAlexei Starovoitov if (env->insn_aux_data[t].jt) 399*f8a8faceSAlexei Starovoitov return 0; 400*f8a8faceSAlexei Starovoitov 401*f8a8faceSAlexei Starovoitov jt = bpf_iarray_realloc(NULL, 2); 402*f8a8faceSAlexei Starovoitov if (!jt) 403*f8a8faceSAlexei Starovoitov return -ENOMEM; 404*f8a8faceSAlexei Starovoitov 405*f8a8faceSAlexei Starovoitov subprog = bpf_find_containing_subprog(env, t); 406*f8a8faceSAlexei Starovoitov jt->items[0] = t + 1; 407*f8a8faceSAlexei Starovoitov jt->items[1] = subprog->exit_idx; 408*f8a8faceSAlexei Starovoitov env->insn_aux_data[t].jt = jt; 409*f8a8faceSAlexei Starovoitov return 0; 410*f8a8faceSAlexei Starovoitov } 411*f8a8faceSAlexei Starovoitov 412*f8a8faceSAlexei Starovoitov /* Visits the instruction at index t and returns one of the following: 413*f8a8faceSAlexei Starovoitov * < 0 - an error occurred 414*f8a8faceSAlexei Starovoitov * DONE_EXPLORING - the instruction was fully explored 415*f8a8faceSAlexei Starovoitov * KEEP_EXPLORING - there is still work to be done before it is fully explored 416*f8a8faceSAlexei Starovoitov */ 417*f8a8faceSAlexei Starovoitov static int visit_insn(int t, struct bpf_verifier_env *env) 418*f8a8faceSAlexei Starovoitov { 419*f8a8faceSAlexei Starovoitov struct bpf_insn *insns = env->prog->insnsi, *insn = &insns[t]; 420*f8a8faceSAlexei Starovoitov int ret, off, insn_sz; 421*f8a8faceSAlexei Starovoitov 422*f8a8faceSAlexei Starovoitov if (bpf_pseudo_func(insn)) 423*f8a8faceSAlexei Starovoitov return visit_func_call_insn(t, insns, env, true); 424*f8a8faceSAlexei Starovoitov 425*f8a8faceSAlexei Starovoitov /* All non-branch instructions have a single fall-through edge. */ 426*f8a8faceSAlexei Starovoitov if (BPF_CLASS(insn->code) != BPF_JMP && 427*f8a8faceSAlexei Starovoitov BPF_CLASS(insn->code) != BPF_JMP32) { 428*f8a8faceSAlexei Starovoitov if (BPF_CLASS(insn->code) == BPF_LD && 429*f8a8faceSAlexei Starovoitov (BPF_MODE(insn->code) == BPF_ABS || 430*f8a8faceSAlexei Starovoitov BPF_MODE(insn->code) == BPF_IND)) { 431*f8a8faceSAlexei Starovoitov ret = visit_abnormal_return_insn(env, t); 432*f8a8faceSAlexei Starovoitov if (ret) 433*f8a8faceSAlexei Starovoitov return ret; 434*f8a8faceSAlexei Starovoitov } 435*f8a8faceSAlexei Starovoitov insn_sz = bpf_is_ldimm64(insn) ? 2 : 1; 436*f8a8faceSAlexei Starovoitov return push_insn(t, t + insn_sz, FALLTHROUGH, env); 437*f8a8faceSAlexei Starovoitov } 438*f8a8faceSAlexei Starovoitov 439*f8a8faceSAlexei Starovoitov switch (BPF_OP(insn->code)) { 440*f8a8faceSAlexei Starovoitov case BPF_EXIT: 441*f8a8faceSAlexei Starovoitov return DONE_EXPLORING; 442*f8a8faceSAlexei Starovoitov 443*f8a8faceSAlexei Starovoitov case BPF_CALL: 444*f8a8faceSAlexei Starovoitov if (bpf_is_async_callback_calling_insn(insn)) 445*f8a8faceSAlexei Starovoitov /* Mark this call insn as a prune point to trigger 446*f8a8faceSAlexei Starovoitov * is_state_visited() check before call itself is 447*f8a8faceSAlexei Starovoitov * processed by __check_func_call(). Otherwise new 448*f8a8faceSAlexei Starovoitov * async state will be pushed for further exploration. 449*f8a8faceSAlexei Starovoitov */ 450*f8a8faceSAlexei Starovoitov mark_prune_point(env, t); 451*f8a8faceSAlexei Starovoitov /* For functions that invoke callbacks it is not known how many times 452*f8a8faceSAlexei Starovoitov * callback would be called. Verifier models callback calling functions 453*f8a8faceSAlexei Starovoitov * by repeatedly visiting callback bodies and returning to origin call 454*f8a8faceSAlexei Starovoitov * instruction. 455*f8a8faceSAlexei Starovoitov * In order to stop such iteration verifier needs to identify when a 456*f8a8faceSAlexei Starovoitov * state identical some state from a previous iteration is reached. 457*f8a8faceSAlexei Starovoitov * Check below forces creation of checkpoint before callback calling 458*f8a8faceSAlexei Starovoitov * instruction to allow search for such identical states. 459*f8a8faceSAlexei Starovoitov */ 460*f8a8faceSAlexei Starovoitov if (bpf_is_sync_callback_calling_insn(insn)) { 461*f8a8faceSAlexei Starovoitov mark_calls_callback(env, t); 462*f8a8faceSAlexei Starovoitov mark_force_checkpoint(env, t); 463*f8a8faceSAlexei Starovoitov mark_prune_point(env, t); 464*f8a8faceSAlexei Starovoitov mark_jmp_point(env, t); 465*f8a8faceSAlexei Starovoitov } 466*f8a8faceSAlexei Starovoitov if (bpf_helper_call(insn)) { 467*f8a8faceSAlexei Starovoitov const struct bpf_func_proto *fp; 468*f8a8faceSAlexei Starovoitov 469*f8a8faceSAlexei Starovoitov ret = bpf_get_helper_proto(env, insn->imm, &fp); 470*f8a8faceSAlexei Starovoitov /* If called in a non-sleepable context program will be 471*f8a8faceSAlexei Starovoitov * rejected anyway, so we should end up with precise 472*f8a8faceSAlexei Starovoitov * sleepable marks on subprogs, except for dead code 473*f8a8faceSAlexei Starovoitov * elimination. 474*f8a8faceSAlexei Starovoitov */ 475*f8a8faceSAlexei Starovoitov if (ret == 0 && fp->might_sleep) 476*f8a8faceSAlexei Starovoitov mark_subprog_might_sleep(env, t); 477*f8a8faceSAlexei Starovoitov if (bpf_helper_changes_pkt_data(insn->imm)) 478*f8a8faceSAlexei Starovoitov mark_subprog_changes_pkt_data(env, t); 479*f8a8faceSAlexei Starovoitov if (insn->imm == BPF_FUNC_tail_call) { 480*f8a8faceSAlexei Starovoitov ret = visit_abnormal_return_insn(env, t); 481*f8a8faceSAlexei Starovoitov if (ret) 482*f8a8faceSAlexei Starovoitov return ret; 483*f8a8faceSAlexei Starovoitov } 484*f8a8faceSAlexei Starovoitov } else if (insn->src_reg == BPF_PSEUDO_KFUNC_CALL) { 485*f8a8faceSAlexei Starovoitov struct bpf_kfunc_call_arg_meta meta; 486*f8a8faceSAlexei Starovoitov 487*f8a8faceSAlexei Starovoitov ret = bpf_fetch_kfunc_arg_meta(env, insn->imm, insn->off, &meta); 488*f8a8faceSAlexei Starovoitov if (ret == 0 && bpf_is_iter_next_kfunc(&meta)) { 489*f8a8faceSAlexei Starovoitov mark_prune_point(env, t); 490*f8a8faceSAlexei Starovoitov /* Checking and saving state checkpoints at iter_next() call 491*f8a8faceSAlexei Starovoitov * is crucial for fast convergence of open-coded iterator loop 492*f8a8faceSAlexei Starovoitov * logic, so we need to force it. If we don't do that, 493*f8a8faceSAlexei Starovoitov * is_state_visited() might skip saving a checkpoint, causing 494*f8a8faceSAlexei Starovoitov * unnecessarily long sequence of not checkpointed 495*f8a8faceSAlexei Starovoitov * instructions and jumps, leading to exhaustion of jump 496*f8a8faceSAlexei Starovoitov * history buffer, and potentially other undesired outcomes. 497*f8a8faceSAlexei Starovoitov * It is expected that with correct open-coded iterators 498*f8a8faceSAlexei Starovoitov * convergence will happen quickly, so we don't run a risk of 499*f8a8faceSAlexei Starovoitov * exhausting memory. 500*f8a8faceSAlexei Starovoitov */ 501*f8a8faceSAlexei Starovoitov mark_force_checkpoint(env, t); 502*f8a8faceSAlexei Starovoitov } 503*f8a8faceSAlexei Starovoitov /* Same as helpers, if called in a non-sleepable context 504*f8a8faceSAlexei Starovoitov * program will be rejected anyway, so we should end up 505*f8a8faceSAlexei Starovoitov * with precise sleepable marks on subprogs, except for 506*f8a8faceSAlexei Starovoitov * dead code elimination. 507*f8a8faceSAlexei Starovoitov */ 508*f8a8faceSAlexei Starovoitov if (ret == 0 && bpf_is_kfunc_sleepable(&meta)) 509*f8a8faceSAlexei Starovoitov mark_subprog_might_sleep(env, t); 510*f8a8faceSAlexei Starovoitov if (ret == 0 && bpf_is_kfunc_pkt_changing(&meta)) 511*f8a8faceSAlexei Starovoitov mark_subprog_changes_pkt_data(env, t); 512*f8a8faceSAlexei Starovoitov } 513*f8a8faceSAlexei Starovoitov return visit_func_call_insn(t, insns, env, insn->src_reg == BPF_PSEUDO_CALL); 514*f8a8faceSAlexei Starovoitov 515*f8a8faceSAlexei Starovoitov case BPF_JA: 516*f8a8faceSAlexei Starovoitov if (BPF_SRC(insn->code) == BPF_X) 517*f8a8faceSAlexei Starovoitov return visit_gotox_insn(t, env); 518*f8a8faceSAlexei Starovoitov 519*f8a8faceSAlexei Starovoitov if (BPF_CLASS(insn->code) == BPF_JMP) 520*f8a8faceSAlexei Starovoitov off = insn->off; 521*f8a8faceSAlexei Starovoitov else 522*f8a8faceSAlexei Starovoitov off = insn->imm; 523*f8a8faceSAlexei Starovoitov 524*f8a8faceSAlexei Starovoitov /* unconditional jump with single edge */ 525*f8a8faceSAlexei Starovoitov ret = push_insn(t, t + off + 1, FALLTHROUGH, env); 526*f8a8faceSAlexei Starovoitov if (ret) 527*f8a8faceSAlexei Starovoitov return ret; 528*f8a8faceSAlexei Starovoitov 529*f8a8faceSAlexei Starovoitov mark_prune_point(env, t + off + 1); 530*f8a8faceSAlexei Starovoitov mark_jmp_point(env, t + off + 1); 531*f8a8faceSAlexei Starovoitov 532*f8a8faceSAlexei Starovoitov return ret; 533*f8a8faceSAlexei Starovoitov 534*f8a8faceSAlexei Starovoitov default: 535*f8a8faceSAlexei Starovoitov /* conditional jump with two edges */ 536*f8a8faceSAlexei Starovoitov mark_prune_point(env, t); 537*f8a8faceSAlexei Starovoitov if (bpf_is_may_goto_insn(insn)) 538*f8a8faceSAlexei Starovoitov mark_force_checkpoint(env, t); 539*f8a8faceSAlexei Starovoitov 540*f8a8faceSAlexei Starovoitov ret = push_insn(t, t + 1, FALLTHROUGH, env); 541*f8a8faceSAlexei Starovoitov if (ret) 542*f8a8faceSAlexei Starovoitov return ret; 543*f8a8faceSAlexei Starovoitov 544*f8a8faceSAlexei Starovoitov return push_insn(t, t + insn->off + 1, BRANCH, env); 545*f8a8faceSAlexei Starovoitov } 546*f8a8faceSAlexei Starovoitov } 547*f8a8faceSAlexei Starovoitov 548*f8a8faceSAlexei Starovoitov /* non-recursive depth-first-search to detect loops in BPF program 549*f8a8faceSAlexei Starovoitov * loop == back-edge in directed graph 550*f8a8faceSAlexei Starovoitov */ 551*f8a8faceSAlexei Starovoitov int bpf_check_cfg(struct bpf_verifier_env *env) 552*f8a8faceSAlexei Starovoitov { 553*f8a8faceSAlexei Starovoitov int insn_cnt = env->prog->len; 554*f8a8faceSAlexei Starovoitov int *insn_stack, *insn_state; 555*f8a8faceSAlexei Starovoitov int ex_insn_beg, i, ret = 0; 556*f8a8faceSAlexei Starovoitov 557*f8a8faceSAlexei Starovoitov insn_state = env->cfg.insn_state = kvzalloc_objs(int, insn_cnt, 558*f8a8faceSAlexei Starovoitov GFP_KERNEL_ACCOUNT); 559*f8a8faceSAlexei Starovoitov if (!insn_state) 560*f8a8faceSAlexei Starovoitov return -ENOMEM; 561*f8a8faceSAlexei Starovoitov 562*f8a8faceSAlexei Starovoitov insn_stack = env->cfg.insn_stack = kvzalloc_objs(int, insn_cnt, 563*f8a8faceSAlexei Starovoitov GFP_KERNEL_ACCOUNT); 564*f8a8faceSAlexei Starovoitov if (!insn_stack) { 565*f8a8faceSAlexei Starovoitov kvfree(insn_state); 566*f8a8faceSAlexei Starovoitov return -ENOMEM; 567*f8a8faceSAlexei Starovoitov } 568*f8a8faceSAlexei Starovoitov 569*f8a8faceSAlexei Starovoitov ex_insn_beg = env->exception_callback_subprog 570*f8a8faceSAlexei Starovoitov ? env->subprog_info[env->exception_callback_subprog].start 571*f8a8faceSAlexei Starovoitov : 0; 572*f8a8faceSAlexei Starovoitov 573*f8a8faceSAlexei Starovoitov insn_state[0] = DISCOVERED; /* mark 1st insn as discovered */ 574*f8a8faceSAlexei Starovoitov insn_stack[0] = 0; /* 0 is the first instruction */ 575*f8a8faceSAlexei Starovoitov env->cfg.cur_stack = 1; 576*f8a8faceSAlexei Starovoitov 577*f8a8faceSAlexei Starovoitov walk_cfg: 578*f8a8faceSAlexei Starovoitov while (env->cfg.cur_stack > 0) { 579*f8a8faceSAlexei Starovoitov int t = insn_stack[env->cfg.cur_stack - 1]; 580*f8a8faceSAlexei Starovoitov 581*f8a8faceSAlexei Starovoitov ret = visit_insn(t, env); 582*f8a8faceSAlexei Starovoitov switch (ret) { 583*f8a8faceSAlexei Starovoitov case DONE_EXPLORING: 584*f8a8faceSAlexei Starovoitov insn_state[t] = EXPLORED; 585*f8a8faceSAlexei Starovoitov env->cfg.cur_stack--; 586*f8a8faceSAlexei Starovoitov break; 587*f8a8faceSAlexei Starovoitov case KEEP_EXPLORING: 588*f8a8faceSAlexei Starovoitov break; 589*f8a8faceSAlexei Starovoitov default: 590*f8a8faceSAlexei Starovoitov if (ret > 0) { 591*f8a8faceSAlexei Starovoitov verifier_bug(env, "visit_insn internal bug"); 592*f8a8faceSAlexei Starovoitov ret = -EFAULT; 593*f8a8faceSAlexei Starovoitov } 594*f8a8faceSAlexei Starovoitov goto err_free; 595*f8a8faceSAlexei Starovoitov } 596*f8a8faceSAlexei Starovoitov } 597*f8a8faceSAlexei Starovoitov 598*f8a8faceSAlexei Starovoitov if (env->cfg.cur_stack < 0) { 599*f8a8faceSAlexei Starovoitov verifier_bug(env, "pop stack internal bug"); 600*f8a8faceSAlexei Starovoitov ret = -EFAULT; 601*f8a8faceSAlexei Starovoitov goto err_free; 602*f8a8faceSAlexei Starovoitov } 603*f8a8faceSAlexei Starovoitov 604*f8a8faceSAlexei Starovoitov if (ex_insn_beg && insn_state[ex_insn_beg] != EXPLORED) { 605*f8a8faceSAlexei Starovoitov insn_state[ex_insn_beg] = DISCOVERED; 606*f8a8faceSAlexei Starovoitov insn_stack[0] = ex_insn_beg; 607*f8a8faceSAlexei Starovoitov env->cfg.cur_stack = 1; 608*f8a8faceSAlexei Starovoitov goto walk_cfg; 609*f8a8faceSAlexei Starovoitov } 610*f8a8faceSAlexei Starovoitov 611*f8a8faceSAlexei Starovoitov for (i = 0; i < insn_cnt; i++) { 612*f8a8faceSAlexei Starovoitov struct bpf_insn *insn = &env->prog->insnsi[i]; 613*f8a8faceSAlexei Starovoitov 614*f8a8faceSAlexei Starovoitov if (insn_state[i] != EXPLORED) { 615*f8a8faceSAlexei Starovoitov verbose(env, "unreachable insn %d\n", i); 616*f8a8faceSAlexei Starovoitov ret = -EINVAL; 617*f8a8faceSAlexei Starovoitov goto err_free; 618*f8a8faceSAlexei Starovoitov } 619*f8a8faceSAlexei Starovoitov if (bpf_is_ldimm64(insn)) { 620*f8a8faceSAlexei Starovoitov if (insn_state[i + 1] != 0) { 621*f8a8faceSAlexei Starovoitov verbose(env, "jump into the middle of ldimm64 insn %d\n", i); 622*f8a8faceSAlexei Starovoitov ret = -EINVAL; 623*f8a8faceSAlexei Starovoitov goto err_free; 624*f8a8faceSAlexei Starovoitov } 625*f8a8faceSAlexei Starovoitov i++; /* skip second half of ldimm64 */ 626*f8a8faceSAlexei Starovoitov } 627*f8a8faceSAlexei Starovoitov } 628*f8a8faceSAlexei Starovoitov ret = 0; /* cfg looks good */ 629*f8a8faceSAlexei Starovoitov env->prog->aux->changes_pkt_data = env->subprog_info[0].changes_pkt_data; 630*f8a8faceSAlexei Starovoitov env->prog->aux->might_sleep = env->subprog_info[0].might_sleep; 631*f8a8faceSAlexei Starovoitov 632*f8a8faceSAlexei Starovoitov err_free: 633*f8a8faceSAlexei Starovoitov kvfree(insn_state); 634*f8a8faceSAlexei Starovoitov kvfree(insn_stack); 635*f8a8faceSAlexei Starovoitov env->cfg.insn_state = env->cfg.insn_stack = NULL; 636*f8a8faceSAlexei Starovoitov return ret; 637*f8a8faceSAlexei Starovoitov } 638*f8a8faceSAlexei Starovoitov 639*f8a8faceSAlexei Starovoitov /* 640*f8a8faceSAlexei Starovoitov * For each subprogram 'i' fill array env->cfg.insn_subprogram sub-range 641*f8a8faceSAlexei Starovoitov * [env->subprog_info[i].postorder_start, env->subprog_info[i+1].postorder_start) 642*f8a8faceSAlexei Starovoitov * with indices of 'i' instructions in postorder. 643*f8a8faceSAlexei Starovoitov */ 644*f8a8faceSAlexei Starovoitov int bpf_compute_postorder(struct bpf_verifier_env *env) 645*f8a8faceSAlexei Starovoitov { 646*f8a8faceSAlexei Starovoitov u32 cur_postorder, i, top, stack_sz, s; 647*f8a8faceSAlexei Starovoitov int *stack = NULL, *postorder = NULL, *state = NULL; 648*f8a8faceSAlexei Starovoitov struct bpf_iarray *succ; 649*f8a8faceSAlexei Starovoitov 650*f8a8faceSAlexei Starovoitov postorder = kvzalloc_objs(int, env->prog->len, GFP_KERNEL_ACCOUNT); 651*f8a8faceSAlexei Starovoitov state = kvzalloc_objs(int, env->prog->len, GFP_KERNEL_ACCOUNT); 652*f8a8faceSAlexei Starovoitov stack = kvzalloc_objs(int, env->prog->len, GFP_KERNEL_ACCOUNT); 653*f8a8faceSAlexei Starovoitov if (!postorder || !state || !stack) { 654*f8a8faceSAlexei Starovoitov kvfree(postorder); 655*f8a8faceSAlexei Starovoitov kvfree(state); 656*f8a8faceSAlexei Starovoitov kvfree(stack); 657*f8a8faceSAlexei Starovoitov return -ENOMEM; 658*f8a8faceSAlexei Starovoitov } 659*f8a8faceSAlexei Starovoitov cur_postorder = 0; 660*f8a8faceSAlexei Starovoitov for (i = 0; i < env->subprog_cnt; i++) { 661*f8a8faceSAlexei Starovoitov env->subprog_info[i].postorder_start = cur_postorder; 662*f8a8faceSAlexei Starovoitov stack[0] = env->subprog_info[i].start; 663*f8a8faceSAlexei Starovoitov stack_sz = 1; 664*f8a8faceSAlexei Starovoitov do { 665*f8a8faceSAlexei Starovoitov top = stack[stack_sz - 1]; 666*f8a8faceSAlexei Starovoitov state[top] |= DISCOVERED; 667*f8a8faceSAlexei Starovoitov if (state[top] & EXPLORED) { 668*f8a8faceSAlexei Starovoitov postorder[cur_postorder++] = top; 669*f8a8faceSAlexei Starovoitov stack_sz--; 670*f8a8faceSAlexei Starovoitov continue; 671*f8a8faceSAlexei Starovoitov } 672*f8a8faceSAlexei Starovoitov succ = bpf_insn_successors(env, top); 673*f8a8faceSAlexei Starovoitov for (s = 0; s < succ->cnt; ++s) { 674*f8a8faceSAlexei Starovoitov if (!state[succ->items[s]]) { 675*f8a8faceSAlexei Starovoitov stack[stack_sz++] = succ->items[s]; 676*f8a8faceSAlexei Starovoitov state[succ->items[s]] |= DISCOVERED; 677*f8a8faceSAlexei Starovoitov } 678*f8a8faceSAlexei Starovoitov } 679*f8a8faceSAlexei Starovoitov state[top] |= EXPLORED; 680*f8a8faceSAlexei Starovoitov } while (stack_sz); 681*f8a8faceSAlexei Starovoitov } 682*f8a8faceSAlexei Starovoitov env->subprog_info[i].postorder_start = cur_postorder; 683*f8a8faceSAlexei Starovoitov env->cfg.insn_postorder = postorder; 684*f8a8faceSAlexei Starovoitov env->cfg.cur_postorder = cur_postorder; 685*f8a8faceSAlexei Starovoitov kvfree(stack); 686*f8a8faceSAlexei Starovoitov kvfree(state); 687*f8a8faceSAlexei Starovoitov return 0; 688*f8a8faceSAlexei Starovoitov } 689*f8a8faceSAlexei Starovoitov 690*f8a8faceSAlexei Starovoitov /* 691*f8a8faceSAlexei Starovoitov * Compute strongly connected components (SCCs) on the CFG. 692*f8a8faceSAlexei Starovoitov * Assign an SCC number to each instruction, recorded in env->insn_aux[*].scc. 693*f8a8faceSAlexei Starovoitov * If instruction is a sole member of its SCC and there are no self edges, 694*f8a8faceSAlexei Starovoitov * assign it SCC number of zero. 695*f8a8faceSAlexei Starovoitov * Uses a non-recursive adaptation of Tarjan's algorithm for SCC computation. 696*f8a8faceSAlexei Starovoitov */ 697*f8a8faceSAlexei Starovoitov int bpf_compute_scc(struct bpf_verifier_env *env) 698*f8a8faceSAlexei Starovoitov { 699*f8a8faceSAlexei Starovoitov const u32 NOT_ON_STACK = U32_MAX; 700*f8a8faceSAlexei Starovoitov 701*f8a8faceSAlexei Starovoitov struct bpf_insn_aux_data *aux = env->insn_aux_data; 702*f8a8faceSAlexei Starovoitov const u32 insn_cnt = env->prog->len; 703*f8a8faceSAlexei Starovoitov int stack_sz, dfs_sz, err = 0; 704*f8a8faceSAlexei Starovoitov u32 *stack, *pre, *low, *dfs; 705*f8a8faceSAlexei Starovoitov u32 i, j, t, w; 706*f8a8faceSAlexei Starovoitov u32 next_preorder_num; 707*f8a8faceSAlexei Starovoitov u32 next_scc_id; 708*f8a8faceSAlexei Starovoitov bool assign_scc; 709*f8a8faceSAlexei Starovoitov struct bpf_iarray *succ; 710*f8a8faceSAlexei Starovoitov 711*f8a8faceSAlexei Starovoitov next_preorder_num = 1; 712*f8a8faceSAlexei Starovoitov next_scc_id = 1; 713*f8a8faceSAlexei Starovoitov /* 714*f8a8faceSAlexei Starovoitov * - 'stack' accumulates vertices in DFS order, see invariant comment below; 715*f8a8faceSAlexei Starovoitov * - 'pre[t] == p' => preorder number of vertex 't' is 'p'; 716*f8a8faceSAlexei Starovoitov * - 'low[t] == n' => smallest preorder number of the vertex reachable from 't' is 'n'; 717*f8a8faceSAlexei Starovoitov * - 'dfs' DFS traversal stack, used to emulate explicit recursion. 718*f8a8faceSAlexei Starovoitov */ 719*f8a8faceSAlexei Starovoitov stack = kvcalloc(insn_cnt, sizeof(int), GFP_KERNEL_ACCOUNT); 720*f8a8faceSAlexei Starovoitov pre = kvcalloc(insn_cnt, sizeof(int), GFP_KERNEL_ACCOUNT); 721*f8a8faceSAlexei Starovoitov low = kvcalloc(insn_cnt, sizeof(int), GFP_KERNEL_ACCOUNT); 722*f8a8faceSAlexei Starovoitov dfs = kvcalloc(insn_cnt, sizeof(*dfs), GFP_KERNEL_ACCOUNT); 723*f8a8faceSAlexei Starovoitov if (!stack || !pre || !low || !dfs) { 724*f8a8faceSAlexei Starovoitov err = -ENOMEM; 725*f8a8faceSAlexei Starovoitov goto exit; 726*f8a8faceSAlexei Starovoitov } 727*f8a8faceSAlexei Starovoitov /* 728*f8a8faceSAlexei Starovoitov * References: 729*f8a8faceSAlexei Starovoitov * [1] R. Tarjan "Depth-First Search and Linear Graph Algorithms" 730*f8a8faceSAlexei Starovoitov * [2] D. J. Pearce "A Space-Efficient Algorithm for Finding Strongly Connected Components" 731*f8a8faceSAlexei Starovoitov * 732*f8a8faceSAlexei Starovoitov * The algorithm maintains the following invariant: 733*f8a8faceSAlexei Starovoitov * - suppose there is a path 'u' ~> 'v', such that 'pre[v] < pre[u]'; 734*f8a8faceSAlexei Starovoitov * - then, vertex 'u' remains on stack while vertex 'v' is on stack. 735*f8a8faceSAlexei Starovoitov * 736*f8a8faceSAlexei Starovoitov * Consequently: 737*f8a8faceSAlexei Starovoitov * - If 'low[v] < pre[v]', there is a path from 'v' to some vertex 'u', 738*f8a8faceSAlexei Starovoitov * such that 'pre[u] == low[v]'; vertex 'u' is currently on the stack, 739*f8a8faceSAlexei Starovoitov * and thus there is an SCC (loop) containing both 'u' and 'v'. 740*f8a8faceSAlexei Starovoitov * - If 'low[v] == pre[v]', loops containing 'v' have been explored, 741*f8a8faceSAlexei Starovoitov * and 'v' can be considered the root of some SCC. 742*f8a8faceSAlexei Starovoitov * 743*f8a8faceSAlexei Starovoitov * Here is a pseudo-code for an explicitly recursive version of the algorithm: 744*f8a8faceSAlexei Starovoitov * 745*f8a8faceSAlexei Starovoitov * NOT_ON_STACK = insn_cnt + 1 746*f8a8faceSAlexei Starovoitov * pre = [0] * insn_cnt 747*f8a8faceSAlexei Starovoitov * low = [0] * insn_cnt 748*f8a8faceSAlexei Starovoitov * scc = [0] * insn_cnt 749*f8a8faceSAlexei Starovoitov * stack = [] 750*f8a8faceSAlexei Starovoitov * 751*f8a8faceSAlexei Starovoitov * next_preorder_num = 1 752*f8a8faceSAlexei Starovoitov * next_scc_id = 1 753*f8a8faceSAlexei Starovoitov * 754*f8a8faceSAlexei Starovoitov * def recur(w): 755*f8a8faceSAlexei Starovoitov * nonlocal next_preorder_num 756*f8a8faceSAlexei Starovoitov * nonlocal next_scc_id 757*f8a8faceSAlexei Starovoitov * 758*f8a8faceSAlexei Starovoitov * pre[w] = next_preorder_num 759*f8a8faceSAlexei Starovoitov * low[w] = next_preorder_num 760*f8a8faceSAlexei Starovoitov * next_preorder_num += 1 761*f8a8faceSAlexei Starovoitov * stack.append(w) 762*f8a8faceSAlexei Starovoitov * for s in successors(w): 763*f8a8faceSAlexei Starovoitov * # Note: for classic algorithm the block below should look as: 764*f8a8faceSAlexei Starovoitov * # 765*f8a8faceSAlexei Starovoitov * # if pre[s] == 0: 766*f8a8faceSAlexei Starovoitov * # recur(s) 767*f8a8faceSAlexei Starovoitov * # low[w] = min(low[w], low[s]) 768*f8a8faceSAlexei Starovoitov * # elif low[s] != NOT_ON_STACK: 769*f8a8faceSAlexei Starovoitov * # low[w] = min(low[w], pre[s]) 770*f8a8faceSAlexei Starovoitov * # 771*f8a8faceSAlexei Starovoitov * # But replacing both 'min' instructions with 'low[w] = min(low[w], low[s])' 772*f8a8faceSAlexei Starovoitov * # does not break the invariant and makes iterative version of the algorithm 773*f8a8faceSAlexei Starovoitov * # simpler. See 'Algorithm #3' from [2]. 774*f8a8faceSAlexei Starovoitov * 775*f8a8faceSAlexei Starovoitov * # 's' not yet visited 776*f8a8faceSAlexei Starovoitov * if pre[s] == 0: 777*f8a8faceSAlexei Starovoitov * recur(s) 778*f8a8faceSAlexei Starovoitov * # if 's' is on stack, pick lowest reachable preorder number from it; 779*f8a8faceSAlexei Starovoitov * # if 's' is not on stack 'low[s] == NOT_ON_STACK > low[w]', 780*f8a8faceSAlexei Starovoitov * # so 'min' would be a noop. 781*f8a8faceSAlexei Starovoitov * low[w] = min(low[w], low[s]) 782*f8a8faceSAlexei Starovoitov * 783*f8a8faceSAlexei Starovoitov * if low[w] == pre[w]: 784*f8a8faceSAlexei Starovoitov * # 'w' is the root of an SCC, pop all vertices 785*f8a8faceSAlexei Starovoitov * # below 'w' on stack and assign same SCC to them. 786*f8a8faceSAlexei Starovoitov * while True: 787*f8a8faceSAlexei Starovoitov * t = stack.pop() 788*f8a8faceSAlexei Starovoitov * low[t] = NOT_ON_STACK 789*f8a8faceSAlexei Starovoitov * scc[t] = next_scc_id 790*f8a8faceSAlexei Starovoitov * if t == w: 791*f8a8faceSAlexei Starovoitov * break 792*f8a8faceSAlexei Starovoitov * next_scc_id += 1 793*f8a8faceSAlexei Starovoitov * 794*f8a8faceSAlexei Starovoitov * for i in range(0, insn_cnt): 795*f8a8faceSAlexei Starovoitov * if pre[i] == 0: 796*f8a8faceSAlexei Starovoitov * recur(i) 797*f8a8faceSAlexei Starovoitov * 798*f8a8faceSAlexei Starovoitov * Below implementation replaces explicit recursion with array 'dfs'. 799*f8a8faceSAlexei Starovoitov */ 800*f8a8faceSAlexei Starovoitov for (i = 0; i < insn_cnt; i++) { 801*f8a8faceSAlexei Starovoitov if (pre[i]) 802*f8a8faceSAlexei Starovoitov continue; 803*f8a8faceSAlexei Starovoitov stack_sz = 0; 804*f8a8faceSAlexei Starovoitov dfs_sz = 1; 805*f8a8faceSAlexei Starovoitov dfs[0] = i; 806*f8a8faceSAlexei Starovoitov dfs_continue: 807*f8a8faceSAlexei Starovoitov while (dfs_sz) { 808*f8a8faceSAlexei Starovoitov w = dfs[dfs_sz - 1]; 809*f8a8faceSAlexei Starovoitov if (pre[w] == 0) { 810*f8a8faceSAlexei Starovoitov low[w] = next_preorder_num; 811*f8a8faceSAlexei Starovoitov pre[w] = next_preorder_num; 812*f8a8faceSAlexei Starovoitov next_preorder_num++; 813*f8a8faceSAlexei Starovoitov stack[stack_sz++] = w; 814*f8a8faceSAlexei Starovoitov } 815*f8a8faceSAlexei Starovoitov /* Visit 'w' successors */ 816*f8a8faceSAlexei Starovoitov succ = bpf_insn_successors(env, w); 817*f8a8faceSAlexei Starovoitov for (j = 0; j < succ->cnt; ++j) { 818*f8a8faceSAlexei Starovoitov if (pre[succ->items[j]]) { 819*f8a8faceSAlexei Starovoitov low[w] = min(low[w], low[succ->items[j]]); 820*f8a8faceSAlexei Starovoitov } else { 821*f8a8faceSAlexei Starovoitov dfs[dfs_sz++] = succ->items[j]; 822*f8a8faceSAlexei Starovoitov goto dfs_continue; 823*f8a8faceSAlexei Starovoitov } 824*f8a8faceSAlexei Starovoitov } 825*f8a8faceSAlexei Starovoitov /* 826*f8a8faceSAlexei Starovoitov * Preserve the invariant: if some vertex above in the stack 827*f8a8faceSAlexei Starovoitov * is reachable from 'w', keep 'w' on the stack. 828*f8a8faceSAlexei Starovoitov */ 829*f8a8faceSAlexei Starovoitov if (low[w] < pre[w]) { 830*f8a8faceSAlexei Starovoitov dfs_sz--; 831*f8a8faceSAlexei Starovoitov goto dfs_continue; 832*f8a8faceSAlexei Starovoitov } 833*f8a8faceSAlexei Starovoitov /* 834*f8a8faceSAlexei Starovoitov * Assign SCC number only if component has two or more elements, 835*f8a8faceSAlexei Starovoitov * or if component has a self reference, or if instruction is a 836*f8a8faceSAlexei Starovoitov * callback calling function (implicit loop). 837*f8a8faceSAlexei Starovoitov */ 838*f8a8faceSAlexei Starovoitov assign_scc = stack[stack_sz - 1] != w; /* two or more elements? */ 839*f8a8faceSAlexei Starovoitov for (j = 0; j < succ->cnt; ++j) { /* self reference? */ 840*f8a8faceSAlexei Starovoitov if (succ->items[j] == w) { 841*f8a8faceSAlexei Starovoitov assign_scc = true; 842*f8a8faceSAlexei Starovoitov break; 843*f8a8faceSAlexei Starovoitov } 844*f8a8faceSAlexei Starovoitov } 845*f8a8faceSAlexei Starovoitov if (bpf_calls_callback(env, w)) /* implicit loop? */ 846*f8a8faceSAlexei Starovoitov assign_scc = true; 847*f8a8faceSAlexei Starovoitov /* Pop component elements from stack */ 848*f8a8faceSAlexei Starovoitov do { 849*f8a8faceSAlexei Starovoitov t = stack[--stack_sz]; 850*f8a8faceSAlexei Starovoitov low[t] = NOT_ON_STACK; 851*f8a8faceSAlexei Starovoitov if (assign_scc) 852*f8a8faceSAlexei Starovoitov aux[t].scc = next_scc_id; 853*f8a8faceSAlexei Starovoitov } while (t != w); 854*f8a8faceSAlexei Starovoitov if (assign_scc) 855*f8a8faceSAlexei Starovoitov next_scc_id++; 856*f8a8faceSAlexei Starovoitov dfs_sz--; 857*f8a8faceSAlexei Starovoitov } 858*f8a8faceSAlexei Starovoitov } 859*f8a8faceSAlexei Starovoitov env->scc_info = kvzalloc_objs(*env->scc_info, next_scc_id, 860*f8a8faceSAlexei Starovoitov GFP_KERNEL_ACCOUNT); 861*f8a8faceSAlexei Starovoitov if (!env->scc_info) { 862*f8a8faceSAlexei Starovoitov err = -ENOMEM; 863*f8a8faceSAlexei Starovoitov goto exit; 864*f8a8faceSAlexei Starovoitov } 865*f8a8faceSAlexei Starovoitov env->scc_cnt = next_scc_id; 866*f8a8faceSAlexei Starovoitov exit: 867*f8a8faceSAlexei Starovoitov kvfree(stack); 868*f8a8faceSAlexei Starovoitov kvfree(pre); 869*f8a8faceSAlexei Starovoitov kvfree(low); 870*f8a8faceSAlexei Starovoitov kvfree(dfs); 871*f8a8faceSAlexei Starovoitov return err; 872*f8a8faceSAlexei Starovoitov } 873