Skip to content

Commit 99992b1

Browse files
pchaignoKernel Patches Daemon
authored andcommitted
bpf: Save pruning point states in oracle
This patch saves information on the verifier states, at each pruning point, into bpf_insn_aux_data, for use by the BPF oracle. The verifier is already saving states into explored_states for state pruning, but we can't reuse it for the oracle. For state pruning, we only save a subset of all states at each pruning point. Specifically, we will only save a new state if we've seen at least 8 instructions and 2 BPF_JMPs since we last saved a state. For the oracle, we will use the saved information to ensure that concrete values match at least one verifier state. If we are missing states, we will have false positives. This patch therefore saves information on verifier states at every pruning point, regardless of existing heuristics. A later patch will limit this behavior to CONFIG_BPF_ORACLE. At the moment, the oracle only saves information on the type and ranges (in case of scalars) of registers. No information is kept for stack slots. More checks can be added later. Signed-off-by: Paul Chaignon <[email protected]>
1 parent 6ba2fc1 commit 99992b1

File tree

4 files changed

+103
-7
lines changed

4 files changed

+103
-7
lines changed

include/linux/bpf_verifier.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,30 @@ struct bpf_verifier_state_list {
485485
u32 in_free_list:1;
486486
};
487487

488+
struct bpf_reg_oracle_state {
489+
bool scalar;
490+
bool ptr_not_null;
491+
492+
struct tnum var_off;
493+
s64 smin_value;
494+
s64 smax_value;
495+
u64 umin_value;
496+
u64 umax_value;
497+
s32 s32_min_value;
498+
s32 s32_max_value;
499+
u32 u32_min_value;
500+
u32 u32_max_value;
501+
};
502+
503+
struct bpf_oracle_state {
504+
struct bpf_reg_oracle_state regs[MAX_BPF_REG - 1];
505+
};
506+
507+
struct bpf_oracle_state_list {
508+
struct bpf_oracle_state state;
509+
struct list_head node;
510+
};
511+
488512
struct bpf_loop_inline_state {
489513
unsigned int initialized:1; /* set to true upon first entry */
490514
unsigned int fit_for_inline:1; /* true if callback function is the same
@@ -551,6 +575,7 @@ struct bpf_insn_aux_data {
551575
};
552576
struct bpf_iarray *jt; /* jump table for gotox or bpf_tailcall call instruction */
553577
struct btf_struct_meta *kptr_struct_meta;
578+
struct list_head *oracle_states;
554579
u64 map_key_state; /* constant (32 bit) key tracking for maps */
555580
int ctx_field_size; /* the ctx field size for load insn, maybe 0 */
556581
u32 seen; /* this insn was processed by the verifier at env->pass_cnt */
@@ -1060,11 +1085,18 @@ static inline bool insn_is_gotox(struct bpf_insn *insn)
10601085
BPF_SRC(insn->code) == BPF_X;
10611086
}
10621087

1088+
static inline struct bpf_insn_aux_data *cur_aux(const struct bpf_verifier_env *env)
1089+
{
1090+
return &env->insn_aux_data[env->insn_idx];
1091+
}
1092+
10631093
const char *reg_type_str(struct bpf_verifier_env *env, enum bpf_reg_type type);
10641094
const char *dynptr_type_str(enum bpf_dynptr_type type);
10651095
const char *iter_type_str(const struct btf *btf, u32 btf_id);
10661096
const char *iter_state_str(enum bpf_iter_state state);
10671097

1098+
bool reg_not_null(const struct bpf_reg_state *reg);
1099+
10681100
void print_verifier_state(struct bpf_verifier_env *env, const struct bpf_verifier_state *vstate,
10691101
u32 frameno, bool print_all);
10701102
void print_insn_state(struct bpf_verifier_env *env, const struct bpf_verifier_state *vstate,
@@ -1087,4 +1119,6 @@ int bpf_live_stack_query_init(struct bpf_verifier_env *env, struct bpf_verifier_
10871119
bool bpf_stack_slot_alive(struct bpf_verifier_env *env, u32 frameno, u32 spi);
10881120
void bpf_reset_live_stack_callchain(struct bpf_verifier_env *env);
10891121

1122+
int save_state_in_oracle(struct bpf_verifier_env *env, int insn_idx);
1123+
10901124
#endif /* _LINUX_BPF_VERIFIER_H */

kernel/bpf/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ cflags-nogcse-$(CONFIG_X86)$(CONFIG_CC_IS_GCC) := -fno-gcse
66
endif
77
CFLAGS_core.o += -Wno-override-init $(cflags-nogcse-yy)
88

9-
obj-$(CONFIG_BPF_SYSCALL) += syscall.o verifier.o inode.o helpers.o tnum.o log.o token.o liveness.o
9+
obj-$(CONFIG_BPF_SYSCALL) += syscall.o verifier.o inode.o helpers.o tnum.o oracle.o log.o token.o liveness.o
1010
obj-$(CONFIG_BPF_SYSCALL) += bpf_iter.o map_iter.o task_iter.o prog_iter.o link_iter.o
1111
obj-$(CONFIG_BPF_SYSCALL) += hashtab.o arraymap.o percpu_freelist.o bpf_lru_list.o lpm_trie.o map_in_map.o bloom_filter.o
1212
obj-$(CONFIG_BPF_SYSCALL) += local_storage.o queue_stack_maps.o ringbuf.o bpf_insn_array.o

kernel/bpf/oracle.c

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/*
3+
* This file implements a test oracle for the verifier. When the oracle is enabled, the verifier
4+
* saves information on variables at regular points throughout the program. This information is
5+
* then compared at runtime with the concrete values to ensure that the verifier's information is
6+
* correct.
7+
*/
8+
9+
#include <linux/bpf_verifier.h>
10+
11+
static void convert_oracle_state(struct bpf_verifier_state *istate, struct bpf_oracle_state *ostate)
12+
{
13+
struct bpf_func_state *frame = istate->frame[istate->curframe];
14+
struct bpf_reg_oracle_state *oreg;
15+
struct bpf_reg_state *ireg;
16+
int i;
17+
18+
/* No need to check R10 with the oracle. */
19+
for (i = 0; i < MAX_BPF_REG - 1; i++) {
20+
ireg = &frame->regs[i];
21+
oreg = &ostate->regs[i];
22+
23+
oreg->scalar = ireg->type == SCALAR_VALUE;
24+
oreg->ptr_not_null = reg_not_null(ireg);
25+
26+
oreg->var_off = ireg->var_off;
27+
oreg->smin_value = ireg->smin_value;
28+
oreg->smax_value = ireg->smax_value;
29+
oreg->umin_value = ireg->umin_value;
30+
oreg->umax_value = ireg->umax_value;
31+
oreg->s32_min_value = ireg->s32_min_value;
32+
oreg->s32_max_value = ireg->s32_max_value;
33+
oreg->u32_min_value = ireg->u32_min_value;
34+
oreg->u32_max_value = ireg->u32_max_value;
35+
}
36+
}
37+
38+
int save_state_in_oracle(struct bpf_verifier_env *env, int insn_idx)
39+
{
40+
struct bpf_verifier_state *cur = env->cur_state;
41+
struct bpf_insn_aux_data *aux = cur_aux(env);
42+
struct bpf_oracle_state_list *new_sl;
43+
44+
if (env->subprog_cnt > 1)
45+
/* Skip the oracle if subprogs are used. */
46+
return 0;
47+
48+
if (!aux->oracle_states) {
49+
aux->oracle_states = kmalloc(sizeof(*aux->oracle_states), GFP_KERNEL_ACCOUNT);
50+
if (!aux->oracle_states)
51+
return -ENOMEM;
52+
53+
INIT_LIST_HEAD(aux->oracle_states);
54+
}
55+
56+
new_sl = kzalloc(sizeof(*new_sl), GFP_KERNEL_ACCOUNT);
57+
if (!new_sl)
58+
return -ENOMEM;
59+
convert_oracle_state(cur, &new_sl->state);
60+
list_add(&new_sl->node, aux->oracle_states);
61+
62+
return 0;
63+
}

kernel/bpf/verifier.c

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -394,7 +394,7 @@ static void verbose_invalid_scalar(struct bpf_verifier_env *env,
394394
verbose(env, " should have been in [%d, %d]\n", range.minval, range.maxval);
395395
}
396396

397-
static bool reg_not_null(const struct bpf_reg_state *reg)
397+
bool reg_not_null(const struct bpf_reg_state *reg)
398398
{
399399
enum bpf_reg_type type;
400400

@@ -11398,11 +11398,6 @@ static int check_get_func_ip(struct bpf_verifier_env *env)
1139811398
return -ENOTSUPP;
1139911399
}
1140011400

11401-
static struct bpf_insn_aux_data *cur_aux(const struct bpf_verifier_env *env)
11402-
{
11403-
return &env->insn_aux_data[env->insn_idx];
11404-
}
11405-
1140611401
static bool loop_flag_is_zero(struct bpf_verifier_env *env)
1140711402
{
1140811403
struct bpf_reg_state *regs = cur_regs(env);
@@ -20508,6 +20503,10 @@ static int do_check(struct bpf_verifier_env *env)
2050820503
state->insn_idx = env->insn_idx;
2050920504

2051020505
if (is_prune_point(env, env->insn_idx)) {
20506+
err = save_state_in_oracle(env, env->insn_idx);
20507+
if (err < 0)
20508+
return err;
20509+
2051120510
err = is_state_visited(env, env->insn_idx);
2051220511
if (err < 0)
2051320512
return err;

0 commit comments

Comments
 (0)