Commit fbeb1603 authored by Arthur Fabre's avatar Arthur Fabre Committed by Daniel Borkmann

bpf: verifier: MOV64 don't mark dst reg unbounded

When check_alu_op() handles a BPF_MOV64 between two registers,
it calls check_reg_arg(DST_OP) on the dst register, marking it
as unbounded. If the src and dst register are the same, this
marks the src as unbounded, which can lead to unexpected errors
for further checks that rely on bounds info. For example:

	BPF_MOV64_IMM(BPF_REG_2, 0),
	BPF_MOV64_REG(BPF_REG_2, BPF_REG_2),
	BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_2),
	BPF_MOV64_IMM(BPF_REG_0, 0),
	BPF_EXIT_INSN(),

Results in:

	"math between ctx pointer and register with unbounded
	min value is not allowed"

check_alu_op() now uses check_reg_arg(DST_OP_NO_MARK), and MOVs
that need to mark the dst register (MOVIMM, MOV32) do so.

Added a test case for MOV64 dst == src, and dst != src.
Signed-off-by: default avatarArthur Fabre <afabre@cloudflare.com>
Acked-by: default avatarEdward Cree <ecree@solarflare.com>
Signed-off-by: default avatarDaniel Borkmann <daniel@iogearbox.net>
parent 194db0d9
...@@ -3238,8 +3238,8 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn) ...@@ -3238,8 +3238,8 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn)
} }
} }
/* check dest operand */ /* check dest operand, mark as required later */
err = check_reg_arg(env, insn->dst_reg, DST_OP); err = check_reg_arg(env, insn->dst_reg, DST_OP_NO_MARK);
if (err) if (err)
return err; return err;
...@@ -3265,6 +3265,8 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn) ...@@ -3265,6 +3265,8 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn)
/* case: R = imm /* case: R = imm
* remember the value we stored into this reg * remember the value we stored into this reg
*/ */
/* clear any state __mark_reg_known doesn't set */
mark_reg_unknown(env, regs, insn->dst_reg);
regs[insn->dst_reg].type = SCALAR_VALUE; regs[insn->dst_reg].type = SCALAR_VALUE;
if (BPF_CLASS(insn->code) == BPF_ALU64) { if (BPF_CLASS(insn->code) == BPF_ALU64) {
__mark_reg_known(regs + insn->dst_reg, __mark_reg_known(regs + insn->dst_reg,
......
...@@ -12332,6 +12332,32 @@ static struct bpf_test tests[] = { ...@@ -12332,6 +12332,32 @@ static struct bpf_test tests[] = {
.result = REJECT, .result = REJECT,
.errstr = "variable ctx access var_off=(0x0; 0x4)", .errstr = "variable ctx access var_off=(0x0; 0x4)",
}, },
{
"mov64 src == dst",
.insns = {
BPF_MOV64_IMM(BPF_REG_2, 0),
BPF_MOV64_REG(BPF_REG_2, BPF_REG_2),
// Check bounds are OK
BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_2),
BPF_MOV64_IMM(BPF_REG_0, 0),
BPF_EXIT_INSN(),
},
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
.result = ACCEPT,
},
{
"mov64 src != dst",
.insns = {
BPF_MOV64_IMM(BPF_REG_3, 0),
BPF_MOV64_REG(BPF_REG_2, BPF_REG_3),
// Check bounds are OK
BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_2),
BPF_MOV64_IMM(BPF_REG_0, 0),
BPF_EXIT_INSN(),
},
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
.result = ACCEPT,
},
}; };
static int probe_filter_length(const struct bpf_insn *fp) static int probe_filter_length(const struct bpf_insn *fp)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment