diff options
-rw-r--r-- | patches/cve/CVE-2017-16996-bpf-fix-incorrect-tracking-of-register-size-truncati.patch | 132 |
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 @@ | |||
1 | From bf5ee24e87e39548bf30d4e18e479e61a5a98336 Mon Sep 17 00:00:00 2001 | ||
2 | From: Daniel Borkmann <daniel@iogearbox.net> | ||
3 | Date: Fri, 22 Dec 2017 16:23:06 +0100 | ||
4 | Subject: [PATCH] bpf: fix incorrect tracking of register size truncation | ||
5 | |||
6 | From: Jann Horn <jannh@google.com> | ||
7 | |||
8 | [ Upstream commit 0c17d1d2c61936401f4702e1846e2c19b200f958 ] | ||
9 | |||
10 | Properly handle register truncation to a smaller size. | ||
11 | |||
12 | The old code first mirrors the clearing of the high 32 bits in the bitwise | ||
13 | tristate representation, which is correct. But then, it computes the new | ||
14 | arithmetic bounds as the intersection between the old arithmetic bounds and | ||
15 | the bounds resulting from the bitwise tristate representation. Therefore, | ||
16 | when 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. | ||
19 | This is incorrect: The truncated number could also be in the range [0, 7], | ||
20 | and no meaningful arithmetic bounds can be computed in that case apart from | ||
21 | the obvious [0, 0xffff'ffff]. | ||
22 | |||
23 | Starting with v4.14, this is exploitable by unprivileged users as long as | ||
24 | the unprivileged_bpf_disabled sysctl isn't set. | ||
25 | |||
26 | Debian assigned CVE-2017-16996 for this issue. | ||
27 | |||
28 | v2: | ||
29 | - flip the mask during arithmetic bounds calculation (Ben Hutchings) | ||
30 | v3: | ||
31 | - add CVE number (Ben Hutchings) | ||
32 | |||
33 | CVE: CVE-2017-16996 | ||
34 | Upstream-Status: Backport [https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?h=linux-4.14.y&id=bf5ee24e87e39548bf30d4e18e479e61a5a98336] | ||
35 | |||
36 | Fixes: b03c9f9fdc37 ("bpf/verifier: track signed and unsigned min/max values") | ||
37 | Signed-off-by: Jann Horn <jannh@google.com> | ||
38 | Acked-by: Edward Cree <ecree@solarflare.com> | ||
39 | Signed-off-by: Alexei Starovoitov <ast@kernel.org> | ||
40 | Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> | ||
41 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | ||
42 | Signed-off-by: Andreas Wellving <andreas.wellving@enea.com> | ||
43 | --- | ||
44 | kernel/bpf/verifier.c | 44 ++++++++++++++++++++++++++----------------- | ||
45 | 1 file changed, 27 insertions(+), 17 deletions(-) | ||
46 | |||
47 | diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c | ||
48 | index 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(®s[insn->dst_reg]); | ||
126 | + coerce_reg_to_size(®s[insn->dst_reg], 4); | ||
127 | } | ||
128 | } else { | ||
129 | /* case: R = imm | ||
130 | -- | ||
131 | 2.20.1 | ||
132 | |||