Skip to content

Commit 5f99f31

Browse files
anakryikoAlexei Starovoitov
authored andcommitted
bpf: add register bounds sanity checks and sanitization
Add simple sanity checks that validate well-formed ranges (min <= max) across u64, s64, u32, and s32 ranges. Also for cases when the value is constant (either 64-bit or 32-bit), we validate that ranges and tnums are in agreement. These bounds checks are performed at the end of BPF_ALU/BPF_ALU64 operations, on conditional jumps, and for LDX instructions (where subreg zero/sign extension is probably the most important to check). This covers most of the interesting cases. Also, we validate the sanity of the return register when manually adjusting it for some special helpers. By default, sanity violation will trigger a warning in verifier log and resetting register bounds to "unbounded" ones. But to aid development and debugging, BPF_F_TEST_SANITY_STRICT flag is added, which will trigger hard failure of verification with -EFAULT on register bounds violations. This allows selftests to catch such issues. veristat will also gain a CLI option to enable this behavior. Acked-by: Eduard Zingerman <[email protected]> Signed-off-by: Andrii Nakryiko <[email protected]> Acked-by: Shung-Hsi Yu <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]>
1 parent be41a20 commit 5f99f31

File tree

5 files changed

+101
-26
lines changed

5 files changed

+101
-26
lines changed

include/linux/bpf_verifier.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,7 @@ struct bpf_verifier_env {
602602
int stack_size; /* number of states to be processed */
603603
bool strict_alignment; /* perform strict pointer alignment checks */
604604
bool test_state_freq; /* test verifier with different pruning frequency */
605+
bool test_sanity_strict; /* fail verification on sanity violations */
605606
struct bpf_verifier_state *cur_state; /* current verifier state */
606607
struct bpf_verifier_state_list **explored_states; /* search pruning optimization */
607608
struct bpf_verifier_state_list *free_list;

include/uapi/linux/bpf.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1200,6 +1200,9 @@ enum bpf_perf_event_type {
12001200
*/
12011201
#define BPF_F_XDP_DEV_BOUND_ONLY (1U << 6)
12021202

1203+
/* The verifier internal test flag. Behavior is undefined */
1204+
#define BPF_F_TEST_SANITY_STRICT (1U << 7)
1205+
12031206
/* link_create.kprobe_multi.flags used in LINK_CREATE command for
12041207
* BPF_TRACE_KPROBE_MULTI attach type to create return probe.
12051208
*/

kernel/bpf/syscall.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2573,7 +2573,8 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size)
25732573
BPF_F_SLEEPABLE |
25742574
BPF_F_TEST_RND_HI32 |
25752575
BPF_F_XDP_HAS_FRAGS |
2576-
BPF_F_XDP_DEV_BOUND_ONLY))
2576+
BPF_F_XDP_DEV_BOUND_ONLY |
2577+
BPF_F_TEST_SANITY_STRICT))
25772578
return -EINVAL;
25782579

25792580
if (!IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) &&

kernel/bpf/verifier.c

Lines changed: 92 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2615,6 +2615,56 @@ static void reg_bounds_sync(struct bpf_reg_state *reg)
26152615
__update_reg_bounds(reg);
26162616
}
26172617

2618+
static int reg_bounds_sanity_check(struct bpf_verifier_env *env,
2619+
struct bpf_reg_state *reg, const char *ctx)
2620+
{
2621+
const char *msg;
2622+
2623+
if (reg->umin_value > reg->umax_value ||
2624+
reg->smin_value > reg->smax_value ||
2625+
reg->u32_min_value > reg->u32_max_value ||
2626+
reg->s32_min_value > reg->s32_max_value) {
2627+
msg = "range bounds violation";
2628+
goto out;
2629+
}
2630+
2631+
if (tnum_is_const(reg->var_off)) {
2632+
u64 uval = reg->var_off.value;
2633+
s64 sval = (s64)uval;
2634+
2635+
if (reg->umin_value != uval || reg->umax_value != uval ||
2636+
reg->smin_value != sval || reg->smax_value != sval) {
2637+
msg = "const tnum out of sync with range bounds";
2638+
goto out;
2639+
}
2640+
}
2641+
2642+
if (tnum_subreg_is_const(reg->var_off)) {
2643+
u32 uval32 = tnum_subreg(reg->var_off).value;
2644+
s32 sval32 = (s32)uval32;
2645+
2646+
if (reg->u32_min_value != uval32 || reg->u32_max_value != uval32 ||
2647+
reg->s32_min_value != sval32 || reg->s32_max_value != sval32) {
2648+
msg = "const subreg tnum out of sync with range bounds";
2649+
goto out;
2650+
}
2651+
}
2652+
2653+
return 0;
2654+
out:
2655+
verbose(env, "REG SANITY VIOLATION (%s): %s u64=[%#llx, %#llx] "
2656+
"s64=[%#llx, %#llx] u32=[%#x, %#x] s32=[%#x, %#x] var_off=(%#llx, %#llx)\n",
2657+
ctx, msg, reg->umin_value, reg->umax_value,
2658+
reg->smin_value, reg->smax_value,
2659+
reg->u32_min_value, reg->u32_max_value,
2660+
reg->s32_min_value, reg->s32_max_value,
2661+
reg->var_off.value, reg->var_off.mask);
2662+
if (env->test_sanity_strict)
2663+
return -EFAULT;
2664+
__mark_reg_unbounded(reg);
2665+
return 0;
2666+
}
2667+
26182668
static bool __reg32_bound_s64(s32 a)
26192669
{
26202670
return a >= 0 && a <= S32_MAX;
@@ -9982,14 +10032,15 @@ static int prepare_func_exit(struct bpf_verifier_env *env, int *insn_idx)
998210032
return 0;
998310033
}
998410034

9985-
static void do_refine_retval_range(struct bpf_reg_state *regs, int ret_type,
9986-
int func_id,
9987-
struct bpf_call_arg_meta *meta)
10035+
static int do_refine_retval_range(struct bpf_verifier_env *env,
10036+
struct bpf_reg_state *regs, int ret_type,
10037+
int func_id,
10038+
struct bpf_call_arg_meta *meta)
998810039
{
998910040
struct bpf_reg_state *ret_reg = &regs[BPF_REG_0];
999010041

999110042
if (ret_type != RET_INTEGER)
9992-
return;
10043+
return 0;
999310044

999410045
switch (func_id) {
999510046
case BPF_FUNC_get_stack:
@@ -10015,6 +10066,8 @@ static void do_refine_retval_range(struct bpf_reg_state *regs, int ret_type,
1001510066
reg_bounds_sync(ret_reg);
1001610067
break;
1001710068
}
10069+
10070+
return reg_bounds_sanity_check(env, ret_reg, "retval");
1001810071
}
1001910072

1002010073
static int
@@ -10666,7 +10719,9 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
1066610719
regs[BPF_REG_0].ref_obj_id = id;
1066710720
}
1066810721

10669-
do_refine_retval_range(regs, fn->ret_type, func_id, &meta);
10722+
err = do_refine_retval_range(env, regs, fn->ret_type, func_id, &meta);
10723+
if (err)
10724+
return err;
1067010725

1067110726
err = check_map_func_compatibility(env, meta.map_ptr, func_id);
1067210727
if (err)
@@ -14166,13 +14221,12 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn)
1416614221

1416714222
/* check dest operand */
1416814223
err = check_reg_arg(env, insn->dst_reg, DST_OP_NO_MARK);
14224+
err = err ?: adjust_reg_min_max_vals(env, insn);
1416914225
if (err)
1417014226
return err;
14171-
14172-
return adjust_reg_min_max_vals(env, insn);
1417314227
}
1417414228

14175-
return 0;
14229+
return reg_bounds_sanity_check(env, &regs[insn->dst_reg], "alu");
1417614230
}
1417714231

1417814232
static void find_good_pkt_pointers(struct bpf_verifier_state *vstate,
@@ -14653,18 +14707,21 @@ static void regs_refine_cond_op(struct bpf_reg_state *reg1, struct bpf_reg_state
1465314707
* Technically we can do similar adjustments for pointers to the same object,
1465414708
* but we don't support that right now.
1465514709
*/
14656-
static void reg_set_min_max(struct bpf_reg_state *true_reg1,
14657-
struct bpf_reg_state *true_reg2,
14658-
struct bpf_reg_state *false_reg1,
14659-
struct bpf_reg_state *false_reg2,
14660-
u8 opcode, bool is_jmp32)
14710+
static int reg_set_min_max(struct bpf_verifier_env *env,
14711+
struct bpf_reg_state *true_reg1,
14712+
struct bpf_reg_state *true_reg2,
14713+
struct bpf_reg_state *false_reg1,
14714+
struct bpf_reg_state *false_reg2,
14715+
u8 opcode, bool is_jmp32)
1466114716
{
14717+
int err;
14718+
1466214719
/* If either register is a pointer, we can't learn anything about its
1466314720
* variable offset from the compare (unless they were a pointer into
1466414721
* the same object, but we don't bother with that).
1466514722
*/
1466614723
if (false_reg1->type != SCALAR_VALUE || false_reg2->type != SCALAR_VALUE)
14667-
return;
14724+
return 0;
1466814725

1466914726
/* fallthrough (FALSE) branch */
1467014727
regs_refine_cond_op(false_reg1, false_reg2, rev_opcode(opcode), is_jmp32);
@@ -14675,6 +14732,12 @@ static void reg_set_min_max(struct bpf_reg_state *true_reg1,
1467514732
regs_refine_cond_op(true_reg1, true_reg2, opcode, is_jmp32);
1467614733
reg_bounds_sync(true_reg1);
1467714734
reg_bounds_sync(true_reg2);
14735+
14736+
err = reg_bounds_sanity_check(env, true_reg1, "true_reg1");
14737+
err = err ?: reg_bounds_sanity_check(env, true_reg2, "true_reg2");
14738+
err = err ?: reg_bounds_sanity_check(env, false_reg1, "false_reg1");
14739+
err = err ?: reg_bounds_sanity_check(env, false_reg2, "false_reg2");
14740+
return err;
1467814741
}
1467914742

1468014743
static void mark_ptr_or_null_reg(struct bpf_func_state *state,
@@ -14968,15 +15031,20 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env,
1496815031
other_branch_regs = other_branch->frame[other_branch->curframe]->regs;
1496915032

1497015033
if (BPF_SRC(insn->code) == BPF_X) {
14971-
reg_set_min_max(&other_branch_regs[insn->dst_reg],
14972-
&other_branch_regs[insn->src_reg],
14973-
dst_reg, src_reg, opcode, is_jmp32);
15034+
err = reg_set_min_max(env,
15035+
&other_branch_regs[insn->dst_reg],
15036+
&other_branch_regs[insn->src_reg],
15037+
dst_reg, src_reg, opcode, is_jmp32);
1497415038
} else /* BPF_SRC(insn->code) == BPF_K */ {
14975-
reg_set_min_max(&other_branch_regs[insn->dst_reg],
14976-
src_reg /* fake one */,
14977-
dst_reg, src_reg /* same fake one */,
14978-
opcode, is_jmp32);
15039+
err = reg_set_min_max(env,
15040+
&other_branch_regs[insn->dst_reg],
15041+
src_reg /* fake one */,
15042+
dst_reg, src_reg /* same fake one */,
15043+
opcode, is_jmp32);
1497915044
}
15045+
if (err)
15046+
return err;
15047+
1498015048
if (BPF_SRC(insn->code) == BPF_X &&
1498115049
src_reg->type == SCALAR_VALUE && src_reg->id &&
1498215050
!WARN_ON_ONCE(src_reg->id != other_branch_regs[insn->src_reg].id)) {
@@ -17479,10 +17547,8 @@ static int do_check(struct bpf_verifier_env *env)
1747917547
insn->off, BPF_SIZE(insn->code),
1748017548
BPF_READ, insn->dst_reg, false,
1748117549
BPF_MODE(insn->code) == BPF_MEMSX);
17482-
if (err)
17483-
return err;
17484-
17485-
err = save_aux_ptr_type(env, src_reg_type, true);
17550+
err = err ?: save_aux_ptr_type(env, src_reg_type, true);
17551+
err = err ?: reg_bounds_sanity_check(env, &regs[insn->dst_reg], "ldx");
1748617552
if (err)
1748717553
return err;
1748817554
} else if (class == BPF_STX) {
@@ -20769,6 +20835,7 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr, __u3
2076920835

2077020836
if (is_priv)
2077120837
env->test_state_freq = attr->prog_flags & BPF_F_TEST_STATE_FREQ;
20838+
env->test_sanity_strict = attr->prog_flags & BPF_F_TEST_SANITY_STRICT;
2077220839

2077320840
env->explored_states = kvcalloc(state_htab_size(env),
2077420841
sizeof(struct bpf_verifier_state_list *),

tools/include/uapi/linux/bpf.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1200,6 +1200,9 @@ enum bpf_perf_event_type {
12001200
*/
12011201
#define BPF_F_XDP_DEV_BOUND_ONLY (1U << 6)
12021202

1203+
/* The verifier internal test flag. Behavior is undefined */
1204+
#define BPF_F_TEST_SANITY_STRICT (1U << 7)
1205+
12031206
/* link_create.kprobe_multi.flags used in LINK_CREATE command for
12041207
* BPF_TRACE_KPROBE_MULTI attach type to create return probe.
12051208
*/

0 commit comments

Comments
 (0)