xref: /linux/kernel/bpf/const_fold.c (revision f5ad4101009e7f5f5984ffea6923d4fcd470932a)
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