bpf: Guard stack limits against 32bit overflow
[platform/kernel/linux-rpi.git] / kernel / bpf / verifier.c
index a35e9b5..4759950 100644 (file)
@@ -3444,7 +3444,12 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx,
        if (class == BPF_ALU || class == BPF_ALU64) {
                if (!bt_is_reg_set(bt, dreg))
                        return 0;
-               if (opcode == BPF_MOV) {
+               if (opcode == BPF_END || opcode == BPF_NEG) {
+                       /* sreg is reserved and unused
+                        * dreg still need precision before this insn
+                        */
+                       return 0;
+               } else if (opcode == BPF_MOV) {
                        if (BPF_SRC(insn->code) == BPF_X) {
                                /* dreg = sreg or dreg = (s8, s16, s32)sreg
                                 * dreg needs precision after this insn
@@ -4325,7 +4330,7 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env,
         * so it's aligned access and [off, off + size) are within stack limits
         */
        if (!env->allow_ptr_leaks &&
-           state->stack[spi].slot_type[0] == STACK_SPILL &&
+           is_spilled_reg(&state->stack[spi]) &&
            size != BPF_REG_SIZE) {
                verbose(env, "attempt to corrupt spilled pointer on stack\n");
                return -EACCES;
@@ -4376,7 +4381,7 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env,
                   insn->imm != 0 && env->bpf_capable) {
                struct bpf_reg_state fake_reg = {};
 
-               __mark_reg_known(&fake_reg, (u32)insn->imm);
+               __mark_reg_known(&fake_reg, insn->imm);
                fake_reg.type = SCALAR_VALUE;
                save_register_state(state, spi, &fake_reg, size);
        } else if (reg && is_spillable_regtype(reg->type)) {
@@ -6366,7 +6371,7 @@ static int check_ptr_to_map_access(struct bpf_verifier_env *env,
  * The minimum valid offset is -MAX_BPF_STACK for writes, and
  * -state->allocated_stack for reads.
  */
-static int check_stack_slot_within_bounds(int off,
+static int check_stack_slot_within_bounds(s64 off,
                                          struct bpf_func_state *state,
                                          enum bpf_access_type t)
 {
@@ -6395,7 +6400,7 @@ static int check_stack_access_within_bounds(
        struct bpf_reg_state *regs = cur_regs(env);
        struct bpf_reg_state *reg = regs + regno;
        struct bpf_func_state *state = func(env, reg);
-       int min_off, max_off;
+       s64 min_off, max_off;
        int err;
        char *err_extra;
 
@@ -6408,11 +6413,8 @@ static int check_stack_access_within_bounds(
                err_extra = " write to";
 
        if (tnum_is_const(reg->var_off)) {
-               min_off = reg->var_off.value + off;
-               if (access_size > 0)
-                       max_off = min_off + access_size - 1;
-               else
-                       max_off = min_off;
+               min_off = (s64)reg->var_off.value + off;
+               max_off = min_off + access_size;
        } else {
                if (reg->smax_value >= BPF_MAX_VAR_OFF ||
                    reg->smin_value <= -BPF_MAX_VAR_OFF) {
@@ -6421,15 +6423,12 @@ static int check_stack_access_within_bounds(
                        return -EACCES;
                }
                min_off = reg->smin_value + off;
-               if (access_size > 0)
-                       max_off = reg->smax_value + off + access_size - 1;
-               else
-                       max_off = min_off;
+               max_off = reg->smax_value + off + access_size;
        }
 
        err = check_stack_slot_within_bounds(min_off, state, type);
-       if (!err)
-               err = check_stack_slot_within_bounds(max_off, state, type);
+       if (!err && max_off > 0)
+               err = -EINVAL; /* out of stack access into non-negative offsets */
 
        if (err) {
                if (tnum_is_const(reg->var_off)) {
@@ -9279,6 +9278,13 @@ static int prepare_func_exit(struct bpf_verifier_env *env, int *insn_idx)
                        verbose(env, "R0 not a scalar value\n");
                        return -EACCES;
                }
+
+               /* we are going to rely on register's precise value */
+               err = mark_reg_read(env, r0, r0->parent, REG_LIVE_READ64);
+               err = err ?: mark_chain_precision(env, BPF_REG_0);
+               if (err)
+                       return err;
+
                if (!tnum_in(range, r0->var_off)) {
                        verbose_invalid_scalar(env, r0, &range, "callback return", "R0");
                        return -EINVAL;
@@ -14751,8 +14757,7 @@ enum {
  * w - next instruction
  * e - edge
  */
-static int push_insn(int t, int w, int e, struct bpf_verifier_env *env,
-                    bool loop_ok)
+static int push_insn(int t, int w, int e, struct bpf_verifier_env *env)
 {
        int *insn_stack = env->cfg.insn_stack;
        int *insn_state = env->cfg.insn_state;
@@ -14784,7 +14789,7 @@ static int push_insn(int t, int w, int e, struct bpf_verifier_env *env,
                insn_stack[env->cfg.cur_stack++] = w;
                return KEEP_EXPLORING;
        } else if ((insn_state[w] & 0xF0) == DISCOVERED) {
-               if (loop_ok && env->bpf_capable)
+               if (env->bpf_capable)
                        return DONE_EXPLORING;
                verbose_linfo(env, t, "%d: ", t);
                verbose_linfo(env, w, "%d: ", w);
@@ -14807,7 +14812,7 @@ static int visit_func_call_insn(int t, struct bpf_insn *insns,
        int ret, insn_sz;
 
        insn_sz = bpf_is_ldimm64(&insns[t]) ? 2 : 1;
-       ret = push_insn(t, t + insn_sz, FALLTHROUGH, env, false);
+       ret = push_insn(t, t + insn_sz, FALLTHROUGH, env);
        if (ret)
                return ret;
 
@@ -14817,12 +14822,7 @@ static int visit_func_call_insn(int t, struct bpf_insn *insns,
 
        if (visit_callee) {
                mark_prune_point(env, t);
-               ret = push_insn(t, t + insns[t].imm + 1, BRANCH, env,
-                               /* It's ok to allow recursion from CFG point of
-                                * view. __check_func_call() will do the actual
-                                * check.
-                                */
-                               bpf_pseudo_func(insns + t));
+               ret = push_insn(t, t + insns[t].imm + 1, BRANCH, env);
        }
        return ret;
 }
@@ -14844,7 +14844,7 @@ static int visit_insn(int t, struct bpf_verifier_env *env)
        if (BPF_CLASS(insn->code) != BPF_JMP &&
            BPF_CLASS(insn->code) != BPF_JMP32) {
                insn_sz = bpf_is_ldimm64(insn) ? 2 : 1;
-               return push_insn(t, t + insn_sz, FALLTHROUGH, env, false);
+               return push_insn(t, t + insn_sz, FALLTHROUGH, env);
        }
 
        switch (BPF_OP(insn->code)) {
@@ -14891,8 +14891,7 @@ static int visit_insn(int t, struct bpf_verifier_env *env)
                        off = insn->imm;
 
                /* unconditional jump with single edge */
-               ret = push_insn(t, t + off + 1, FALLTHROUGH, env,
-                               true);
+               ret = push_insn(t, t + off + 1, FALLTHROUGH, env);
                if (ret)
                        return ret;
 
@@ -14905,11 +14904,11 @@ static int visit_insn(int t, struct bpf_verifier_env *env)
                /* conditional jump with two edges */
                mark_prune_point(env, t);
 
-               ret = push_insn(t, t + 1, FALLTHROUGH, env, true);
+               ret = push_insn(t, t + 1, FALLTHROUGH, env);
                if (ret)
                        return ret;
 
-               return push_insn(t, t + insn->off + 1, BRANCH, env, true);
+               return push_insn(t, t + insn->off + 1, BRANCH, env);
        }
 }