bpf: Preserve const register type on const OR alu ops
Occasionally, clang (e.g. version 3.8.1) translates a sum between two constant operands using a BPF_OR instead of a BPF_ADD. The verifier is currently not handling this scenario, and the destination register type becomes UNKNOWN_VALUE even if it's still storing a constant. As a result, the destination register cannot be used as argument to a helper function expecting a ARG_CONST_STACK_*, limiting some use cases. Modify the verifier to handle this case, and add a few tests to make sure all combinations are supported, and stack boundaries are still verified even with BPF_OR. Signed-off-by: Gianluca Borello <g.borello@gmail.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Acked-by: Daniel Borkmann <daniel@iogearbox.net> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Родитель
f0903ea371
Коммит
3c839744b3
|
@ -1481,14 +1481,19 @@ static int evaluate_reg_imm_alu(struct bpf_verifier_env *env,
|
||||||
struct bpf_reg_state *src_reg = ®s[insn->src_reg];
|
struct bpf_reg_state *src_reg = ®s[insn->src_reg];
|
||||||
u8 opcode = BPF_OP(insn->code);
|
u8 opcode = BPF_OP(insn->code);
|
||||||
|
|
||||||
/* dst_reg->type == CONST_IMM here, simulate execution of 'add' insn.
|
/* dst_reg->type == CONST_IMM here, simulate execution of 'add'/'or'
|
||||||
* Don't care about overflow or negative values, just add them
|
* insn. Don't care about overflow or negative values, just add them
|
||||||
*/
|
*/
|
||||||
if (opcode == BPF_ADD && BPF_SRC(insn->code) == BPF_K)
|
if (opcode == BPF_ADD && BPF_SRC(insn->code) == BPF_K)
|
||||||
dst_reg->imm += insn->imm;
|
dst_reg->imm += insn->imm;
|
||||||
else if (opcode == BPF_ADD && BPF_SRC(insn->code) == BPF_X &&
|
else if (opcode == BPF_ADD && BPF_SRC(insn->code) == BPF_X &&
|
||||||
src_reg->type == CONST_IMM)
|
src_reg->type == CONST_IMM)
|
||||||
dst_reg->imm += src_reg->imm;
|
dst_reg->imm += src_reg->imm;
|
||||||
|
else if (opcode == BPF_OR && BPF_SRC(insn->code) == BPF_K)
|
||||||
|
dst_reg->imm |= insn->imm;
|
||||||
|
else if (opcode == BPF_OR && BPF_SRC(insn->code) == BPF_X &&
|
||||||
|
src_reg->type == CONST_IMM)
|
||||||
|
dst_reg->imm |= src_reg->imm;
|
||||||
else
|
else
|
||||||
mark_reg_unknown_value(regs, insn->dst_reg);
|
mark_reg_unknown_value(regs, insn->dst_reg);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
test_verifier
|
test_verifier
|
||||||
test_maps
|
test_maps
|
||||||
|
test_lru_map
|
||||||
|
|
|
@ -2683,6 +2683,66 @@ static struct bpf_test tests[] = {
|
||||||
.errstr_unpriv = "R0 pointer arithmetic prohibited",
|
.errstr_unpriv = "R0 pointer arithmetic prohibited",
|
||||||
.result_unpriv = REJECT,
|
.result_unpriv = REJECT,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"constant register |= constant should keep constant type",
|
||||||
|
.insns = {
|
||||||
|
BPF_MOV64_REG(BPF_REG_1, BPF_REG_10),
|
||||||
|
BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -48),
|
||||||
|
BPF_MOV64_IMM(BPF_REG_2, 34),
|
||||||
|
BPF_ALU64_IMM(BPF_OR, BPF_REG_2, 13),
|
||||||
|
BPF_MOV64_IMM(BPF_REG_3, 0),
|
||||||
|
BPF_EMIT_CALL(BPF_FUNC_probe_read),
|
||||||
|
BPF_EXIT_INSN(),
|
||||||
|
},
|
||||||
|
.result = ACCEPT,
|
||||||
|
.prog_type = BPF_PROG_TYPE_TRACEPOINT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant register |= constant should not bypass stack boundary checks",
|
||||||
|
.insns = {
|
||||||
|
BPF_MOV64_REG(BPF_REG_1, BPF_REG_10),
|
||||||
|
BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -48),
|
||||||
|
BPF_MOV64_IMM(BPF_REG_2, 34),
|
||||||
|
BPF_ALU64_IMM(BPF_OR, BPF_REG_2, 24),
|
||||||
|
BPF_MOV64_IMM(BPF_REG_3, 0),
|
||||||
|
BPF_EMIT_CALL(BPF_FUNC_probe_read),
|
||||||
|
BPF_EXIT_INSN(),
|
||||||
|
},
|
||||||
|
.errstr = "invalid stack type R1 off=-48 access_size=58",
|
||||||
|
.result = REJECT,
|
||||||
|
.prog_type = BPF_PROG_TYPE_TRACEPOINT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant register |= constant register should keep constant type",
|
||||||
|
.insns = {
|
||||||
|
BPF_MOV64_REG(BPF_REG_1, BPF_REG_10),
|
||||||
|
BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -48),
|
||||||
|
BPF_MOV64_IMM(BPF_REG_2, 34),
|
||||||
|
BPF_MOV64_IMM(BPF_REG_4, 13),
|
||||||
|
BPF_ALU64_REG(BPF_OR, BPF_REG_2, BPF_REG_4),
|
||||||
|
BPF_MOV64_IMM(BPF_REG_3, 0),
|
||||||
|
BPF_EMIT_CALL(BPF_FUNC_probe_read),
|
||||||
|
BPF_EXIT_INSN(),
|
||||||
|
},
|
||||||
|
.result = ACCEPT,
|
||||||
|
.prog_type = BPF_PROG_TYPE_TRACEPOINT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant register |= constant register should not bypass stack boundary checks",
|
||||||
|
.insns = {
|
||||||
|
BPF_MOV64_REG(BPF_REG_1, BPF_REG_10),
|
||||||
|
BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -48),
|
||||||
|
BPF_MOV64_IMM(BPF_REG_2, 34),
|
||||||
|
BPF_MOV64_IMM(BPF_REG_4, 24),
|
||||||
|
BPF_ALU64_REG(BPF_OR, BPF_REG_2, BPF_REG_4),
|
||||||
|
BPF_MOV64_IMM(BPF_REG_3, 0),
|
||||||
|
BPF_EMIT_CALL(BPF_FUNC_probe_read),
|
||||||
|
BPF_EXIT_INSN(),
|
||||||
|
},
|
||||||
|
.errstr = "invalid stack type R1 off=-48 access_size=58",
|
||||||
|
.result = REJECT,
|
||||||
|
.prog_type = BPF_PROG_TYPE_TRACEPOINT,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static int probe_filter_length(const struct bpf_insn *fp)
|
static int probe_filter_length(const struct bpf_insn *fp)
|
||||||
|
|
Загрузка…
Ссылка в новой задаче