bpf: reject stores into ctx via st and xadd
authorDaniel Borkmann <daniel@iogearbox.net>
Tue, 16 Jan 2018 22:30:10 +0000 (23:30 +0100)
committerAlexei Starovoitov <ast@kernel.org>
Tue, 16 Jan 2018 23:04:58 +0000 (15:04 -0800)
Alexei found that verifier does not reject stores into context
via BPF_ST instead of BPF_STX. And while looking at it, we
also should not allow XADD variant of BPF_STX.

The context rewriter is only assuming either BPF_LDX_MEM- or
BPF_STX_MEM-type operations, thus reject anything other than
that so that assumptions in the rewriter properly hold. Add
test cases as well for BPF selftests.

Fixes: d691f9e8d440 ("bpf: allow programs to write to certain skb fields")
Reported-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
kernel/bpf/verifier.c
tools/testing/selftests/bpf/test_verifier.c

index b744834..eb062b0 100644 (file)
@@ -978,6 +978,13 @@ static bool is_pointer_value(struct bpf_verifier_env *env, int regno)
        return __is_pointer_value(env->allow_ptr_leaks, cur_regs(env) + regno);
 }
 
+static bool is_ctx_reg(struct bpf_verifier_env *env, int regno)
+{
+       const struct bpf_reg_state *reg = cur_regs(env) + regno;
+
+       return reg->type == PTR_TO_CTX;
+}
+
 static int check_pkt_ptr_alignment(struct bpf_verifier_env *env,
                                   const struct bpf_reg_state *reg,
                                   int off, int size, bool strict)
@@ -1258,6 +1265,12 @@ static int check_xadd(struct bpf_verifier_env *env, int insn_idx, struct bpf_ins
                return -EACCES;
        }
 
+       if (is_ctx_reg(env, insn->dst_reg)) {
+               verbose(env, "BPF_XADD stores into R%d context is not allowed\n",
+                       insn->dst_reg);
+               return -EACCES;
+       }
+
        /* check whether atomic_add can read the memory */
        err = check_mem_access(env, insn_idx, insn->dst_reg, insn->off,
                               BPF_SIZE(insn->code), BPF_READ, -1);
@@ -3993,6 +4006,12 @@ static int do_check(struct bpf_verifier_env *env)
                        if (err)
                                return err;
 
+                       if (is_ctx_reg(env, insn->dst_reg)) {
+                               verbose(env, "BPF_ST stores into R%d context is not allowed\n",
+                                       insn->dst_reg);
+                               return -EACCES;
+                       }
+
                        /* check that memory (dst_reg + off) is writeable */
                        err = check_mem_access(env, insn_idx, insn->dst_reg, insn->off,
                                               BPF_SIZE(insn->code), BPF_WRITE,
index 6bafa54..67e7c41 100644 (file)
@@ -2592,6 +2592,29 @@ static struct bpf_test tests[] = {
                .result = ACCEPT,
                .prog_type = BPF_PROG_TYPE_SCHED_CLS,
        },
+       {
+               "context stores via ST",
+               .insns = {
+                       BPF_MOV64_IMM(BPF_REG_0, 0),
+                       BPF_ST_MEM(BPF_DW, BPF_REG_1, offsetof(struct __sk_buff, mark), 0),
+                       BPF_EXIT_INSN(),
+               },
+               .errstr = "BPF_ST stores into R1 context is not allowed",
+               .result = REJECT,
+               .prog_type = BPF_PROG_TYPE_SCHED_CLS,
+       },
+       {
+               "context stores via XADD",
+               .insns = {
+                       BPF_MOV64_IMM(BPF_REG_0, 0),
+                       BPF_RAW_INSN(BPF_STX | BPF_XADD | BPF_W, BPF_REG_1,
+                                    BPF_REG_0, offsetof(struct __sk_buff, mark), 0),
+                       BPF_EXIT_INSN(),
+               },
+               .errstr = "BPF_XADD stores into R1 context is not allowed",
+               .result = REJECT,
+               .prog_type = BPF_PROG_TYPE_SCHED_CLS,
+       },
        {
                "direct packet access: test1",
                .insns = {
@@ -4312,7 +4335,8 @@ static struct bpf_test tests[] = {
                .fixup_map1 = { 2 },
                .errstr_unpriv = "R2 leaks addr into mem",
                .result_unpriv = REJECT,
-               .result = ACCEPT,
+               .result = REJECT,
+               .errstr = "BPF_XADD stores into R1 context is not allowed",
        },
        {
                "leak pointer into ctx 2",
@@ -4326,7 +4350,8 @@ static struct bpf_test tests[] = {
                },
                .errstr_unpriv = "R10 leaks addr into mem",
                .result_unpriv = REJECT,
-               .result = ACCEPT,
+               .result = REJECT,
+               .errstr = "BPF_XADD stores into R1 context is not allowed",
        },
        {
                "leak pointer into ctx 3",