summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--patches/cve/CVE-2017-16996-bpf-fix-incorrect-tracking-of-register-size-truncati.patch132
1 files changed, 132 insertions, 0 deletions
diff --git a/patches/cve/CVE-2017-16996-bpf-fix-incorrect-tracking-of-register-size-truncati.patch b/patches/cve/CVE-2017-16996-bpf-fix-incorrect-tracking-of-register-size-truncati.patch
new file mode 100644
index 0000000..4bd8231
--- /dev/null
+++ b/patches/cve/CVE-2017-16996-bpf-fix-incorrect-tracking-of-register-size-truncati.patch
@@ -0,0 +1,132 @@
1From bf5ee24e87e39548bf30d4e18e479e61a5a98336 Mon Sep 17 00:00:00 2001
2From: Daniel Borkmann <daniel@iogearbox.net>
3Date: Fri, 22 Dec 2017 16:23:06 +0100
4Subject: [PATCH] bpf: fix incorrect tracking of register size truncation
5
6From: Jann Horn <jannh@google.com>
7
8[ Upstream commit 0c17d1d2c61936401f4702e1846e2c19b200f958 ]
9
10Properly handle register truncation to a smaller size.
11
12The old code first mirrors the clearing of the high 32 bits in the bitwise
13tristate representation, which is correct. But then, it computes the new
14arithmetic bounds as the intersection between the old arithmetic bounds and
15the bounds resulting from the bitwise tristate representation. Therefore,
16when coerce_reg_to_32() is called on a number with bounds
17[0xffff'fff8, 0x1'0000'0007], the verifier computes
18[0xffff'fff8, 0xffff'ffff] as bounds of the truncated number.
19This is incorrect: The truncated number could also be in the range [0, 7],
20and no meaningful arithmetic bounds can be computed in that case apart from
21the obvious [0, 0xffff'ffff].
22
23Starting with v4.14, this is exploitable by unprivileged users as long as
24the unprivileged_bpf_disabled sysctl isn't set.
25
26Debian assigned CVE-2017-16996 for this issue.
27
28v2:
29 - flip the mask during arithmetic bounds calculation (Ben Hutchings)
30v3:
31 - add CVE number (Ben Hutchings)
32
33CVE: CVE-2017-16996
34Upstream-Status: Backport [https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?h=linux-4.14.y&id=bf5ee24e87e39548bf30d4e18e479e61a5a98336]
35
36Fixes: b03c9f9fdc37 ("bpf/verifier: track signed and unsigned min/max values")
37Signed-off-by: Jann Horn <jannh@google.com>
38Acked-by: Edward Cree <ecree@solarflare.com>
39Signed-off-by: Alexei Starovoitov <ast@kernel.org>
40Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
41Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
42Signed-off-by: Andreas Wellving <andreas.wellving@enea.com>
43---
44 kernel/bpf/verifier.c | 44 ++++++++++++++++++++++++++-----------------
45 1 file changed, 27 insertions(+), 17 deletions(-)
46
47diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
48index 2811ced3c2a9..1e2afb264c31 100644
49--- a/kernel/bpf/verifier.c
50+++ b/kernel/bpf/verifier.c
51@@ -1068,6 +1068,29 @@ static int check_ptr_alignment(struct bpf_verifier_env *env,
52 return check_generic_ptr_alignment(reg, pointer_desc, off, size, strict);
53 }
54
55+/* truncate register to smaller size (in bytes)
56+ * must be called with size < BPF_REG_SIZE
57+ */
58+static void coerce_reg_to_size(struct bpf_reg_state *reg, int size)
59+{
60+ u64 mask;
61+
62+ /* clear high bits in bit representation */
63+ reg->var_off = tnum_cast(reg->var_off, size);
64+
65+ /* fix arithmetic bounds */
66+ mask = ((u64)1 << (size * 8)) - 1;
67+ if ((reg->umin_value & ~mask) == (reg->umax_value & ~mask)) {
68+ reg->umin_value &= mask;
69+ reg->umax_value &= mask;
70+ } else {
71+ reg->umin_value = 0;
72+ reg->umax_value = mask;
73+ }
74+ reg->smin_value = reg->umin_value;
75+ reg->smax_value = reg->umax_value;
76+}
77+
78 /* check whether memory at (regno + off) is accessible for t = (read | write)
79 * if t==write, value_regno is a register which value is stored into memory
80 * if t==read, value_regno is a register which will receive the value from memory
81@@ -1200,9 +1223,7 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn
82 if (!err && size < BPF_REG_SIZE && value_regno >= 0 && t == BPF_READ &&
83 state->regs[value_regno].type == SCALAR_VALUE) {
84 /* b/h/w load zero-extends, mark upper bits as known 0 */
85- state->regs[value_regno].var_off = tnum_cast(
86- state->regs[value_regno].var_off, size);
87- __update_reg_bounds(&state->regs[value_regno]);
88+ coerce_reg_to_size(&state->regs[value_regno], size);
89 }
90 return err;
91 }
92@@ -1742,14 +1763,6 @@ static int check_call(struct bpf_verifier_env *env, int func_id, int insn_idx)
93 return 0;
94 }
95
96-static void coerce_reg_to_32(struct bpf_reg_state *reg)
97-{
98- /* clear high 32 bits */
99- reg->var_off = tnum_cast(reg->var_off, 4);
100- /* Update bounds */
101- __update_reg_bounds(reg);
102-}
103-
104 static bool signed_add_overflows(s64 a, s64 b)
105 {
106 /* Do the add in u64, where overflow is well-defined */
107@@ -1984,8 +1997,8 @@ static int adjust_scalar_min_max_vals(struct bpf_verifier_env *env,
108
109 if (BPF_CLASS(insn->code) != BPF_ALU64) {
110 /* 32-bit ALU ops are (32,32)->64 */
111- coerce_reg_to_32(dst_reg);
112- coerce_reg_to_32(&src_reg);
113+ coerce_reg_to_size(dst_reg, 4);
114+ coerce_reg_to_size(&src_reg, 4);
115 }
116 smin_val = src_reg.smin_value;
117 smax_val = src_reg.smax_value;
118@@ -2364,10 +2377,7 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn)
119 return -EACCES;
120 }
121 mark_reg_unknown(regs, insn->dst_reg);
122- /* high 32 bits are known zero. */
123- regs[insn->dst_reg].var_off = tnum_cast(
124- regs[insn->dst_reg].var_off, 4);
125- __update_reg_bounds(&regs[insn->dst_reg]);
126+ coerce_reg_to_size(&regs[insn->dst_reg], 4);
127 }
128 } else {
129 /* case: R = imm
130--
1312.20.1
132