]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - kernel/bpf/verifier.c
bpf: perf event change needed for subsequent bpf helpers
[mirror_ubuntu-bionic-kernel.git] / kernel / bpf / verifier.c
index 799b2451ef2df42b17aadfe679a09dee9a0848c9..52b022310f6ac91a16deb0593e794e5122561956 100644 (file)
@@ -177,6 +177,12 @@ static __printf(1, 2) void verbose(const char *fmt, ...)
        va_end(args);
 }
 
+static bool type_is_pkt_pointer(enum bpf_reg_type type)
+{
+       return type == PTR_TO_PACKET ||
+              type == PTR_TO_PACKET_META;
+}
+
 /* string representation of 'enum bpf_reg_type' */
 static const char * const reg_type_str[] = {
        [NOT_INIT]              = "?",
@@ -187,6 +193,7 @@ static const char * const reg_type_str[] = {
        [PTR_TO_MAP_VALUE_OR_NULL] = "map_value_or_null",
        [PTR_TO_STACK]          = "fp",
        [PTR_TO_PACKET]         = "pkt",
+       [PTR_TO_PACKET_META]    = "pkt_meta",
        [PTR_TO_PACKET_END]     = "pkt_end",
 };
 
@@ -226,7 +233,7 @@ static void print_verifier_state(struct bpf_verifier_state *state)
                        verbose("(id=%d", reg->id);
                        if (t != SCALAR_VALUE)
                                verbose(",off=%d", reg->off);
-                       if (t == PTR_TO_PACKET)
+                       if (type_is_pkt_pointer(t))
                                verbose(",r=%d", reg->range);
                        else if (t == CONST_PTR_TO_MAP ||
                                 t == PTR_TO_MAP_VALUE ||
@@ -325,26 +332,45 @@ static const char *const bpf_jmp_string[16] = {
        [BPF_EXIT >> 4] = "exit",
 };
 
+static void print_bpf_end_insn(const struct bpf_verifier_env *env,
+                              const struct bpf_insn *insn)
+{
+       verbose("(%02x) r%d = %s%d r%d\n", insn->code, insn->dst_reg,
+               BPF_SRC(insn->code) == BPF_TO_BE ? "be" : "le",
+               insn->imm, insn->dst_reg);
+}
+
 static void print_bpf_insn(const struct bpf_verifier_env *env,
                           const struct bpf_insn *insn)
 {
        u8 class = BPF_CLASS(insn->code);
 
        if (class == BPF_ALU || class == BPF_ALU64) {
-               if (BPF_SRC(insn->code) == BPF_X)
+               if (BPF_OP(insn->code) == BPF_END) {
+                       if (class == BPF_ALU64)
+                               verbose("BUG_alu64_%02x\n", insn->code);
+                       else
+                               print_bpf_end_insn(env, insn);
+               } else if (BPF_OP(insn->code) == BPF_NEG) {
+                       verbose("(%02x) r%d = %s-r%d\n",
+                               insn->code, insn->dst_reg,
+                               class == BPF_ALU ? "(u32) " : "",
+                               insn->dst_reg);
+               } else if (BPF_SRC(insn->code) == BPF_X) {
                        verbose("(%02x) %sr%d %s %sr%d\n",
                                insn->code, class == BPF_ALU ? "(u32) " : "",
                                insn->dst_reg,
                                bpf_alu_string[BPF_OP(insn->code) >> 4],
                                class == BPF_ALU ? "(u32) " : "",
                                insn->src_reg);
-               else
+               } else {
                        verbose("(%02x) %sr%d %s %s%d\n",
                                insn->code, class == BPF_ALU ? "(u32) " : "",
                                insn->dst_reg,
                                bpf_alu_string[BPF_OP(insn->code) >> 4],
                                class == BPF_ALU ? "(u32) " : "",
                                insn->imm);
+               }
        } else if (class == BPF_STX) {
                if (BPF_MODE(insn->code) == BPF_MEM)
                        verbose("(%02x) *(%s *)(r%d %+d) = r%d\n",
@@ -519,6 +545,31 @@ static void mark_reg_known_zero(struct bpf_reg_state *regs, u32 regno)
        __mark_reg_known_zero(regs + regno);
 }
 
+static bool reg_is_pkt_pointer(const struct bpf_reg_state *reg)
+{
+       return type_is_pkt_pointer(reg->type);
+}
+
+static bool reg_is_pkt_pointer_any(const struct bpf_reg_state *reg)
+{
+       return reg_is_pkt_pointer(reg) ||
+              reg->type == PTR_TO_PACKET_END;
+}
+
+/* Unmodified PTR_TO_PACKET[_META,_END] register from ctx access. */
+static bool reg_is_init_pkt_pointer(const struct bpf_reg_state *reg,
+                                   enum bpf_reg_type which)
+{
+       /* The register can already have a range from prior markings.
+        * This is fine as long as it hasn't been advanced from its
+        * origin.
+        */
+       return reg->type == which &&
+              reg->id == 0 &&
+              reg->off == 0 &&
+              tnum_equals_const(reg->var_off, 0);
+}
+
 /* Attempts to improve min/max values based on var_off information */
 static void __update_reg_bounds(struct bpf_reg_state *reg)
 {
@@ -702,6 +753,7 @@ static bool is_spillable_regtype(enum bpf_reg_type type)
        case PTR_TO_STACK:
        case PTR_TO_CTX:
        case PTR_TO_PACKET:
+       case PTR_TO_PACKET_META:
        case PTR_TO_PACKET_END:
        case CONST_PTR_TO_MAP:
                return true;
@@ -1047,7 +1099,10 @@ static int check_ptr_alignment(struct bpf_verifier_env *env,
 
        switch (reg->type) {
        case PTR_TO_PACKET:
-               /* special case, because of NET_IP_ALIGN */
+       case PTR_TO_PACKET_META:
+               /* Special case, because of NET_IP_ALIGN. Given metadata sits
+                * right in front, treat it the very same way.
+                */
                return check_pkt_ptr_alignment(reg, off, size, strict);
        case PTR_TO_MAP_VALUE:
                pointer_desc = "value ";
@@ -1124,8 +1179,8 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn
                err = check_ctx_access(env, insn_idx, off, size, t, &reg_type);
                if (!err && t == BPF_READ && value_regno >= 0) {
                        /* ctx access returns either a scalar, or a
-                        * PTR_TO_PACKET[_END].  In the latter case, we know
-                        * the offset is zero.
+                        * PTR_TO_PACKET[_META,_END]. In the latter
+                        * case, we know the offset is zero.
                         */
                        if (reg_type == SCALAR_VALUE)
                                mark_reg_unknown(state->regs, value_regno);
@@ -1170,7 +1225,7 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn
                } else {
                        err = check_stack_read(state, off, size, value_regno);
                }
-       } else if (reg->type == PTR_TO_PACKET) {
+       } else if (reg_is_pkt_pointer(reg)) {
                if (t == BPF_WRITE && !may_access_direct_pkt_data(env, NULL, t)) {
                        verbose("cannot write into packet\n");
                        return -EACCES;
@@ -1310,6 +1365,7 @@ static int check_helper_mem_access(struct bpf_verifier_env *env, int regno,
 
        switch (reg->type) {
        case PTR_TO_PACKET:
+       case PTR_TO_PACKET_META:
                return check_packet_access(env, regno, reg->off, access_size);
        case PTR_TO_MAP_VALUE:
                return check_map_access(env, regno, reg->off, access_size);
@@ -1342,7 +1398,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 regno,
                return 0;
        }
 
-       if (type == PTR_TO_PACKET &&
+       if (type_is_pkt_pointer(type) &&
            !may_access_direct_pkt_data(env, meta, BPF_READ)) {
                verbose("helper access to the packet is not allowed\n");
                return -EACCES;
@@ -1351,7 +1407,8 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 regno,
        if (arg_type == ARG_PTR_TO_MAP_KEY ||
            arg_type == ARG_PTR_TO_MAP_VALUE) {
                expected_type = PTR_TO_STACK;
-               if (type != PTR_TO_PACKET && type != expected_type)
+               if (!type_is_pkt_pointer(type) &&
+                   type != expected_type)
                        goto err_type;
        } else if (arg_type == ARG_CONST_SIZE ||
                   arg_type == ARG_CONST_SIZE_OR_ZERO) {
@@ -1375,7 +1432,8 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 regno,
                 */
                if (register_is_null(*reg))
                        /* final test in check_stack_boundary() */;
-               else if (type != PTR_TO_PACKET && type != PTR_TO_MAP_VALUE &&
+               else if (!type_is_pkt_pointer(type) &&
+                        type != PTR_TO_MAP_VALUE &&
                         type != expected_type)
                        goto err_type;
                meta->raw_mode = arg_type == ARG_PTR_TO_UNINIT_MEM;
@@ -1401,7 +1459,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 regno,
                        verbose("invalid map_ptr to access map->key\n");
                        return -EACCES;
                }
-               if (type == PTR_TO_PACKET)
+               if (type_is_pkt_pointer(type))
                        err = check_packet_access(env, regno, reg->off,
                                                  meta->map_ptr->key_size);
                else
@@ -1417,7 +1475,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 regno,
                        verbose("invalid map_ptr to access map->value\n");
                        return -EACCES;
                }
-               if (type == PTR_TO_PACKET)
+               if (type_is_pkt_pointer(type))
                        err = check_packet_access(env, regno, reg->off,
                                                  meta->map_ptr->value_size);
                else
@@ -1590,8 +1648,8 @@ static int check_raw_mode(const struct bpf_func_proto *fn)
        return count > 1 ? -EINVAL : 0;
 }
 
-/* Packet data might have moved, any old PTR_TO_PACKET[_END] are now invalid,
- * so turn them into unknown SCALAR_VALUE.
+/* Packet data might have moved, any old PTR_TO_PACKET[_META,_END]
+ * are now invalid, so turn them into unknown SCALAR_VALUE.
  */
 static void clear_all_pkt_pointers(struct bpf_verifier_env *env)
 {
@@ -1600,18 +1658,15 @@ static void clear_all_pkt_pointers(struct bpf_verifier_env *env)
        int i;
 
        for (i = 0; i < MAX_BPF_REG; i++)
-               if (regs[i].type == PTR_TO_PACKET ||
-                   regs[i].type == PTR_TO_PACKET_END)
+               if (reg_is_pkt_pointer_any(&regs[i]))
                        mark_reg_unknown(regs, i);
 
        for (i = 0; i < MAX_BPF_STACK; i += BPF_REG_SIZE) {
                if (state->stack_slot_type[i] != STACK_SPILL)
                        continue;
                reg = &state->spilled_regs[i / BPF_REG_SIZE];
-               if (reg->type != PTR_TO_PACKET &&
-                   reg->type != PTR_TO_PACKET_END)
-                       continue;
-               __mark_reg_unknown(reg);
+               if (reg_is_pkt_pointer_any(reg))
+                       __mark_reg_unknown(reg);
        }
 }
 
@@ -1871,7 +1926,7 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env,
                }
                dst_reg->var_off = tnum_add(ptr_reg->var_off, off_reg->var_off);
                dst_reg->off = ptr_reg->off;
-               if (ptr_reg->type == PTR_TO_PACKET) {
+               if (reg_is_pkt_pointer(ptr_reg)) {
                        dst_reg->id = ++env->id_gen;
                        /* something was added to pkt_ptr, set range to zero */
                        dst_reg->range = 0;
@@ -1931,7 +1986,7 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env,
                }
                dst_reg->var_off = tnum_sub(ptr_reg->var_off, off_reg->var_off);
                dst_reg->off = ptr_reg->off;
-               if (ptr_reg->type == PTR_TO_PACKET) {
+               if (reg_is_pkt_pointer(ptr_reg)) {
                        dst_reg->id = ++env->id_gen;
                        /* something was added to pkt_ptr, set range to zero */
                        if (smin_val < 0)
@@ -2421,7 +2476,8 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn)
 }
 
 static void find_good_pkt_pointers(struct bpf_verifier_state *state,
-                                  struct bpf_reg_state *dst_reg)
+                                  struct bpf_reg_state *dst_reg,
+                                  enum bpf_reg_type type)
 {
        struct bpf_reg_state *regs = state->regs, *reg;
        int i;
@@ -2483,7 +2539,7 @@ static void find_good_pkt_pointers(struct bpf_verifier_state *state,
         * dst_reg->off is known < MAX_PACKET_OFF, therefore it fits in a u16.
         */
        for (i = 0; i < MAX_BPF_REG; i++)
-               if (regs[i].type == PTR_TO_PACKET && regs[i].id == dst_reg->id)
+               if (regs[i].type == type && regs[i].id == dst_reg->id)
                        /* keep the maximum range already checked */
                        regs[i].range = max_t(u16, regs[i].range, dst_reg->off);
 
@@ -2491,7 +2547,7 @@ static void find_good_pkt_pointers(struct bpf_verifier_state *state,
                if (state->stack_slot_type[i] != STACK_SPILL)
                        continue;
                reg = &state->spilled_regs[i / BPF_REG_SIZE];
-               if (reg->type == PTR_TO_PACKET && reg->id == dst_reg->id)
+               if (reg->type == type && reg->id == dst_reg->id)
                        reg->range = max_t(u16, reg->range, dst_reg->off);
        }
 }
@@ -2856,19 +2912,39 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env,
        } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JGT &&
                   dst_reg->type == PTR_TO_PACKET &&
                   regs[insn->src_reg].type == PTR_TO_PACKET_END) {
-               find_good_pkt_pointers(this_branch, dst_reg);
+               find_good_pkt_pointers(this_branch, dst_reg, PTR_TO_PACKET);
        } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JLT &&
                   dst_reg->type == PTR_TO_PACKET &&
                   regs[insn->src_reg].type == PTR_TO_PACKET_END) {
-               find_good_pkt_pointers(other_branch, dst_reg);
+               find_good_pkt_pointers(other_branch, dst_reg, PTR_TO_PACKET);
        } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JGE &&
                   dst_reg->type == PTR_TO_PACKET_END &&
                   regs[insn->src_reg].type == PTR_TO_PACKET) {
-               find_good_pkt_pointers(other_branch, &regs[insn->src_reg]);
+               find_good_pkt_pointers(other_branch, &regs[insn->src_reg],
+                                      PTR_TO_PACKET);
        } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JLE &&
                   dst_reg->type == PTR_TO_PACKET_END &&
                   regs[insn->src_reg].type == PTR_TO_PACKET) {
-               find_good_pkt_pointers(this_branch, &regs[insn->src_reg]);
+               find_good_pkt_pointers(this_branch, &regs[insn->src_reg],
+                                      PTR_TO_PACKET);
+       } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JGT &&
+                  dst_reg->type == PTR_TO_PACKET_META &&
+                  reg_is_init_pkt_pointer(&regs[insn->src_reg], PTR_TO_PACKET)) {
+               find_good_pkt_pointers(this_branch, dst_reg, PTR_TO_PACKET_META);
+       } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JLT &&
+                  dst_reg->type == PTR_TO_PACKET_META &&
+                  reg_is_init_pkt_pointer(&regs[insn->src_reg], PTR_TO_PACKET)) {
+               find_good_pkt_pointers(other_branch, dst_reg, PTR_TO_PACKET_META);
+       } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JGE &&
+                  reg_is_init_pkt_pointer(dst_reg, PTR_TO_PACKET) &&
+                  regs[insn->src_reg].type == PTR_TO_PACKET_META) {
+               find_good_pkt_pointers(other_branch, &regs[insn->src_reg],
+                                      PTR_TO_PACKET_META);
+       } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JLE &&
+                  reg_is_init_pkt_pointer(dst_reg, PTR_TO_PACKET) &&
+                  regs[insn->src_reg].type == PTR_TO_PACKET_META) {
+               find_good_pkt_pointers(this_branch, &regs[insn->src_reg],
+                                      PTR_TO_PACKET_META);
        } else if (is_pointer_value(env, insn->dst_reg)) {
                verbose("R%d pointer comparison prohibited\n", insn->dst_reg);
                return -EACCES;
@@ -2997,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
@@ -3298,8 +3411,9 @@ static bool regsafe(struct bpf_reg_state *rold, struct bpf_reg_state *rcur,
                        return false;
                /* Check our ids match any regs they're supposed to */
                return check_ids(rold->id, rcur->id, idmap);
+       case PTR_TO_PACKET_META:
        case PTR_TO_PACKET:
-               if (rcur->type != PTR_TO_PACKET)
+               if (rcur->type != rold->type)
                        return false;
                /* We must have at least as much range as the old ptr
                 * did, so that any accesses which were safe before are
@@ -3786,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) {
@@ -4205,7 +4322,12 @@ static int fixup_bpf_calls(struct bpf_verifier_env *env)
                }
 
                if (insn->imm == BPF_FUNC_redirect_map) {
-                       u64 addr = (unsigned long)prog;
+                       /* Note, we cannot use prog directly as imm as subsequent
+                        * rewrites would still change the prog pointer. The only
+                        * stable address we can use is aux, which also works with
+                        * prog clones during blinding.
+                        */
+                       u64 addr = (unsigned long)prog->aux;
                        struct bpf_insn r4_ld[] = {
                                BPF_LD_IMM64(BPF_REG_4, addr),
                                *insn,