Skip to content

Commit

Permalink
bpf: fix two bugs in verification logic when accessing 'ctx' pointer
Browse files Browse the repository at this point in the history
1.
first bug is a silly mistake. It broke tracing examples and prevented
simple bpf programs from loading.

In the following code:
if (insn->imm == 0 && BPF_SIZE(insn->code) == BPF_W) {
} else if (...) {
  // this part should have been executed when
  // insn->code == BPF_W and insn->imm != 0
}

Obviously it's not doing that. So simple instructions like:
r2 = *(u64 *)(r1 + 8)
will be rejected. Note the comments in the code around these branches
were and still valid and indicate the true intent.

Replace it with:
if (BPF_SIZE(insn->code) != BPF_W)
  continue;

if (insn->imm == 0) {
} else if (...) {
  // now this code will be executed when
  // insn->code == BPF_W and insn->imm != 0
}

2.
second bug is more subtle.
If malicious code is using the same dest register as source register,
the checks designed to prevent the same instruction to be used with different
pointer types will fail to trigger, since we were assigning src_reg_type
when it was already overwritten by check_mem_access().
The fix is trivial. Just move line:
src_reg_type = regs[insn->src_reg].type;
before check_mem_access().
Add new 'access skb fields bad4' test to check this case.

Fixes: 9bac3d6 ("bpf: allow extended BPF programs access skb fields")
Signed-off-by: Alexei Starovoitov <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
Alexei Starovoitov authored and davem330 committed Apr 16, 2015
1 parent a166151 commit 725f9dc
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 2 deletions.
9 changes: 7 additions & 2 deletions kernel/bpf/verifier.c
Original file line number Diff line number Diff line change
Expand Up @@ -1637,6 +1637,8 @@ static int do_check(struct verifier_env *env)
if (err)
return err;

src_reg_type = regs[insn->src_reg].type;

/* check that memory (src_reg + off) is readable,
* the state of dst_reg will be updated by this func
*/
Expand All @@ -1646,9 +1648,12 @@ static int do_check(struct verifier_env *env)
if (err)
return err;

src_reg_type = regs[insn->src_reg].type;
if (BPF_SIZE(insn->code) != BPF_W) {
insn_idx++;
continue;
}

if (insn->imm == 0 && BPF_SIZE(insn->code) == BPF_W) {
if (insn->imm == 0) {
/* saw a valid insn
* dst_reg = *(u32 *)(src_reg + off)
* use reserved 'imm' field to mark this insn
Expand Down
22 changes: 22 additions & 0 deletions samples/bpf/test_verifier.c
Original file line number Diff line number Diff line change
Expand Up @@ -721,6 +721,28 @@ static struct bpf_test tests[] = {
.errstr = "different pointers",
.result = REJECT,
},
{
"access skb fields bad4",
.insns = {
BPF_JMP_IMM(BPF_JGE, BPF_REG_1, 0, 3),
BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_1,
offsetof(struct __sk_buff, len)),
BPF_MOV64_IMM(BPF_REG_0, 0),
BPF_EXIT_INSN(),
BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
BPF_LD_MAP_FD(BPF_REG_1, 0),
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
BPF_EXIT_INSN(),
BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
BPF_JMP_IMM(BPF_JA, 0, 0, -13),
},
.fixup = {7},
.errstr = "different pointers",
.result = REJECT,
},
};

static int probe_filter_length(struct bpf_insn *fp)
Expand Down

0 comments on commit 725f9dc

Please sign in to comment.