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