Skip to content

Commit

Permalink
bpf: enforce return code for cgroup-bpf programs
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Alexei Starovoitov authored and David S. Miller committed Oct 4, 2017
1 parent 468e2f6 commit 390ee7e
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 0 deletions.
40 changes: 40 additions & 0 deletions kernel/bpf/verifier.c
Original file line number Diff line number Diff line change
Expand Up @@ -3073,6 +3073,43 @@ static int check_ld_abs(struct bpf_verifier_env *env, struct bpf_insn *insn)
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
* 1 procedure DFS-iterative(G,v):
* 2 label v as discovered
Expand Down Expand Up @@ -3863,6 +3900,9 @@ static int do_check(struct bpf_verifier_env *env)
return -EACCES;
}

err = check_return_code(env);
if (err)
return err;
process_bpf_exit:
insn_idx = pop_stack(env, &prev_insn_idx);
if (insn_idx < 0) {
Expand Down
72 changes: 72 additions & 0 deletions tools/testing/selftests/bpf/test_verifier.c
Original file line number Diff line number Diff line change
Expand Up @@ -6892,6 +6892,78 @@ static struct bpf_test tests[] = {
.result = ACCEPT,
.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)
Expand Down

0 comments on commit 390ee7e

Please sign in to comment.