]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/blobdiff - kernel/bpf/verifier.c
bpf: No need to simulate speculative domain for immediates
[mirror_ubuntu-hirsute-kernel.git] / kernel / bpf / verifier.c
index 3dd297203ab5cfda8493ddb9b4a06dd66aa74460..63d761cdd6782198eb47f77c81597767ab7a57c9 100644 (file)
@@ -5721,18 +5721,10 @@ enum {
 };
 
 static int retrieve_ptr_limit(const struct bpf_reg_state *ptr_reg,
-                             const struct bpf_reg_state *off_reg,
-                             u32 *alu_limit, u8 opcode)
+                             u32 *alu_limit, bool mask_to_left)
 {
-       bool off_is_neg = off_reg->smin_value < 0;
-       bool mask_to_left = (opcode == BPF_ADD &&  off_is_neg) ||
-                           (opcode == BPF_SUB && !off_is_neg);
        u32 max = 0, ptr_limit = 0;
 
-       if (!tnum_is_const(off_reg->var_off) &&
-           (off_reg->smin_value < 0) != (off_reg->smax_value < 0))
-               return REASON_BOUNDS;
-
        switch (ptr_reg->type) {
        case PTR_TO_STACK:
                /* Offset 0 is out-of-bounds, but acceptable start for the
@@ -5798,15 +5790,20 @@ static bool sanitize_needed(u8 opcode)
        return opcode == BPF_ADD || opcode == BPF_SUB;
 }
 
+struct bpf_sanitize_info {
+       struct bpf_insn_aux_data aux;
+       bool mask_to_left;
+};
+
 static int sanitize_ptr_alu(struct bpf_verifier_env *env,
                            struct bpf_insn *insn,
                            const struct bpf_reg_state *ptr_reg,
                            const struct bpf_reg_state *off_reg,
                            struct bpf_reg_state *dst_reg,
-                           struct bpf_insn_aux_data *tmp_aux,
+                           struct bpf_sanitize_info *info,
                            const bool commit_window)
 {
-       struct bpf_insn_aux_data *aux = commit_window ? cur_aux(env) : tmp_aux;
+       struct bpf_insn_aux_data *aux = commit_window ? cur_aux(env) : &info->aux;
        struct bpf_verifier_state *vstate = env->cur_state;
        bool off_is_imm = tnum_is_const(off_reg->var_off);
        bool off_is_neg = off_reg->smin_value < 0;
@@ -5827,7 +5824,16 @@ static int sanitize_ptr_alu(struct bpf_verifier_env *env,
        if (vstate->speculative)
                goto do_sim;
 
-       err = retrieve_ptr_limit(ptr_reg, off_reg, &alu_limit, opcode);
+       if (!commit_window) {
+               if (!tnum_is_const(off_reg->var_off) &&
+                   (off_reg->smin_value < 0) != (off_reg->smax_value < 0))
+                       return REASON_BOUNDS;
+
+               info->mask_to_left = (opcode == BPF_ADD &&  off_is_neg) ||
+                                    (opcode == BPF_SUB && !off_is_neg);
+       }
+
+       err = retrieve_ptr_limit(ptr_reg, &alu_limit, info->mask_to_left);
        if (err < 0)
                return err;
 
@@ -5835,8 +5841,8 @@ static int sanitize_ptr_alu(struct bpf_verifier_env *env,
                /* In commit phase we narrow the masking window based on
                 * the observed pointer move after the simulated operation.
                 */
-               alu_state = tmp_aux->alu_state;
-               alu_limit = abs(tmp_aux->alu_limit - alu_limit);
+               alu_state = info->aux.alu_state;
+               alu_limit = abs(info->aux.alu_limit - alu_limit);
        } else {
                alu_state  = off_is_neg ? BPF_ALU_NEG_VALUE : 0;
                alu_state |= off_is_imm ? BPF_ALU_IMMEDIATE : 0;
@@ -5851,8 +5857,12 @@ do_sim:
        /* If we're in commit phase, we're done here given we already
         * pushed the truncated dst_reg into the speculative verification
         * stack.
+        *
+        * Also, when register is a known constant, we rewrite register-based
+        * operation to immediate-based, and thus do not need masking (and as
+        * a consequence, do not need to simulate the zero-truncation either).
         */
-       if (commit_window)
+       if (commit_window || off_is_imm)
                return 0;
 
        /* Simulate and find potential out-of-bounds access under
@@ -5997,7 +6007,7 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env,
            smin_ptr = ptr_reg->smin_value, smax_ptr = ptr_reg->smax_value;
        u64 umin_val = off_reg->umin_value, umax_val = off_reg->umax_value,
            umin_ptr = ptr_reg->umin_value, umax_ptr = ptr_reg->umax_value;
-       struct bpf_insn_aux_data tmp_aux = {};
+       struct bpf_sanitize_info info = {};
        u8 opcode = BPF_OP(insn->code);
        u32 dst = insn->dst_reg;
        int ret;
@@ -6066,7 +6076,7 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env,
 
        if (sanitize_needed(opcode)) {
                ret = sanitize_ptr_alu(env, insn, ptr_reg, off_reg, dst_reg,
-                                      &tmp_aux, false);
+                                      &info, false);
                if (ret < 0)
                        return sanitize_err(env, insn, ret, off_reg, dst_reg);
        }
@@ -6207,7 +6217,7 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env,
                return -EACCES;
        if (sanitize_needed(opcode)) {
                ret = sanitize_ptr_alu(env, insn, dst_reg, off_reg, dst_reg,
-                                      &tmp_aux, true);
+                                      &info, true);
                if (ret < 0)
                        return sanitize_err(env, insn, ret, off_reg, dst_reg);
        }
@@ -6396,11 +6406,10 @@ static void scalar32_min_max_and(struct bpf_reg_state *dst_reg,
        s32 smin_val = src_reg->s32_min_value;
        u32 umax_val = src_reg->u32_max_value;
 
-       /* Assuming scalar64_min_max_and will be called so its safe
-        * to skip updating register for known 32-bit case.
-        */
-       if (src_known && dst_known)
+       if (src_known && dst_known) {
+               __mark_reg32_known(dst_reg, var32_off.value);
                return;
+       }
 
        /* We get our minimum from the var_off, since that's inherently
         * bitwise.  Our maximum is the minimum of the operands' maxima.
@@ -6420,7 +6429,6 @@ static void scalar32_min_max_and(struct bpf_reg_state *dst_reg,
                dst_reg->s32_min_value = dst_reg->u32_min_value;
                dst_reg->s32_max_value = dst_reg->u32_max_value;
        }
-
 }
 
 static void scalar_min_max_and(struct bpf_reg_state *dst_reg,
@@ -6467,11 +6475,10 @@ static void scalar32_min_max_or(struct bpf_reg_state *dst_reg,
        s32 smin_val = src_reg->s32_min_value;
        u32 umin_val = src_reg->u32_min_value;
 
-       /* Assuming scalar64_min_max_or will be called so it is safe
-        * to skip updating register for known case.
-        */
-       if (src_known && dst_known)
+       if (src_known && dst_known) {
+               __mark_reg32_known(dst_reg, var32_off.value);
                return;
+       }
 
        /* We get our maximum from the var_off, and our minimum is the
         * maximum of the operands' minima
@@ -6536,11 +6543,10 @@ static void scalar32_min_max_xor(struct bpf_reg_state *dst_reg,
        struct tnum var32_off = tnum_subreg(dst_reg->var_off);
        s32 smin_val = src_reg->s32_min_value;
 
-       /* Assuming scalar64_min_max_xor will be called so it is safe
-        * to skip updating register for known case.
-        */
-       if (src_known && dst_known)
+       if (src_known && dst_known) {
+               __mark_reg32_known(dst_reg, var32_off.value);
                return;
+       }
 
        /* We get both minimum and maximum from the var32_off. */
        dst_reg->u32_min_value = var32_off.value;