]> git.proxmox.com Git - mirror_ubuntu-kernels.git/commitdiff
bpf: fix matching of data/data_end in verifier
authorAlexei Starovoitov <ast@fb.com>
Thu, 16 Jun 2016 01:25:38 +0000 (18:25 -0700)
committerDavid S. Miller <davem@davemloft.net>
Thu, 16 Jun 2016 06:37:54 +0000 (23:37 -0700)
The ctx structure passed into bpf programs is different depending on bpf
program type. The verifier incorrectly marked ctx->data and ctx->data_end
access based on ctx offset only. That caused loads in tracing programs
int bpf_prog(struct pt_regs *ctx) { .. ctx->ax .. }
to be incorrectly marked as PTR_TO_PACKET which later caused verifier
to reject the program that was actually valid in tracing context.
Fix this by doing program type specific matching of ctx offsets.

Fixes: 969bf05eb3ce ("bpf: direct packet access")
Reported-by: Sasha Goldshtein <goldshtn@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>
include/linux/bpf.h
kernel/bpf/verifier.c
kernel/trace/bpf_trace.c
net/core/filter.c

index 8ee27b8afe81c5d45b66f7b629c976ed8138d26a..8269cafc6eb18929dd373f7031a1fd419c652bb0 100644 (file)
@@ -111,6 +111,31 @@ enum bpf_access_type {
        BPF_WRITE = 2
 };
 
+/* types of values stored in eBPF registers */
+enum bpf_reg_type {
+       NOT_INIT = 0,            /* nothing was written into register */
+       UNKNOWN_VALUE,           /* reg doesn't contain a valid pointer */
+       PTR_TO_CTX,              /* reg points to bpf_context */
+       CONST_PTR_TO_MAP,        /* reg points to struct bpf_map */
+       PTR_TO_MAP_VALUE,        /* reg points to map element value */
+       PTR_TO_MAP_VALUE_OR_NULL,/* points to map elem value or NULL */
+       FRAME_PTR,               /* reg == frame_pointer */
+       PTR_TO_STACK,            /* reg == frame_pointer + imm */
+       CONST_IMM,               /* constant integer value */
+
+       /* PTR_TO_PACKET represents:
+        * skb->data
+        * skb->data + imm
+        * skb->data + (u16) var
+        * skb->data + (u16) var + imm
+        * if (range > 0) then [ptr, ptr + range - off) is safe to access
+        * if (id > 0) means that some 'var' was added
+        * if (off > 0) menas that 'imm' was added
+        */
+       PTR_TO_PACKET,
+       PTR_TO_PACKET_END,       /* skb->data + headlen */
+};
+
 struct bpf_prog;
 
 struct bpf_verifier_ops {
@@ -120,7 +145,8 @@ struct bpf_verifier_ops {
        /* return true if 'size' wide access at offset 'off' within bpf_context
         * with 'type' (read or write) is allowed
         */
-       bool (*is_valid_access)(int off, int size, enum bpf_access_type type);
+       bool (*is_valid_access)(int off, int size, enum bpf_access_type type,
+                               enum bpf_reg_type *reg_type);
 
        u32 (*convert_ctx_access)(enum bpf_access_type type, int dst_reg,
                                  int src_reg, int ctx_off,
index 668e07903c8f1a3950c4e494eb7443748710f08c..eec9f90ba030410a5104991cfcd377400cb4bb7d 100644 (file)
  * are set to NOT_INIT to indicate that they are no longer readable.
  */
 
-/* types of values stored in eBPF registers */
-enum bpf_reg_type {
-       NOT_INIT = 0,            /* nothing was written into register */
-       UNKNOWN_VALUE,           /* reg doesn't contain a valid pointer */
-       PTR_TO_CTX,              /* reg points to bpf_context */
-       CONST_PTR_TO_MAP,        /* reg points to struct bpf_map */
-       PTR_TO_MAP_VALUE,        /* reg points to map element value */
-       PTR_TO_MAP_VALUE_OR_NULL,/* points to map elem value or NULL */
-       FRAME_PTR,               /* reg == frame_pointer */
-       PTR_TO_STACK,            /* reg == frame_pointer + imm */
-       CONST_IMM,               /* constant integer value */
-
-       /* PTR_TO_PACKET represents:
-        * skb->data
-        * skb->data + imm
-        * skb->data + (u16) var
-        * skb->data + (u16) var + imm
-        * if (range > 0) then [ptr, ptr + range - off) is safe to access
-        * if (id > 0) means that some 'var' was added
-        * if (off > 0) menas that 'imm' was added
-        */
-       PTR_TO_PACKET,
-       PTR_TO_PACKET_END,       /* skb->data + headlen */
-};
-
 struct reg_state {
        enum bpf_reg_type type;
        union {
@@ -695,10 +670,10 @@ static int check_packet_access(struct verifier_env *env, u32 regno, int off,
 
 /* check access to 'struct bpf_context' fields */
 static int check_ctx_access(struct verifier_env *env, int off, int size,
-                           enum bpf_access_type t)
+                           enum bpf_access_type t, enum bpf_reg_type *reg_type)
 {
        if (env->prog->aux->ops->is_valid_access &&
-           env->prog->aux->ops->is_valid_access(off, size, t)) {
+           env->prog->aux->ops->is_valid_access(off, size, t, reg_type)) {
                /* remember the offset of last byte accessed in ctx */
                if (env->prog->aux->max_ctx_offset < off + size)
                        env->prog->aux->max_ctx_offset = off + size;
@@ -798,21 +773,19 @@ static int check_mem_access(struct verifier_env *env, u32 regno, int off,
                        mark_reg_unknown_value(state->regs, value_regno);
 
        } else if (reg->type == PTR_TO_CTX) {
+               enum bpf_reg_type reg_type = UNKNOWN_VALUE;
+
                if (t == BPF_WRITE && value_regno >= 0 &&
                    is_pointer_value(env, value_regno)) {
                        verbose("R%d leaks addr into ctx\n", value_regno);
                        return -EACCES;
                }
-               err = check_ctx_access(env, off, size, t);
+               err = check_ctx_access(env, off, size, t, &reg_type);
                if (!err && t == BPF_READ && value_regno >= 0) {
                        mark_reg_unknown_value(state->regs, value_regno);
-                       if (off == offsetof(struct __sk_buff, data) &&
-                           env->allow_ptr_leaks)
+                       if (env->allow_ptr_leaks)
                                /* note that reg.[id|off|range] == 0 */
-                               state->regs[value_regno].type = PTR_TO_PACKET;
-                       else if (off == offsetof(struct __sk_buff, data_end) &&
-                                env->allow_ptr_leaks)
-                               state->regs[value_regno].type = PTR_TO_PACKET_END;
+                               state->regs[value_regno].type = reg_type;
                }
 
        } else if (reg->type == FRAME_PTR || reg->type == PTR_TO_STACK) {
index 720b7bb01d43ae5f49761da3a34d8fce4e55dbc2..e7af6cb9d5cf580b402e8211e493865f96929e9c 100644 (file)
@@ -349,7 +349,8 @@ static const struct bpf_func_proto *kprobe_prog_func_proto(enum bpf_func_id func
 }
 
 /* bpf+kprobe programs can access fields of 'struct pt_regs' */
-static bool kprobe_prog_is_valid_access(int off, int size, enum bpf_access_type type)
+static bool kprobe_prog_is_valid_access(int off, int size, enum bpf_access_type type,
+                                       enum bpf_reg_type *reg_type)
 {
        /* check bounds */
        if (off < 0 || off >= sizeof(struct pt_regs))
@@ -427,7 +428,8 @@ static const struct bpf_func_proto *tp_prog_func_proto(enum bpf_func_id func_id)
        }
 }
 
-static bool tp_prog_is_valid_access(int off, int size, enum bpf_access_type type)
+static bool tp_prog_is_valid_access(int off, int size, enum bpf_access_type type,
+                                   enum bpf_reg_type *reg_type)
 {
        if (off < sizeof(void *) || off >= PERF_MAX_TRACE_SIZE)
                return false;
index 68adb5f52110d85fead496a8a76a7248ae8cefae..c4b330c85c02d6bfc1b1ce12dafb42d10b264584 100644 (file)
@@ -2085,7 +2085,8 @@ static bool __is_valid_access(int off, int size, enum bpf_access_type type)
 }
 
 static bool sk_filter_is_valid_access(int off, int size,
-                                     enum bpf_access_type type)
+                                     enum bpf_access_type type,
+                                     enum bpf_reg_type *reg_type)
 {
        switch (off) {
        case offsetof(struct __sk_buff, tc_classid):
@@ -2108,7 +2109,8 @@ static bool sk_filter_is_valid_access(int off, int size,
 }
 
 static bool tc_cls_act_is_valid_access(int off, int size,
-                                      enum bpf_access_type type)
+                                      enum bpf_access_type type,
+                                      enum bpf_reg_type *reg_type)
 {
        if (type == BPF_WRITE) {
                switch (off) {
@@ -2123,6 +2125,16 @@ static bool tc_cls_act_is_valid_access(int off, int size,
                        return false;
                }
        }
+
+       switch (off) {
+       case offsetof(struct __sk_buff, data):
+               *reg_type = PTR_TO_PACKET;
+               break;
+       case offsetof(struct __sk_buff, data_end):
+               *reg_type = PTR_TO_PACKET_END;
+               break;
+       }
+
        return __is_valid_access(off, size, type);
 }