1*f1606dd0SAlexei Starovoitov // SPDX-License-Identifier: GPL-2.0-only 2*f1606dd0SAlexei Starovoitov /* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */ 3*f1606dd0SAlexei Starovoitov 4*f1606dd0SAlexei Starovoitov #include <linux/bpf_verifier.h> 5*f1606dd0SAlexei Starovoitov 6*f1606dd0SAlexei Starovoitov /* 7*f1606dd0SAlexei Starovoitov * Forward dataflow analysis to determine constant register values at every 8*f1606dd0SAlexei Starovoitov * instruction. Tracks 64-bit constant values in R0-R9 through the program, 9*f1606dd0SAlexei Starovoitov * using a fixed-point iteration in reverse postorder. Records which registers 10*f1606dd0SAlexei Starovoitov * hold known constants and their values in 11*f1606dd0SAlexei Starovoitov * env->insn_aux_data[].{const_reg_mask, const_reg_vals}. 12*f1606dd0SAlexei Starovoitov */ 13*f1606dd0SAlexei Starovoitov 14*f1606dd0SAlexei Starovoitov enum const_arg_state { 15*f1606dd0SAlexei Starovoitov CONST_ARG_UNVISITED, /* instruction not yet reached */ 16*f1606dd0SAlexei Starovoitov CONST_ARG_UNKNOWN, /* register value not a known constant */ 17*f1606dd0SAlexei Starovoitov CONST_ARG_CONST, /* register holds a known 64-bit constant */ 18*f1606dd0SAlexei Starovoitov CONST_ARG_MAP_PTR, /* register holds a map pointer, map_index is set */ 19*f1606dd0SAlexei Starovoitov CONST_ARG_MAP_VALUE, /* register points to map value data, val is offset */ 20*f1606dd0SAlexei Starovoitov CONST_ARG_SUBPROG, /* register holds a subprog pointer, val is subprog number */ 21*f1606dd0SAlexei Starovoitov }; 22*f1606dd0SAlexei Starovoitov 23*f1606dd0SAlexei Starovoitov struct const_arg_info { 24*f1606dd0SAlexei Starovoitov enum const_arg_state state; 25*f1606dd0SAlexei Starovoitov u32 map_index; 26*f1606dd0SAlexei Starovoitov u64 val; 27*f1606dd0SAlexei Starovoitov }; 28*f1606dd0SAlexei Starovoitov 29*f1606dd0SAlexei Starovoitov static bool ci_is_unvisited(const struct const_arg_info *ci) 30*f1606dd0SAlexei Starovoitov { 31*f1606dd0SAlexei Starovoitov return ci->state == CONST_ARG_UNVISITED; 32*f1606dd0SAlexei Starovoitov } 33*f1606dd0SAlexei Starovoitov 34*f1606dd0SAlexei Starovoitov static bool ci_is_unknown(const struct const_arg_info *ci) 35*f1606dd0SAlexei Starovoitov { 36*f1606dd0SAlexei Starovoitov return ci->state == CONST_ARG_UNKNOWN; 37*f1606dd0SAlexei Starovoitov } 38*f1606dd0SAlexei Starovoitov 39*f1606dd0SAlexei Starovoitov static bool ci_is_const(const struct const_arg_info *ci) 40*f1606dd0SAlexei Starovoitov { 41*f1606dd0SAlexei Starovoitov return ci->state == CONST_ARG_CONST; 42*f1606dd0SAlexei Starovoitov } 43*f1606dd0SAlexei Starovoitov 44*f1606dd0SAlexei Starovoitov static bool ci_is_map_value(const struct const_arg_info *ci) 45*f1606dd0SAlexei Starovoitov { 46*f1606dd0SAlexei Starovoitov return ci->state == CONST_ARG_MAP_VALUE; 47*f1606dd0SAlexei Starovoitov } 48*f1606dd0SAlexei Starovoitov 49*f1606dd0SAlexei Starovoitov /* Transfer function: compute output register state from instruction. */ 50*f1606dd0SAlexei Starovoitov static void const_reg_xfer(struct bpf_verifier_env *env, struct const_arg_info *ci_out, 51*f1606dd0SAlexei Starovoitov struct bpf_insn *insn, struct bpf_insn *insns, int idx) 52*f1606dd0SAlexei Starovoitov { 53*f1606dd0SAlexei Starovoitov struct const_arg_info unknown = { .state = CONST_ARG_UNKNOWN, .val = 0 }; 54*f1606dd0SAlexei Starovoitov struct const_arg_info *dst = &ci_out[insn->dst_reg]; 55*f1606dd0SAlexei Starovoitov struct const_arg_info *src = &ci_out[insn->src_reg]; 56*f1606dd0SAlexei Starovoitov u8 class = BPF_CLASS(insn->code); 57*f1606dd0SAlexei Starovoitov u8 mode = BPF_MODE(insn->code); 58*f1606dd0SAlexei Starovoitov u8 opcode = BPF_OP(insn->code) | BPF_SRC(insn->code); 59*f1606dd0SAlexei Starovoitov int r; 60*f1606dd0SAlexei Starovoitov 61*f1606dd0SAlexei Starovoitov switch (class) { 62*f1606dd0SAlexei Starovoitov case BPF_ALU: 63*f1606dd0SAlexei Starovoitov case BPF_ALU64: 64*f1606dd0SAlexei Starovoitov switch (opcode) { 65*f1606dd0SAlexei Starovoitov case BPF_MOV | BPF_K: 66*f1606dd0SAlexei Starovoitov dst->state = CONST_ARG_CONST; 67*f1606dd0SAlexei Starovoitov dst->val = (s64)insn->imm; 68*f1606dd0SAlexei Starovoitov break; 69*f1606dd0SAlexei Starovoitov case BPF_MOV | BPF_X: 70*f1606dd0SAlexei Starovoitov *dst = *src; 71*f1606dd0SAlexei Starovoitov if (!insn->off) 72*f1606dd0SAlexei Starovoitov break; 73*f1606dd0SAlexei Starovoitov if (!ci_is_const(dst)) { 74*f1606dd0SAlexei Starovoitov *dst = unknown; 75*f1606dd0SAlexei Starovoitov break; 76*f1606dd0SAlexei Starovoitov } 77*f1606dd0SAlexei Starovoitov switch (insn->off) { 78*f1606dd0SAlexei Starovoitov case 8: dst->val = (s8)dst->val; break; 79*f1606dd0SAlexei Starovoitov case 16: dst->val = (s16)dst->val; break; 80*f1606dd0SAlexei Starovoitov case 32: dst->val = (s32)dst->val; break; 81*f1606dd0SAlexei Starovoitov default: *dst = unknown; break; 82*f1606dd0SAlexei Starovoitov } 83*f1606dd0SAlexei Starovoitov break; 84*f1606dd0SAlexei Starovoitov case BPF_ADD | BPF_K: 85*f1606dd0SAlexei Starovoitov if (!ci_is_const(dst) && !ci_is_map_value(dst)) { 86*f1606dd0SAlexei Starovoitov *dst = unknown; 87*f1606dd0SAlexei Starovoitov break; 88*f1606dd0SAlexei Starovoitov } 89*f1606dd0SAlexei Starovoitov dst->val += insn->imm; 90*f1606dd0SAlexei Starovoitov break; 91*f1606dd0SAlexei Starovoitov case BPF_SUB | BPF_K: 92*f1606dd0SAlexei Starovoitov if (!ci_is_const(dst) && !ci_is_map_value(dst)) { 93*f1606dd0SAlexei Starovoitov *dst = unknown; 94*f1606dd0SAlexei Starovoitov break; 95*f1606dd0SAlexei Starovoitov } 96*f1606dd0SAlexei Starovoitov dst->val -= insn->imm; 97*f1606dd0SAlexei Starovoitov break; 98*f1606dd0SAlexei Starovoitov case BPF_AND | BPF_K: 99*f1606dd0SAlexei Starovoitov if (!ci_is_const(dst)) { 100*f1606dd0SAlexei Starovoitov if (!insn->imm) { 101*f1606dd0SAlexei Starovoitov dst->state = CONST_ARG_CONST; 102*f1606dd0SAlexei Starovoitov dst->val = 0; 103*f1606dd0SAlexei Starovoitov } else { 104*f1606dd0SAlexei Starovoitov *dst = unknown; 105*f1606dd0SAlexei Starovoitov } 106*f1606dd0SAlexei Starovoitov break; 107*f1606dd0SAlexei Starovoitov } 108*f1606dd0SAlexei Starovoitov dst->val &= (s64)insn->imm; 109*f1606dd0SAlexei Starovoitov break; 110*f1606dd0SAlexei Starovoitov case BPF_AND | BPF_X: 111*f1606dd0SAlexei Starovoitov if (ci_is_const(dst) && dst->val == 0) 112*f1606dd0SAlexei Starovoitov break; /* 0 & x == 0 */ 113*f1606dd0SAlexei Starovoitov if (ci_is_const(src) && src->val == 0) { 114*f1606dd0SAlexei Starovoitov dst->state = CONST_ARG_CONST; 115*f1606dd0SAlexei Starovoitov dst->val = 0; 116*f1606dd0SAlexei Starovoitov break; 117*f1606dd0SAlexei Starovoitov } 118*f1606dd0SAlexei Starovoitov if (!ci_is_const(dst) || !ci_is_const(src)) { 119*f1606dd0SAlexei Starovoitov *dst = unknown; 120*f1606dd0SAlexei Starovoitov break; 121*f1606dd0SAlexei Starovoitov } 122*f1606dd0SAlexei Starovoitov dst->val &= src->val; 123*f1606dd0SAlexei Starovoitov break; 124*f1606dd0SAlexei Starovoitov default: 125*f1606dd0SAlexei Starovoitov *dst = unknown; 126*f1606dd0SAlexei Starovoitov break; 127*f1606dd0SAlexei Starovoitov } 128*f1606dd0SAlexei Starovoitov if (class == BPF_ALU) { 129*f1606dd0SAlexei Starovoitov if (ci_is_const(dst)) 130*f1606dd0SAlexei Starovoitov dst->val = (u32)dst->val; 131*f1606dd0SAlexei Starovoitov else if (!ci_is_unknown(dst)) 132*f1606dd0SAlexei Starovoitov *dst = unknown; 133*f1606dd0SAlexei Starovoitov } 134*f1606dd0SAlexei Starovoitov break; 135*f1606dd0SAlexei Starovoitov case BPF_LD: 136*f1606dd0SAlexei Starovoitov if (mode == BPF_ABS || mode == BPF_IND) 137*f1606dd0SAlexei Starovoitov goto process_call; 138*f1606dd0SAlexei Starovoitov if (mode != BPF_IMM || BPF_SIZE(insn->code) != BPF_DW) 139*f1606dd0SAlexei Starovoitov break; 140*f1606dd0SAlexei Starovoitov if (insn->src_reg == BPF_PSEUDO_FUNC) { 141*f1606dd0SAlexei Starovoitov int subprog = bpf_find_subprog(env, idx + insn->imm + 1); 142*f1606dd0SAlexei Starovoitov 143*f1606dd0SAlexei Starovoitov if (subprog >= 0) { 144*f1606dd0SAlexei Starovoitov dst->state = CONST_ARG_SUBPROG; 145*f1606dd0SAlexei Starovoitov dst->val = subprog; 146*f1606dd0SAlexei Starovoitov } else { 147*f1606dd0SAlexei Starovoitov *dst = unknown; 148*f1606dd0SAlexei Starovoitov } 149*f1606dd0SAlexei Starovoitov } else if (insn->src_reg == BPF_PSEUDO_MAP_VALUE || 150*f1606dd0SAlexei Starovoitov insn->src_reg == BPF_PSEUDO_MAP_IDX_VALUE) { 151*f1606dd0SAlexei Starovoitov dst->state = CONST_ARG_MAP_VALUE; 152*f1606dd0SAlexei Starovoitov dst->map_index = env->insn_aux_data[idx].map_index; 153*f1606dd0SAlexei Starovoitov dst->val = env->insn_aux_data[idx].map_off; 154*f1606dd0SAlexei Starovoitov } else if (insn->src_reg == BPF_PSEUDO_MAP_FD || 155*f1606dd0SAlexei Starovoitov insn->src_reg == BPF_PSEUDO_MAP_IDX) { 156*f1606dd0SAlexei Starovoitov dst->state = CONST_ARG_MAP_PTR; 157*f1606dd0SAlexei Starovoitov dst->map_index = env->insn_aux_data[idx].map_index; 158*f1606dd0SAlexei Starovoitov } else if (insn->src_reg == 0) { 159*f1606dd0SAlexei Starovoitov dst->state = CONST_ARG_CONST; 160*f1606dd0SAlexei Starovoitov dst->val = (u64)(u32)insn->imm | ((u64)(u32)insns[idx + 1].imm << 32); 161*f1606dd0SAlexei Starovoitov } else { 162*f1606dd0SAlexei Starovoitov *dst = unknown; 163*f1606dd0SAlexei Starovoitov } 164*f1606dd0SAlexei Starovoitov break; 165*f1606dd0SAlexei Starovoitov case BPF_LDX: 166*f1606dd0SAlexei Starovoitov if (!ci_is_map_value(src)) { 167*f1606dd0SAlexei Starovoitov *dst = unknown; 168*f1606dd0SAlexei Starovoitov break; 169*f1606dd0SAlexei Starovoitov } 170*f1606dd0SAlexei Starovoitov struct bpf_map *map = env->used_maps[src->map_index]; 171*f1606dd0SAlexei Starovoitov int size = bpf_size_to_bytes(BPF_SIZE(insn->code)); 172*f1606dd0SAlexei Starovoitov bool is_ldsx = mode == BPF_MEMSX; 173*f1606dd0SAlexei Starovoitov int off = src->val + insn->off; 174*f1606dd0SAlexei Starovoitov u64 val = 0; 175*f1606dd0SAlexei Starovoitov 176*f1606dd0SAlexei Starovoitov if (!bpf_map_is_rdonly(map) || !map->ops->map_direct_value_addr || 177*f1606dd0SAlexei Starovoitov map->map_type == BPF_MAP_TYPE_INSN_ARRAY || 178*f1606dd0SAlexei Starovoitov off < 0 || off + size > map->value_size || 179*f1606dd0SAlexei Starovoitov bpf_map_direct_read(map, off, size, &val, is_ldsx)) { 180*f1606dd0SAlexei Starovoitov *dst = unknown; 181*f1606dd0SAlexei Starovoitov break; 182*f1606dd0SAlexei Starovoitov } 183*f1606dd0SAlexei Starovoitov dst->state = CONST_ARG_CONST; 184*f1606dd0SAlexei Starovoitov dst->val = val; 185*f1606dd0SAlexei Starovoitov break; 186*f1606dd0SAlexei Starovoitov case BPF_JMP: 187*f1606dd0SAlexei Starovoitov if (opcode != BPF_CALL) 188*f1606dd0SAlexei Starovoitov break; 189*f1606dd0SAlexei Starovoitov process_call: 190*f1606dd0SAlexei Starovoitov for (r = BPF_REG_0; r <= BPF_REG_5; r++) 191*f1606dd0SAlexei Starovoitov ci_out[r] = unknown; 192*f1606dd0SAlexei Starovoitov break; 193*f1606dd0SAlexei Starovoitov case BPF_STX: 194*f1606dd0SAlexei Starovoitov if (mode != BPF_ATOMIC) 195*f1606dd0SAlexei Starovoitov break; 196*f1606dd0SAlexei Starovoitov if (insn->imm == BPF_CMPXCHG) 197*f1606dd0SAlexei Starovoitov ci_out[BPF_REG_0] = unknown; 198*f1606dd0SAlexei Starovoitov else if (insn->imm == BPF_LOAD_ACQ) 199*f1606dd0SAlexei Starovoitov *dst = unknown; 200*f1606dd0SAlexei Starovoitov else if (insn->imm & BPF_FETCH) 201*f1606dd0SAlexei Starovoitov *src = unknown; 202*f1606dd0SAlexei Starovoitov break; 203*f1606dd0SAlexei Starovoitov } 204*f1606dd0SAlexei Starovoitov } 205*f1606dd0SAlexei Starovoitov 206*f1606dd0SAlexei Starovoitov /* Join function: merge output state into a successor's input state. */ 207*f1606dd0SAlexei Starovoitov static bool const_reg_join(struct const_arg_info *ci_target, 208*f1606dd0SAlexei Starovoitov struct const_arg_info *ci_out) 209*f1606dd0SAlexei Starovoitov { 210*f1606dd0SAlexei Starovoitov bool changed = false; 211*f1606dd0SAlexei Starovoitov int r; 212*f1606dd0SAlexei Starovoitov 213*f1606dd0SAlexei Starovoitov for (r = 0; r < MAX_BPF_REG; r++) { 214*f1606dd0SAlexei Starovoitov struct const_arg_info *old = &ci_target[r]; 215*f1606dd0SAlexei Starovoitov struct const_arg_info *new = &ci_out[r]; 216*f1606dd0SAlexei Starovoitov 217*f1606dd0SAlexei Starovoitov if (ci_is_unvisited(old) && !ci_is_unvisited(new)) { 218*f1606dd0SAlexei Starovoitov ci_target[r] = *new; 219*f1606dd0SAlexei Starovoitov changed = true; 220*f1606dd0SAlexei Starovoitov } else if (!ci_is_unknown(old) && !ci_is_unvisited(old) && 221*f1606dd0SAlexei Starovoitov (new->state != old->state || new->val != old->val || 222*f1606dd0SAlexei Starovoitov new->map_index != old->map_index)) { 223*f1606dd0SAlexei Starovoitov old->state = CONST_ARG_UNKNOWN; 224*f1606dd0SAlexei Starovoitov changed = true; 225*f1606dd0SAlexei Starovoitov } 226*f1606dd0SAlexei Starovoitov } 227*f1606dd0SAlexei Starovoitov return changed; 228*f1606dd0SAlexei Starovoitov } 229*f1606dd0SAlexei Starovoitov 230*f1606dd0SAlexei Starovoitov int bpf_compute_const_regs(struct bpf_verifier_env *env) 231*f1606dd0SAlexei Starovoitov { 232*f1606dd0SAlexei Starovoitov struct const_arg_info unknown = { .state = CONST_ARG_UNKNOWN, .val = 0 }; 233*f1606dd0SAlexei Starovoitov struct bpf_insn_aux_data *insn_aux = env->insn_aux_data; 234*f1606dd0SAlexei Starovoitov struct bpf_insn *insns = env->prog->insnsi; 235*f1606dd0SAlexei Starovoitov int insn_cnt = env->prog->len; 236*f1606dd0SAlexei Starovoitov struct const_arg_info (*ci_in)[MAX_BPF_REG]; 237*f1606dd0SAlexei Starovoitov struct const_arg_info ci_out[MAX_BPF_REG]; 238*f1606dd0SAlexei Starovoitov struct bpf_iarray *succ; 239*f1606dd0SAlexei Starovoitov bool changed; 240*f1606dd0SAlexei Starovoitov int i, r; 241*f1606dd0SAlexei Starovoitov 242*f1606dd0SAlexei Starovoitov /* kvzalloc zeroes memory, so all entries start as CONST_ARG_UNVISITED (0) */ 243*f1606dd0SAlexei Starovoitov ci_in = kvzalloc_objs(*ci_in, insn_cnt, GFP_KERNEL_ACCOUNT); 244*f1606dd0SAlexei Starovoitov if (!ci_in) 245*f1606dd0SAlexei Starovoitov return -ENOMEM; 246*f1606dd0SAlexei Starovoitov 247*f1606dd0SAlexei Starovoitov /* Subprogram entries (including main at subprog 0): all registers unknown */ 248*f1606dd0SAlexei Starovoitov for (i = 0; i < env->subprog_cnt; i++) { 249*f1606dd0SAlexei Starovoitov int start = env->subprog_info[i].start; 250*f1606dd0SAlexei Starovoitov 251*f1606dd0SAlexei Starovoitov for (r = 0; r < MAX_BPF_REG; r++) 252*f1606dd0SAlexei Starovoitov ci_in[start][r] = unknown; 253*f1606dd0SAlexei Starovoitov } 254*f1606dd0SAlexei Starovoitov 255*f1606dd0SAlexei Starovoitov redo: 256*f1606dd0SAlexei Starovoitov changed = false; 257*f1606dd0SAlexei Starovoitov for (i = env->cfg.cur_postorder - 1; i >= 0; i--) { 258*f1606dd0SAlexei Starovoitov int idx = env->cfg.insn_postorder[i]; 259*f1606dd0SAlexei Starovoitov struct bpf_insn *insn = &insns[idx]; 260*f1606dd0SAlexei Starovoitov struct const_arg_info *ci = ci_in[idx]; 261*f1606dd0SAlexei Starovoitov 262*f1606dd0SAlexei Starovoitov memcpy(ci_out, ci, sizeof(ci_out)); 263*f1606dd0SAlexei Starovoitov 264*f1606dd0SAlexei Starovoitov const_reg_xfer(env, ci_out, insn, insns, idx); 265*f1606dd0SAlexei Starovoitov 266*f1606dd0SAlexei Starovoitov succ = bpf_insn_successors(env, idx); 267*f1606dd0SAlexei Starovoitov for (int s = 0; s < succ->cnt; s++) 268*f1606dd0SAlexei Starovoitov changed |= const_reg_join(ci_in[succ->items[s]], ci_out); 269*f1606dd0SAlexei Starovoitov } 270*f1606dd0SAlexei Starovoitov if (changed) 271*f1606dd0SAlexei Starovoitov goto redo; 272*f1606dd0SAlexei Starovoitov 273*f1606dd0SAlexei Starovoitov /* Save computed constants into insn_aux[] if they fit into 32-bit */ 274*f1606dd0SAlexei Starovoitov for (i = 0; i < insn_cnt; i++) { 275*f1606dd0SAlexei Starovoitov u16 mask = 0, map_mask = 0, subprog_mask = 0; 276*f1606dd0SAlexei Starovoitov struct bpf_insn_aux_data *aux = &insn_aux[i]; 277*f1606dd0SAlexei Starovoitov struct const_arg_info *ci = ci_in[i]; 278*f1606dd0SAlexei Starovoitov 279*f1606dd0SAlexei Starovoitov for (r = BPF_REG_0; r < ARRAY_SIZE(aux->const_reg_vals); r++) { 280*f1606dd0SAlexei Starovoitov struct const_arg_info *c = &ci[r]; 281*f1606dd0SAlexei Starovoitov 282*f1606dd0SAlexei Starovoitov switch (c->state) { 283*f1606dd0SAlexei Starovoitov case CONST_ARG_CONST: { 284*f1606dd0SAlexei Starovoitov u64 val = c->val; 285*f1606dd0SAlexei Starovoitov 286*f1606dd0SAlexei Starovoitov if (val != (u32)val) 287*f1606dd0SAlexei Starovoitov break; 288*f1606dd0SAlexei Starovoitov mask |= BIT(r); 289*f1606dd0SAlexei Starovoitov aux->const_reg_vals[r] = val; 290*f1606dd0SAlexei Starovoitov break; 291*f1606dd0SAlexei Starovoitov } 292*f1606dd0SAlexei Starovoitov case CONST_ARG_MAP_PTR: 293*f1606dd0SAlexei Starovoitov map_mask |= BIT(r); 294*f1606dd0SAlexei Starovoitov aux->const_reg_vals[r] = c->map_index; 295*f1606dd0SAlexei Starovoitov break; 296*f1606dd0SAlexei Starovoitov case CONST_ARG_SUBPROG: 297*f1606dd0SAlexei Starovoitov subprog_mask |= BIT(r); 298*f1606dd0SAlexei Starovoitov aux->const_reg_vals[r] = c->val; 299*f1606dd0SAlexei Starovoitov break; 300*f1606dd0SAlexei Starovoitov default: 301*f1606dd0SAlexei Starovoitov break; 302*f1606dd0SAlexei Starovoitov } 303*f1606dd0SAlexei Starovoitov } 304*f1606dd0SAlexei Starovoitov aux->const_reg_mask = mask; 305*f1606dd0SAlexei Starovoitov aux->const_reg_map_mask = map_mask; 306*f1606dd0SAlexei Starovoitov aux->const_reg_subprog_mask = subprog_mask; 307*f1606dd0SAlexei Starovoitov } 308*f1606dd0SAlexei Starovoitov 309*f1606dd0SAlexei Starovoitov kvfree(ci_in); 310*f1606dd0SAlexei Starovoitov return 0; 311*f1606dd0SAlexei Starovoitov } 312*f1606dd0SAlexei Starovoitov 313*f1606dd0SAlexei Starovoitov static int eval_const_branch(u8 opcode, u64 dst_val, u64 src_val) 314*f1606dd0SAlexei Starovoitov { 315*f1606dd0SAlexei Starovoitov switch (BPF_OP(opcode)) { 316*f1606dd0SAlexei Starovoitov case BPF_JEQ: return dst_val == src_val; 317*f1606dd0SAlexei Starovoitov case BPF_JNE: return dst_val != src_val; 318*f1606dd0SAlexei Starovoitov case BPF_JGT: return dst_val > src_val; 319*f1606dd0SAlexei Starovoitov case BPF_JGE: return dst_val >= src_val; 320*f1606dd0SAlexei Starovoitov case BPF_JLT: return dst_val < src_val; 321*f1606dd0SAlexei Starovoitov case BPF_JLE: return dst_val <= src_val; 322*f1606dd0SAlexei Starovoitov case BPF_JSGT: return (s64)dst_val > (s64)src_val; 323*f1606dd0SAlexei Starovoitov case BPF_JSGE: return (s64)dst_val >= (s64)src_val; 324*f1606dd0SAlexei Starovoitov case BPF_JSLT: return (s64)dst_val < (s64)src_val; 325*f1606dd0SAlexei Starovoitov case BPF_JSLE: return (s64)dst_val <= (s64)src_val; 326*f1606dd0SAlexei Starovoitov case BPF_JSET: return (bool)(dst_val & src_val); 327*f1606dd0SAlexei Starovoitov default: return -1; 328*f1606dd0SAlexei Starovoitov } 329*f1606dd0SAlexei Starovoitov } 330*f1606dd0SAlexei Starovoitov 331*f1606dd0SAlexei Starovoitov /* 332*f1606dd0SAlexei Starovoitov * Rewrite conditional branches with constant outcomes into unconditional 333*f1606dd0SAlexei Starovoitov * jumps using register values resolved by bpf_compute_const_regs() pass. 334*f1606dd0SAlexei Starovoitov * This eliminates dead edges from the CFG so that compute_live_registers() 335*f1606dd0SAlexei Starovoitov * doesn't propagate liveness through dead code. 336*f1606dd0SAlexei Starovoitov */ 337*f1606dd0SAlexei Starovoitov int bpf_prune_dead_branches(struct bpf_verifier_env *env) 338*f1606dd0SAlexei Starovoitov { 339*f1606dd0SAlexei Starovoitov struct bpf_insn_aux_data *insn_aux = env->insn_aux_data; 340*f1606dd0SAlexei Starovoitov struct bpf_insn *insns = env->prog->insnsi; 341*f1606dd0SAlexei Starovoitov int insn_cnt = env->prog->len; 342*f1606dd0SAlexei Starovoitov bool changed = false; 343*f1606dd0SAlexei Starovoitov int i; 344*f1606dd0SAlexei Starovoitov 345*f1606dd0SAlexei Starovoitov for (i = 0; i < insn_cnt; i++) { 346*f1606dd0SAlexei Starovoitov struct bpf_insn_aux_data *aux = &insn_aux[i]; 347*f1606dd0SAlexei Starovoitov struct bpf_insn *insn = &insns[i]; 348*f1606dd0SAlexei Starovoitov u8 class = BPF_CLASS(insn->code); 349*f1606dd0SAlexei Starovoitov u64 dst_val, src_val; 350*f1606dd0SAlexei Starovoitov int taken; 351*f1606dd0SAlexei Starovoitov 352*f1606dd0SAlexei Starovoitov if (!bpf_insn_is_cond_jump(insn->code)) 353*f1606dd0SAlexei Starovoitov continue; 354*f1606dd0SAlexei Starovoitov if (bpf_is_may_goto_insn(insn)) 355*f1606dd0SAlexei Starovoitov continue; 356*f1606dd0SAlexei Starovoitov 357*f1606dd0SAlexei Starovoitov if (!(aux->const_reg_mask & BIT(insn->dst_reg))) 358*f1606dd0SAlexei Starovoitov continue; 359*f1606dd0SAlexei Starovoitov dst_val = aux->const_reg_vals[insn->dst_reg]; 360*f1606dd0SAlexei Starovoitov 361*f1606dd0SAlexei Starovoitov if (BPF_SRC(insn->code) == BPF_K) { 362*f1606dd0SAlexei Starovoitov src_val = insn->imm; 363*f1606dd0SAlexei Starovoitov } else { 364*f1606dd0SAlexei Starovoitov if (!(aux->const_reg_mask & BIT(insn->src_reg))) 365*f1606dd0SAlexei Starovoitov continue; 366*f1606dd0SAlexei Starovoitov src_val = aux->const_reg_vals[insn->src_reg]; 367*f1606dd0SAlexei Starovoitov } 368*f1606dd0SAlexei Starovoitov 369*f1606dd0SAlexei Starovoitov if (class == BPF_JMP32) { 370*f1606dd0SAlexei Starovoitov /* 371*f1606dd0SAlexei Starovoitov * The (s32) cast maps the 32-bit range into two u64 sub-ranges: 372*f1606dd0SAlexei Starovoitov * [0x00000000, 0x7FFFFFFF] -> [0x0000000000000000, 0x000000007FFFFFFF] 373*f1606dd0SAlexei Starovoitov * [0x80000000, 0xFFFFFFFF] -> [0xFFFFFFFF80000000, 0xFFFFFFFFFFFFFFFF] 374*f1606dd0SAlexei Starovoitov * The ordering is preserved within each sub-range, and 375*f1606dd0SAlexei Starovoitov * the second sub-range is above the first as u64. 376*f1606dd0SAlexei Starovoitov */ 377*f1606dd0SAlexei Starovoitov dst_val = (s32)dst_val; 378*f1606dd0SAlexei Starovoitov src_val = (s32)src_val; 379*f1606dd0SAlexei Starovoitov } 380*f1606dd0SAlexei Starovoitov 381*f1606dd0SAlexei Starovoitov taken = eval_const_branch(insn->code, dst_val, src_val); 382*f1606dd0SAlexei Starovoitov if (taken < 0) { 383*f1606dd0SAlexei Starovoitov bpf_log(&env->log, "Unknown conditional jump %x\n", insn->code); 384*f1606dd0SAlexei Starovoitov return -EFAULT; 385*f1606dd0SAlexei Starovoitov } 386*f1606dd0SAlexei Starovoitov *insn = BPF_JMP_A(taken ? insn->off : 0); 387*f1606dd0SAlexei Starovoitov changed = true; 388*f1606dd0SAlexei Starovoitov } 389*f1606dd0SAlexei Starovoitov 390*f1606dd0SAlexei Starovoitov if (!changed) 391*f1606dd0SAlexei Starovoitov return 0; 392*f1606dd0SAlexei Starovoitov /* recompute postorder, since CFG has changed */ 393*f1606dd0SAlexei Starovoitov kvfree(env->cfg.insn_postorder); 394*f1606dd0SAlexei Starovoitov env->cfg.insn_postorder = NULL; 395*f1606dd0SAlexei Starovoitov return bpf_compute_postorder(env); 396*f1606dd0SAlexei Starovoitov } 397