bpf: enforce return code for cgroup-bpf programs
with addition of tnum logic the verifier got smart enough and we can enforce return codes at program load time. For now do so for cgroup-bpf program types. 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:
parent
468e2f64d2
commit
390ee7e29f
2 changed files with 112 additions and 0 deletions
|
@ -3073,6 +3073,43 @@ static int check_ld_abs(struct bpf_verifier_env *env, struct bpf_insn *insn)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int check_return_code(struct bpf_verifier_env *env)
|
||||||
|
{
|
||||||
|
struct bpf_reg_state *reg;
|
||||||
|
struct tnum range = tnum_range(0, 1);
|
||||||
|
|
||||||
|
switch (env->prog->type) {
|
||||||
|
case BPF_PROG_TYPE_CGROUP_SKB:
|
||||||
|
case BPF_PROG_TYPE_CGROUP_SOCK:
|
||||||
|
case BPF_PROG_TYPE_SOCK_OPS:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
reg = &env->cur_state.regs[BPF_REG_0];
|
||||||
|
if (reg->type != SCALAR_VALUE) {
|
||||||
|
verbose("At program exit the register R0 is not a known value (%s)\n",
|
||||||
|
reg_type_str[reg->type]);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tnum_in(range, reg->var_off)) {
|
||||||
|
verbose("At program exit the register R0 ");
|
||||||
|
if (!tnum_is_unknown(reg->var_off)) {
|
||||||
|
char tn_buf[48];
|
||||||
|
|
||||||
|
tnum_strn(tn_buf, sizeof(tn_buf), reg->var_off);
|
||||||
|
verbose("has value %s", tn_buf);
|
||||||
|
} else {
|
||||||
|
verbose("has unknown scalar value");
|
||||||
|
}
|
||||||
|
verbose(" should have been 0 or 1\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* non-recursive DFS pseudo code
|
/* non-recursive DFS pseudo code
|
||||||
* 1 procedure DFS-iterative(G,v):
|
* 1 procedure DFS-iterative(G,v):
|
||||||
* 2 label v as discovered
|
* 2 label v as discovered
|
||||||
|
@ -3863,6 +3900,9 @@ static int do_check(struct bpf_verifier_env *env)
|
||||||
return -EACCES;
|
return -EACCES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = check_return_code(env);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
process_bpf_exit:
|
process_bpf_exit:
|
||||||
insn_idx = pop_stack(env, &prev_insn_idx);
|
insn_idx = pop_stack(env, &prev_insn_idx);
|
||||||
if (insn_idx < 0) {
|
if (insn_idx < 0) {
|
||||||
|
|
|
@ -6892,6 +6892,78 @@ static struct bpf_test tests[] = {
|
||||||
.result = ACCEPT,
|
.result = ACCEPT,
|
||||||
.prog_type = BPF_PROG_TYPE_XDP,
|
.prog_type = BPF_PROG_TYPE_XDP,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"bpf_exit with invalid return code. test1",
|
||||||
|
.insns = {
|
||||||
|
BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, 0),
|
||||||
|
BPF_EXIT_INSN(),
|
||||||
|
},
|
||||||
|
.errstr = "R0 has value (0x0; 0xffffffff)",
|
||||||
|
.result = REJECT,
|
||||||
|
.prog_type = BPF_PROG_TYPE_CGROUP_SOCK,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bpf_exit with invalid return code. test2",
|
||||||
|
.insns = {
|
||||||
|
BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, 0),
|
||||||
|
BPF_ALU64_IMM(BPF_AND, BPF_REG_0, 1),
|
||||||
|
BPF_EXIT_INSN(),
|
||||||
|
},
|
||||||
|
.result = ACCEPT,
|
||||||
|
.prog_type = BPF_PROG_TYPE_CGROUP_SOCK,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bpf_exit with invalid return code. test3",
|
||||||
|
.insns = {
|
||||||
|
BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, 0),
|
||||||
|
BPF_ALU64_IMM(BPF_AND, BPF_REG_0, 3),
|
||||||
|
BPF_EXIT_INSN(),
|
||||||
|
},
|
||||||
|
.errstr = "R0 has value (0x0; 0x3)",
|
||||||
|
.result = REJECT,
|
||||||
|
.prog_type = BPF_PROG_TYPE_CGROUP_SOCK,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bpf_exit with invalid return code. test4",
|
||||||
|
.insns = {
|
||||||
|
BPF_MOV64_IMM(BPF_REG_0, 1),
|
||||||
|
BPF_EXIT_INSN(),
|
||||||
|
},
|
||||||
|
.result = ACCEPT,
|
||||||
|
.prog_type = BPF_PROG_TYPE_CGROUP_SOCK,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bpf_exit with invalid return code. test5",
|
||||||
|
.insns = {
|
||||||
|
BPF_MOV64_IMM(BPF_REG_0, 2),
|
||||||
|
BPF_EXIT_INSN(),
|
||||||
|
},
|
||||||
|
.errstr = "R0 has value (0x2; 0x0)",
|
||||||
|
.result = REJECT,
|
||||||
|
.prog_type = BPF_PROG_TYPE_CGROUP_SOCK,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bpf_exit with invalid return code. test6",
|
||||||
|
.insns = {
|
||||||
|
BPF_MOV64_REG(BPF_REG_0, BPF_REG_1),
|
||||||
|
BPF_EXIT_INSN(),
|
||||||
|
},
|
||||||
|
.errstr = "R0 is not a known value (ctx)",
|
||||||
|
.result = REJECT,
|
||||||
|
.prog_type = BPF_PROG_TYPE_CGROUP_SOCK,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bpf_exit with invalid return code. test7",
|
||||||
|
.insns = {
|
||||||
|
BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, 0),
|
||||||
|
BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, 4),
|
||||||
|
BPF_ALU64_REG(BPF_MUL, BPF_REG_0, BPF_REG_2),
|
||||||
|
BPF_EXIT_INSN(),
|
||||||
|
},
|
||||||
|
.errstr = "R0 has unknown scalar value",
|
||||||
|
.result = REJECT,
|
||||||
|
.prog_type = BPF_PROG_TYPE_CGROUP_SOCK,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static int probe_filter_length(const struct bpf_insn *fp)
|
static int probe_filter_length(const struct bpf_insn *fp)
|
||||||
|
|
Loading…
Reference in a new issue