Skip to content

Commit f0318d0

Browse files
gianlucaborellodavem330
authored andcommitted
bpf: allow adjusted map element values to spill
commit 4846113 ("bpf: allow access into map value arrays") introduces the ability to do pointer math inside a map element value via the PTR_TO_MAP_VALUE_ADJ register type. The current support doesn't handle the case where a PTR_TO_MAP_VALUE_ADJ is spilled into the stack, limiting several use cases, especially when generating bpf code from a compiler. Handle this case by explicitly enabling the register type PTR_TO_MAP_VALUE_ADJ to be spilled. Also, make sure that min_value and max_value are reset just for BPF_LDX operations that don't result in a restore of a spilled register from stack. Signed-off-by: Gianluca Borello <[email protected]> Acked-by: Daniel Borkmann <[email protected]> Signed-off-by: Alexei Starovoitov <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 5722569 commit f0318d0

File tree

2 files changed

+62
-5
lines changed

2 files changed

+62
-5
lines changed

kernel/bpf/verifier.c

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,13 @@ static void reset_reg_range_values(struct bpf_reg_state *regs, u32 regno)
481481
regs[regno].max_value = BPF_REGISTER_MAX_RANGE;
482482
}
483483

484+
static void mark_reg_unknown_value_and_range(struct bpf_reg_state *regs,
485+
u32 regno)
486+
{
487+
mark_reg_unknown_value(regs, regno);
488+
reset_reg_range_values(regs, regno);
489+
}
490+
484491
enum reg_arg_type {
485492
SRC_OP, /* register is used as source operand */
486493
DST_OP, /* register is used as destination operand */
@@ -532,6 +539,7 @@ static bool is_spillable_regtype(enum bpf_reg_type type)
532539
switch (type) {
533540
case PTR_TO_MAP_VALUE:
534541
case PTR_TO_MAP_VALUE_OR_NULL:
542+
case PTR_TO_MAP_VALUE_ADJ:
535543
case PTR_TO_STACK:
536544
case PTR_TO_CTX:
537545
case PTR_TO_PACKET:
@@ -616,7 +624,8 @@ static int check_stack_read(struct bpf_verifier_state *state, int off, int size,
616624
}
617625
if (value_regno >= 0)
618626
/* have read misc data from the stack */
619-
mark_reg_unknown_value(state->regs, value_regno);
627+
mark_reg_unknown_value_and_range(state->regs,
628+
value_regno);
620629
return 0;
621630
}
622631
}
@@ -825,7 +834,8 @@ static int check_mem_access(struct bpf_verifier_env *env, u32 regno, int off,
825834
else
826835
err = check_map_access(env, regno, off, size);
827836
if (!err && t == BPF_READ && value_regno >= 0)
828-
mark_reg_unknown_value(state->regs, value_regno);
837+
mark_reg_unknown_value_and_range(state->regs,
838+
value_regno);
829839

830840
} else if (reg->type == PTR_TO_CTX) {
831841
enum bpf_reg_type reg_type = UNKNOWN_VALUE;
@@ -837,7 +847,8 @@ static int check_mem_access(struct bpf_verifier_env *env, u32 regno, int off,
837847
}
838848
err = check_ctx_access(env, off, size, t, &reg_type);
839849
if (!err && t == BPF_READ && value_regno >= 0) {
840-
mark_reg_unknown_value(state->regs, value_regno);
850+
mark_reg_unknown_value_and_range(state->regs,
851+
value_regno);
841852
/* note that reg.[id|off|range] == 0 */
842853
state->regs[value_regno].type = reg_type;
843854
}
@@ -870,7 +881,8 @@ static int check_mem_access(struct bpf_verifier_env *env, u32 regno, int off,
870881
}
871882
err = check_packet_access(env, regno, off, size);
872883
if (!err && t == BPF_READ && value_regno >= 0)
873-
mark_reg_unknown_value(state->regs, value_regno);
884+
mark_reg_unknown_value_and_range(state->regs,
885+
value_regno);
874886
} else {
875887
verbose("R%d invalid mem access '%s'\n",
876888
regno, reg_type_str[reg->type]);
@@ -2744,7 +2756,6 @@ static int do_check(struct bpf_verifier_env *env)
27442756
if (err)
27452757
return err;
27462758

2747-
reset_reg_range_values(regs, insn->dst_reg);
27482759
if (BPF_SIZE(insn->code) != BPF_W &&
27492760
BPF_SIZE(insn->code) != BPF_DW) {
27502761
insn_idx++;

tools/testing/selftests/bpf/test_verifier.c

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3396,6 +3396,52 @@ static struct bpf_test tests[] = {
33963396
.result = REJECT,
33973397
.prog_type = BPF_PROG_TYPE_TRACEPOINT,
33983398
},
3399+
{
3400+
"map element value is preserved across register spilling",
3401+
.insns = {
3402+
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
3403+
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
3404+
BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0),
3405+
BPF_LD_MAP_FD(BPF_REG_1, 0),
3406+
BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
3407+
BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 6),
3408+
BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 42),
3409+
BPF_MOV64_REG(BPF_REG_1, BPF_REG_10),
3410+
BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -184),
3411+
BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, 0),
3412+
BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_1, 0),
3413+
BPF_ST_MEM(BPF_DW, BPF_REG_3, 0, 42),
3414+
BPF_EXIT_INSN(),
3415+
},
3416+
.fixup_map2 = { 3 },
3417+
.errstr_unpriv = "R0 leaks addr",
3418+
.result = ACCEPT,
3419+
.result_unpriv = REJECT,
3420+
},
3421+
{
3422+
"map element value (adjusted) is preserved across register spilling",
3423+
.insns = {
3424+
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
3425+
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
3426+
BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0),
3427+
BPF_LD_MAP_FD(BPF_REG_1, 0),
3428+
BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
3429+
BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 7),
3430+
BPF_ALU64_IMM(BPF_ADD, BPF_REG_0,
3431+
offsetof(struct test_val, foo)),
3432+
BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 42),
3433+
BPF_MOV64_REG(BPF_REG_1, BPF_REG_10),
3434+
BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -184),
3435+
BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, 0),
3436+
BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_1, 0),
3437+
BPF_ST_MEM(BPF_DW, BPF_REG_3, 0, 42),
3438+
BPF_EXIT_INSN(),
3439+
},
3440+
.fixup_map2 = { 3 },
3441+
.errstr_unpriv = "R0 pointer arithmetic prohibited",
3442+
.result = ACCEPT,
3443+
.result_unpriv = REJECT,
3444+
},
33993445
};
34003446

34013447
static int probe_filter_length(const struct bpf_insn *fp)

0 commit comments

Comments
 (0)