aboutsummaryrefslogtreecommitdiffstats
path: root/common/recipes-kernel/linux/linux-yocto-4.9.21/0093-bpf-fix-mixed-signed-unsigned-derived-min-max-value-.patch
diff options
context:
space:
mode:
Diffstat (limited to 'common/recipes-kernel/linux/linux-yocto-4.9.21/0093-bpf-fix-mixed-signed-unsigned-derived-min-max-value-.patch')
-rw-r--r--common/recipes-kernel/linux/linux-yocto-4.9.21/0093-bpf-fix-mixed-signed-unsigned-derived-min-max-value-.patch463
1 files changed, 463 insertions, 0 deletions
diff --git a/common/recipes-kernel/linux/linux-yocto-4.9.21/0093-bpf-fix-mixed-signed-unsigned-derived-min-max-value-.patch b/common/recipes-kernel/linux/linux-yocto-4.9.21/0093-bpf-fix-mixed-signed-unsigned-derived-min-max-value-.patch
new file mode 100644
index 00000000..8dce43f7
--- /dev/null
+++ b/common/recipes-kernel/linux/linux-yocto-4.9.21/0093-bpf-fix-mixed-signed-unsigned-derived-min-max-value-.patch
@@ -0,0 +1,463 @@
+From ae18a063a2a05514cf0821c68eecf75831c6200f Mon Sep 17 00:00:00 2001
+From: Daniel Borkmann <daniel@iogearbox.net>
+Date: Fri, 21 Jul 2017 00:00:21 +0200
+Subject: [PATCH 093/102] bpf: fix mixed signed/unsigned derived min/max value
+ bounds
+
+[ Upstream commit 4cabc5b186b5427b9ee5a7495172542af105f02b ]
+
+Edward reported that there's an issue in min/max value bounds
+tracking when signed and unsigned compares both provide hints
+on limits when having unknown variables. E.g. a program such
+as the following should have been rejected:
+
+ 0: (7a) *(u64 *)(r10 -8) = 0
+ 1: (bf) r2 = r10
+ 2: (07) r2 += -8
+ 3: (18) r1 = 0xffff8a94cda93400
+ 5: (85) call bpf_map_lookup_elem#1
+ 6: (15) if r0 == 0x0 goto pc+7
+ R0=map_value(ks=8,vs=8,id=0),min_value=0,max_value=0 R10=fp
+ 7: (7a) *(u64 *)(r10 -16) = -8
+ 8: (79) r1 = *(u64 *)(r10 -16)
+ 9: (b7) r2 = -1
+ 10: (2d) if r1 > r2 goto pc+3
+ R0=map_value(ks=8,vs=8,id=0),min_value=0,max_value=0 R1=inv,min_value=0
+ R2=imm-1,max_value=18446744073709551615,min_align=1 R10=fp
+ 11: (65) if r1 s> 0x1 goto pc+2
+ R0=map_value(ks=8,vs=8,id=0),min_value=0,max_value=0 R1=inv,min_value=0,max_value=1
+ R2=imm-1,max_value=18446744073709551615,min_align=1 R10=fp
+ 12: (0f) r0 += r1
+ 13: (72) *(u8 *)(r0 +0) = 0
+ R0=map_value_adj(ks=8,vs=8,id=0),min_value=0,max_value=1 R1=inv,min_value=0,max_value=1
+ R2=imm-1,max_value=18446744073709551615,min_align=1 R10=fp
+ 14: (b7) r0 = 0
+ 15: (95) exit
+
+What happens is that in the first part ...
+
+ 8: (79) r1 = *(u64 *)(r10 -16)
+ 9: (b7) r2 = -1
+ 10: (2d) if r1 > r2 goto pc+3
+
+... r1 carries an unsigned value, and is compared as unsigned
+against a register carrying an immediate. Verifier deduces in
+reg_set_min_max() that since the compare is unsigned and operation
+is greater than (>), that in the fall-through/false case, r1's
+minimum bound must be 0 and maximum bound must be r2. Latter is
+larger than the bound and thus max value is reset back to being
+'invalid' aka BPF_REGISTER_MAX_RANGE. Thus, r1 state is now
+'R1=inv,min_value=0'. The subsequent test ...
+
+ 11: (65) if r1 s> 0x1 goto pc+2
+
+... is a signed compare of r1 with immediate value 1. Here,
+verifier deduces in reg_set_min_max() that since the compare
+is signed this time and operation is greater than (>), that
+in the fall-through/false case, we can deduce that r1's maximum
+bound must be 1, meaning with prior test, we result in r1 having
+the following state: R1=inv,min_value=0,max_value=1. Given that
+the actual value this holds is -8, the bounds are wrongly deduced.
+When this is being added to r0 which holds the map_value(_adj)
+type, then subsequent store access in above case will go through
+check_mem_access() which invokes check_map_access_adj(), that
+will then probe whether the map memory is in bounds based
+on the min_value and max_value as well as access size since
+the actual unknown value is min_value <= x <= max_value; commit
+fce366a9dd0d ("bpf, verifier: fix alu ops against map_value{,
+_adj} register types") provides some more explanation on the
+semantics.
+
+It's worth to note in this context that in the current code,
+min_value and max_value tracking are used for two things, i)
+dynamic map value access via check_map_access_adj() and since
+commit 06c1c049721a ("bpf: allow helpers access to variable memory")
+ii) also enforced at check_helper_mem_access() when passing a
+memory address (pointer to packet, map value, stack) and length
+pair to a helper and the length in this case is an unknown value
+defining an access range through min_value/max_value in that
+case. The min_value/max_value tracking is /not/ used in the
+direct packet access case to track ranges. However, the issue
+also affects case ii), for example, the following crafted program
+based on the same principle must be rejected as well:
+
+ 0: (b7) r2 = 0
+ 1: (bf) r3 = r10
+ 2: (07) r3 += -512
+ 3: (7a) *(u64 *)(r10 -16) = -8
+ 4: (79) r4 = *(u64 *)(r10 -16)
+ 5: (b7) r6 = -1
+ 6: (2d) if r4 > r6 goto pc+5
+ R1=ctx R2=imm0,min_value=0,max_value=0,min_align=2147483648 R3=fp-512
+ R4=inv,min_value=0 R6=imm-1,max_value=18446744073709551615,min_align=1 R10=fp
+ 7: (65) if r4 s> 0x1 goto pc+4
+ R1=ctx R2=imm0,min_value=0,max_value=0,min_align=2147483648 R3=fp-512
+ R4=inv,min_value=0,max_value=1 R6=imm-1,max_value=18446744073709551615,min_align=1
+ R10=fp
+ 8: (07) r4 += 1
+ 9: (b7) r5 = 0
+ 10: (6a) *(u16 *)(r10 -512) = 0
+ 11: (85) call bpf_skb_load_bytes#26
+ 12: (b7) r0 = 0
+ 13: (95) exit
+
+Meaning, while we initialize the max_value stack slot that the
+verifier thinks we access in the [1,2] range, in reality we
+pass -7 as length which is interpreted as u32 in the helper.
+Thus, this issue is relevant also for the case of helper ranges.
+Resetting both bounds in check_reg_overflow() in case only one
+of them exceeds limits is also not enough as similar test can be
+created that uses values which are within range, thus also here
+learned min value in r1 is incorrect when mixed with later signed
+test to create a range:
+
+ 0: (7a) *(u64 *)(r10 -8) = 0
+ 1: (bf) r2 = r10
+ 2: (07) r2 += -8
+ 3: (18) r1 = 0xffff880ad081fa00
+ 5: (85) call bpf_map_lookup_elem#1
+ 6: (15) if r0 == 0x0 goto pc+7
+ R0=map_value(ks=8,vs=8,id=0),min_value=0,max_value=0 R10=fp
+ 7: (7a) *(u64 *)(r10 -16) = -8
+ 8: (79) r1 = *(u64 *)(r10 -16)
+ 9: (b7) r2 = 2
+ 10: (3d) if r2 >= r1 goto pc+3
+ R0=map_value(ks=8,vs=8,id=0),min_value=0,max_value=0 R1=inv,min_value=3
+ R2=imm2,min_value=2,max_value=2,min_align=2 R10=fp
+ 11: (65) if r1 s> 0x4 goto pc+2
+ R0=map_value(ks=8,vs=8,id=0),min_value=0,max_value=0
+ R1=inv,min_value=3,max_value=4 R2=imm2,min_value=2,max_value=2,min_align=2 R10=fp
+ 12: (0f) r0 += r1
+ 13: (72) *(u8 *)(r0 +0) = 0
+ R0=map_value_adj(ks=8,vs=8,id=0),min_value=3,max_value=4
+ R1=inv,min_value=3,max_value=4 R2=imm2,min_value=2,max_value=2,min_align=2 R10=fp
+ 14: (b7) r0 = 0
+ 15: (95) exit
+
+This leaves us with two options for fixing this: i) to invalidate
+all prior learned information once we switch signed context, ii)
+to track min/max signed and unsigned boundaries separately as
+done in [0]. (Given latter introduces major changes throughout
+the whole verifier, it's rather net-next material, thus this
+patch follows option i), meaning we can derive bounds either
+from only signed tests or only unsigned tests.) There is still the
+case of adjust_reg_min_max_vals(), where we adjust bounds on ALU
+operations, meaning programs like the following where boundaries
+on the reg get mixed in context later on when bounds are merged
+on the dst reg must get rejected, too:
+
+ 0: (7a) *(u64 *)(r10 -8) = 0
+ 1: (bf) r2 = r10
+ 2: (07) r2 += -8
+ 3: (18) r1 = 0xffff89b2bf87ce00
+ 5: (85) call bpf_map_lookup_elem#1
+ 6: (15) if r0 == 0x0 goto pc+6
+ R0=map_value(ks=8,vs=8,id=0),min_value=0,max_value=0 R10=fp
+ 7: (7a) *(u64 *)(r10 -16) = -8
+ 8: (79) r1 = *(u64 *)(r10 -16)
+ 9: (b7) r2 = 2
+ 10: (3d) if r2 >= r1 goto pc+2
+ R0=map_value(ks=8,vs=8,id=0),min_value=0,max_value=0 R1=inv,min_value=3
+ R2=imm2,min_value=2,max_value=2,min_align=2 R10=fp
+ 11: (b7) r7 = 1
+ 12: (65) if r7 s> 0x0 goto pc+2
+ R0=map_value(ks=8,vs=8,id=0),min_value=0,max_value=0 R1=inv,min_value=3
+ R2=imm2,min_value=2,max_value=2,min_align=2 R7=imm1,max_value=0 R10=fp
+ 13: (b7) r0 = 0
+ 14: (95) exit
+
+ from 12 to 15: R0=map_value(ks=8,vs=8,id=0),min_value=0,max_value=0
+ R1=inv,min_value=3 R2=imm2,min_value=2,max_value=2,min_align=2 R7=imm1,min_value=1 R10=fp
+ 15: (0f) r7 += r1
+ 16: (65) if r7 s> 0x4 goto pc+2
+ R0=map_value(ks=8,vs=8,id=0),min_value=0,max_value=0 R1=inv,min_value=3
+ R2=imm2,min_value=2,max_value=2,min_align=2 R7=inv,min_value=4,max_value=4 R10=fp
+ 17: (0f) r0 += r7
+ 18: (72) *(u8 *)(r0 +0) = 0
+ R0=map_value_adj(ks=8,vs=8,id=0),min_value=4,max_value=4 R1=inv,min_value=3
+ R2=imm2,min_value=2,max_value=2,min_align=2 R7=inv,min_value=4,max_value=4 R10=fp
+ 19: (b7) r0 = 0
+ 20: (95) exit
+
+Meaning, in adjust_reg_min_max_vals() we must also reset range
+values on the dst when src/dst registers have mixed signed/
+unsigned derived min/max value bounds with one unbounded value
+as otherwise they can be added together deducing false boundaries.
+Once both boundaries are established from either ALU ops or
+compare operations w/o mixing signed/unsigned insns, then they
+can safely be added to other regs also having both boundaries
+established. Adding regs with one unbounded side to a map value
+where the bounded side has been learned w/o mixing ops is
+possible, but the resulting map value won't recover from that,
+meaning such op is considered invalid on the time of actual
+access. Invalid bounds are set on the dst reg in case i) src reg,
+or ii) in case dst reg already had them. The only way to recover
+would be to perform i) ALU ops but only 'add' is allowed on map
+value types or ii) comparisons, but these are disallowed on
+pointers in case they span a range. This is fine as only BPF_JEQ
+and BPF_JNE may be performed on PTR_TO_MAP_VALUE_OR_NULL registers
+which potentially turn them into PTR_TO_MAP_VALUE type depending
+on the branch, so only here min/max value cannot be invalidated
+for them.
+
+In terms of state pruning, value_from_signed is considered
+as well in states_equal() when dealing with adjusted map values.
+With regards to breaking existing programs, there is a small
+risk, but use-cases are rather quite narrow where this could
+occur and mixing compares probably unlikely.
+
+Joint work with Josef and Edward.
+
+ [0] https://lists.iovisor.org/pipermail/iovisor-dev/2017-June/000822.html
+
+Fixes: 484611357c19 ("bpf: allow access into map value arrays")
+Reported-by: Edward Cree <ecree@solarflare.com>
+Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
+Signed-off-by: Edward Cree <ecree@solarflare.com>
+Signed-off-by: Josef Bacik <jbacik@fb.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/linux/bpf_verifier.h | 1 +
+ kernel/bpf/verifier.c | 110 +++++++++++++++++++++++++++++++++++++------
+ 2 files changed, 97 insertions(+), 14 deletions(-)
+
+diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
+index 2edf8de..070fc49 100644
+--- a/include/linux/bpf_verifier.h
++++ b/include/linux/bpf_verifier.h
+@@ -40,6 +40,7 @@ struct bpf_reg_state {
+ */
+ s64 min_value;
+ u64 max_value;
++ bool value_from_signed;
+ };
+
+ enum bpf_stack_slot_type {
+diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
+index 56a867f..5f274c6 100644
+--- a/kernel/bpf/verifier.c
++++ b/kernel/bpf/verifier.c
+@@ -671,12 +671,13 @@ static int check_ctx_access(struct bpf_verifier_env *env, int off, int size,
+ 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;
+@@ -685,6 +686,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_ptr_alignment(struct bpf_verifier_env *env,
+ struct bpf_reg_state *reg, int off, int size)
+ {
+@@ -1521,10 +1527,24 @@ static void adjust_reg_min_max_vals(struct bpf_verifier_env *env,
+ }
+
+ /* 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;
+ }
+@@ -1855,38 +1875,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;
+@@ -1894,6 +1939,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
+@@ -1903,39 +1954,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;
+@@ -1943,6 +2019,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,
+--
+2.7.4
+