]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - kernel/bpf/verifier.c
bpf: fix mixed signed/unsigned derived min/max value bounds
[karo-tx-linux.git] / kernel / bpf / verifier.c
index 6a86723c5b64bad1534d6519f33e03e9ad4fad99..af9e84a4944e60fdd617c0c69ecbc46d53e230ac 100644 (file)
@@ -504,6 +504,7 @@ static void reset_reg_range_values(struct bpf_reg_state *regs, u32 regno)
 {
        regs[regno].min_value = BPF_REGISTER_MIN_RANGE;
        regs[regno].max_value = BPF_REGISTER_MAX_RANGE;
+       regs[regno].value_from_signed = false;
        regs[regno].min_align = 0;
 }
 
@@ -777,12 +778,13 @@ static int check_ctx_access(struct bpf_verifier_env *env, int insn_idx, int off,
        return -EACCES;
 }
 
-static bool is_pointer_value(struct bpf_verifier_env *env, int regno)
+static bool __is_pointer_value(bool allow_ptr_leaks,
+                              const struct bpf_reg_state *reg)
 {
-       if (env->allow_ptr_leaks)
+       if (allow_ptr_leaks)
                return false;
 
-       switch (env->cur_state.regs[regno].type) {
+       switch (reg->type) {
        case UNKNOWN_VALUE:
        case CONST_IMM:
                return false;
@@ -791,6 +793,11 @@ static bool is_pointer_value(struct bpf_verifier_env *env, int regno)
        }
 }
 
+static bool is_pointer_value(struct bpf_verifier_env *env, int regno)
+{
+       return __is_pointer_value(env->allow_ptr_leaks, &env->cur_state.regs[regno]);
+}
+
 static int check_pkt_ptr_alignment(const struct bpf_reg_state *reg,
                                   int off, int size, bool strict)
 {
@@ -1832,10 +1839,24 @@ static void adjust_reg_min_max_vals(struct bpf_verifier_env *env,
        dst_align = dst_reg->min_align;
 
        /* We don't know anything about what was done to this register, mark it
-        * as unknown.
+        * as unknown. Also, if both derived bounds came from signed/unsigned
+        * mixed compares and one side is unbounded, we cannot really do anything
+        * with them as boundaries cannot be trusted. Thus, arithmetic of two
+        * regs of such kind will get invalidated bounds on the dst side.
         */
-       if (min_val == BPF_REGISTER_MIN_RANGE &&
-           max_val == BPF_REGISTER_MAX_RANGE) {
+       if ((min_val == BPF_REGISTER_MIN_RANGE &&
+            max_val == BPF_REGISTER_MAX_RANGE) ||
+           (BPF_SRC(insn->code) == BPF_X &&
+            ((min_val != BPF_REGISTER_MIN_RANGE &&
+              max_val == BPF_REGISTER_MAX_RANGE) ||
+             (min_val == BPF_REGISTER_MIN_RANGE &&
+              max_val != BPF_REGISTER_MAX_RANGE) ||
+             (dst_reg->min_value != BPF_REGISTER_MIN_RANGE &&
+              dst_reg->max_value == BPF_REGISTER_MAX_RANGE) ||
+             (dst_reg->min_value == BPF_REGISTER_MIN_RANGE &&
+              dst_reg->max_value != BPF_REGISTER_MAX_RANGE)) &&
+            regs[insn->dst_reg].value_from_signed !=
+            regs[insn->src_reg].value_from_signed)) {
                reset_reg_range_values(regs, insn->dst_reg);
                return;
        }
@@ -2023,6 +2044,7 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn)
                        regs[insn->dst_reg].max_value = insn->imm;
                        regs[insn->dst_reg].min_value = insn->imm;
                        regs[insn->dst_reg].min_align = calc_align(insn->imm);
+                       regs[insn->dst_reg].value_from_signed = false;
                }
 
        } else if (opcode > BPF_END) {
@@ -2198,40 +2220,63 @@ static void reg_set_min_max(struct bpf_reg_state *true_reg,
                            struct bpf_reg_state *false_reg, u64 val,
                            u8 opcode)
 {
+       bool value_from_signed = true;
+       bool is_range = true;
+
        switch (opcode) {
        case BPF_JEQ:
                /* If this is false then we know nothing Jon Snow, but if it is
                 * true then we know for sure.
                 */
                true_reg->max_value = true_reg->min_value = val;
+               is_range = false;
                break;
        case BPF_JNE:
                /* If this is true we know nothing Jon Snow, but if it is false
                 * we know the value for sure;
                 */
                false_reg->max_value = false_reg->min_value = val;
+               is_range = false;
                break;
        case BPF_JGT:
-               /* Unsigned comparison, the minimum value is 0. */
-               false_reg->min_value = 0;
+               value_from_signed = false;
                /* fallthrough */
        case BPF_JSGT:
+               if (true_reg->value_from_signed != value_from_signed)
+                       reset_reg_range_values(true_reg, 0);
+               if (false_reg->value_from_signed != value_from_signed)
+                       reset_reg_range_values(false_reg, 0);
+               if (opcode == BPF_JGT) {
+                       /* Unsigned comparison, the minimum value is 0. */
+                       false_reg->min_value = 0;
+               }
                /* If this is false then we know the maximum val is val,
                 * otherwise we know the min val is val+1.
                 */
                false_reg->max_value = val;
+               false_reg->value_from_signed = value_from_signed;
                true_reg->min_value = val + 1;
+               true_reg->value_from_signed = value_from_signed;
                break;
        case BPF_JGE:
-               /* Unsigned comparison, the minimum value is 0. */
-               false_reg->min_value = 0;
+               value_from_signed = false;
                /* fallthrough */
        case BPF_JSGE:
+               if (true_reg->value_from_signed != value_from_signed)
+                       reset_reg_range_values(true_reg, 0);
+               if (false_reg->value_from_signed != value_from_signed)
+                       reset_reg_range_values(false_reg, 0);
+               if (opcode == BPF_JGE) {
+                       /* Unsigned comparison, the minimum value is 0. */
+                       false_reg->min_value = 0;
+               }
                /* If this is false then we know the maximum value is val - 1,
                 * otherwise we know the mimimum value is val.
                 */
                false_reg->max_value = val - 1;
+               false_reg->value_from_signed = value_from_signed;
                true_reg->min_value = val;
+               true_reg->value_from_signed = value_from_signed;
                break;
        default:
                break;
@@ -2239,6 +2284,12 @@ static void reg_set_min_max(struct bpf_reg_state *true_reg,
 
        check_reg_overflow(false_reg);
        check_reg_overflow(true_reg);
+       if (is_range) {
+               if (__is_pointer_value(false, false_reg))
+                       reset_reg_range_values(false_reg, 0);
+               if (__is_pointer_value(false, true_reg))
+                       reset_reg_range_values(true_reg, 0);
+       }
 }
 
 /* Same as above, but for the case that dst_reg is a CONST_IMM reg and src_reg
@@ -2248,41 +2299,64 @@ static void reg_set_min_max_inv(struct bpf_reg_state *true_reg,
                                struct bpf_reg_state *false_reg, u64 val,
                                u8 opcode)
 {
+       bool value_from_signed = true;
+       bool is_range = true;
+
        switch (opcode) {
        case BPF_JEQ:
                /* If this is false then we know nothing Jon Snow, but if it is
                 * true then we know for sure.
                 */
                true_reg->max_value = true_reg->min_value = val;
+               is_range = false;
                break;
        case BPF_JNE:
                /* If this is true we know nothing Jon Snow, but if it is false
                 * we know the value for sure;
                 */
                false_reg->max_value = false_reg->min_value = val;
+               is_range = false;
                break;
        case BPF_JGT:
-               /* Unsigned comparison, the minimum value is 0. */
-               true_reg->min_value = 0;
+               value_from_signed = false;
                /* fallthrough */
        case BPF_JSGT:
+               if (true_reg->value_from_signed != value_from_signed)
+                       reset_reg_range_values(true_reg, 0);
+               if (false_reg->value_from_signed != value_from_signed)
+                       reset_reg_range_values(false_reg, 0);
+               if (opcode == BPF_JGT) {
+                       /* Unsigned comparison, the minimum value is 0. */
+                       true_reg->min_value = 0;
+               }
                /*
                 * If this is false, then the val is <= the register, if it is
                 * true the register <= to the val.
                 */
                false_reg->min_value = val;
+               false_reg->value_from_signed = value_from_signed;
                true_reg->max_value = val - 1;
+               true_reg->value_from_signed = value_from_signed;
                break;
        case BPF_JGE:
-               /* Unsigned comparison, the minimum value is 0. */
-               true_reg->min_value = 0;
+               value_from_signed = false;
                /* fallthrough */
        case BPF_JSGE:
+               if (true_reg->value_from_signed != value_from_signed)
+                       reset_reg_range_values(true_reg, 0);
+               if (false_reg->value_from_signed != value_from_signed)
+                       reset_reg_range_values(false_reg, 0);
+               if (opcode == BPF_JGE) {
+                       /* Unsigned comparison, the minimum value is 0. */
+                       true_reg->min_value = 0;
+               }
                /* If this is false then constant < register, if it is true then
                 * the register < constant.
                 */
                false_reg->min_value = val + 1;
+               false_reg->value_from_signed = value_from_signed;
                true_reg->max_value = val;
+               true_reg->value_from_signed = value_from_signed;
                break;
        default:
                break;
@@ -2290,6 +2364,12 @@ static void reg_set_min_max_inv(struct bpf_reg_state *true_reg,
 
        check_reg_overflow(false_reg);
        check_reg_overflow(true_reg);
+       if (is_range) {
+               if (__is_pointer_value(false, false_reg))
+                       reset_reg_range_values(false_reg, 0);
+               if (__is_pointer_value(false, true_reg))
+                       reset_reg_range_values(true_reg, 0);
+       }
 }
 
 static void mark_map_reg(struct bpf_reg_state *regs, u32 regno, u32 id,