diff options
Diffstat (limited to 'patches/cve/CVE-2017-9074-ipv6-Prevent-overrun-when-parsing-v6-header-options.patch')
-rw-r--r-- | patches/cve/CVE-2017-9074-ipv6-Prevent-overrun-when-parsing-v6-header-options.patch | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/patches/cve/CVE-2017-9074-ipv6-Prevent-overrun-when-parsing-v6-header-options.patch b/patches/cve/CVE-2017-9074-ipv6-Prevent-overrun-when-parsing-v6-header-options.patch new file mode 100644 index 0000000..ade9eaa --- /dev/null +++ b/patches/cve/CVE-2017-9074-ipv6-Prevent-overrun-when-parsing-v6-header-options.patch | |||
@@ -0,0 +1,236 @@ | |||
1 | From e7f05ff30b0cd72b00c8ca7be3cd48fedf96550f Mon Sep 17 00:00:00 2001 | ||
2 | From: Craig Gallek <kraig@google.com> | ||
3 | Date: Tue, 16 May 2017 14:36:23 -0400 | ||
4 | Subject: [PATCH] ipv6: Prevent overrun when parsing v6 header options | ||
5 | |||
6 | [ Upstream commit 2423496af35d94a87156b063ea5cedffc10a70a1 ] | ||
7 | |||
8 | The KASAN warning repoted below was discovered with a syzkaller | ||
9 | program. The reproducer is basically: | ||
10 | int s = socket(AF_INET6, SOCK_RAW, NEXTHDR_HOP); | ||
11 | send(s, &one_byte_of_data, 1, MSG_MORE); | ||
12 | send(s, &more_than_mtu_bytes_data, 2000, 0); | ||
13 | |||
14 | The socket() call sets the nexthdr field of the v6 header to | ||
15 | NEXTHDR_HOP, the first send call primes the payload with a non zero | ||
16 | byte of data, and the second send call triggers the fragmentation path. | ||
17 | |||
18 | The fragmentation code tries to parse the header options in order | ||
19 | to figure out where to insert the fragment option. Since nexthdr points | ||
20 | to an invalid option, the calculation of the size of the network header | ||
21 | can made to be much larger than the linear section of the skb and data | ||
22 | is read outside of it. | ||
23 | |||
24 | This fix makes ip6_find_1stfrag return an error if it detects | ||
25 | running out-of-bounds. | ||
26 | |||
27 | [ 42.361487] ================================================================== | ||
28 | [ 42.364412] BUG: KASAN: slab-out-of-bounds in ip6_fragment+0x11c8/0x3730 | ||
29 | [ 42.365471] Read of size 840 at addr ffff88000969e798 by task ip6_fragment-oo/3789 | ||
30 | [ 42.366469] | ||
31 | [ 42.366696] CPU: 1 PID: 3789 Comm: ip6_fragment-oo Not tainted 4.11.0+ #41 | ||
32 | [ 42.367628] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.1-1ubuntu1 04/01/2014 | ||
33 | [ 42.368824] Call Trace: | ||
34 | [ 42.369183] dump_stack+0xb3/0x10b | ||
35 | [ 42.369664] print_address_description+0x73/0x290 | ||
36 | [ 42.370325] kasan_report+0x252/0x370 | ||
37 | [ 42.370839] ? ip6_fragment+0x11c8/0x3730 | ||
38 | [ 42.371396] check_memory_region+0x13c/0x1a0 | ||
39 | [ 42.371978] memcpy+0x23/0x50 | ||
40 | [ 42.372395] ip6_fragment+0x11c8/0x3730 | ||
41 | [ 42.372920] ? nf_ct_expect_unregister_notifier+0x110/0x110 | ||
42 | [ 42.373681] ? ip6_copy_metadata+0x7f0/0x7f0 | ||
43 | [ 42.374263] ? ip6_forward+0x2e30/0x2e30 | ||
44 | [ 42.374803] ip6_finish_output+0x584/0x990 | ||
45 | [ 42.375350] ip6_output+0x1b7/0x690 | ||
46 | [ 42.375836] ? ip6_finish_output+0x990/0x990 | ||
47 | [ 42.376411] ? ip6_fragment+0x3730/0x3730 | ||
48 | [ 42.376968] ip6_local_out+0x95/0x160 | ||
49 | [ 42.377471] ip6_send_skb+0xa1/0x330 | ||
50 | [ 42.377969] ip6_push_pending_frames+0xb3/0xe0 | ||
51 | [ 42.378589] rawv6_sendmsg+0x2051/0x2db0 | ||
52 | [ 42.379129] ? rawv6_bind+0x8b0/0x8b0 | ||
53 | [ 42.379633] ? _copy_from_user+0x84/0xe0 | ||
54 | [ 42.380193] ? debug_check_no_locks_freed+0x290/0x290 | ||
55 | [ 42.380878] ? ___sys_sendmsg+0x162/0x930 | ||
56 | [ 42.381427] ? rcu_read_lock_sched_held+0xa3/0x120 | ||
57 | [ 42.382074] ? sock_has_perm+0x1f6/0x290 | ||
58 | [ 42.382614] ? ___sys_sendmsg+0x167/0x930 | ||
59 | [ 42.383173] ? lock_downgrade+0x660/0x660 | ||
60 | [ 42.383727] inet_sendmsg+0x123/0x500 | ||
61 | [ 42.384226] ? inet_sendmsg+0x123/0x500 | ||
62 | [ 42.384748] ? inet_recvmsg+0x540/0x540 | ||
63 | [ 42.385263] sock_sendmsg+0xca/0x110 | ||
64 | [ 42.385758] SYSC_sendto+0x217/0x380 | ||
65 | [ 42.386249] ? SYSC_connect+0x310/0x310 | ||
66 | [ 42.386783] ? __might_fault+0x110/0x1d0 | ||
67 | [ 42.387324] ? lock_downgrade+0x660/0x660 | ||
68 | [ 42.387880] ? __fget_light+0xa1/0x1f0 | ||
69 | [ 42.388403] ? __fdget+0x18/0x20 | ||
70 | [ 42.388851] ? sock_common_setsockopt+0x95/0xd0 | ||
71 | [ 42.389472] ? SyS_setsockopt+0x17f/0x260 | ||
72 | [ 42.390021] ? entry_SYSCALL_64_fastpath+0x5/0xbe | ||
73 | [ 42.390650] SyS_sendto+0x40/0x50 | ||
74 | [ 42.391103] entry_SYSCALL_64_fastpath+0x1f/0xbe | ||
75 | [ 42.391731] RIP: 0033:0x7fbbb711e383 | ||
76 | [ 42.392217] RSP: 002b:00007ffff4d34f28 EFLAGS: 00000246 ORIG_RAX: 000000000000002c | ||
77 | [ 42.393235] RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007fbbb711e383 | ||
78 | [ 42.394195] RDX: 0000000000001000 RSI: 00007ffff4d34f60 RDI: 0000000000000003 | ||
79 | [ 42.395145] RBP: 0000000000000046 R08: 00007ffff4d34f40 R09: 0000000000000018 | ||
80 | [ 42.396056] R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000400aad | ||
81 | [ 42.396598] R13: 0000000000000066 R14: 00007ffff4d34ee0 R15: 00007fbbb717af00 | ||
82 | [ 42.397257] | ||
83 | [ 42.397411] Allocated by task 3789: | ||
84 | [ 42.397702] save_stack_trace+0x16/0x20 | ||
85 | [ 42.398005] save_stack+0x46/0xd0 | ||
86 | [ 42.398267] kasan_kmalloc+0xad/0xe0 | ||
87 | [ 42.398548] kasan_slab_alloc+0x12/0x20 | ||
88 | [ 42.398848] __kmalloc_node_track_caller+0xcb/0x380 | ||
89 | [ 42.399224] __kmalloc_reserve.isra.32+0x41/0xe0 | ||
90 | [ 42.399654] __alloc_skb+0xf8/0x580 | ||
91 | [ 42.400003] sock_wmalloc+0xab/0xf0 | ||
92 | [ 42.400346] __ip6_append_data.isra.41+0x2472/0x33d0 | ||
93 | [ 42.400813] ip6_append_data+0x1a8/0x2f0 | ||
94 | [ 42.401122] rawv6_sendmsg+0x11ee/0x2db0 | ||
95 | [ 42.401505] inet_sendmsg+0x123/0x500 | ||
96 | [ 42.401860] sock_sendmsg+0xca/0x110 | ||
97 | [ 42.402209] ___sys_sendmsg+0x7cb/0x930 | ||
98 | [ 42.402582] __sys_sendmsg+0xd9/0x190 | ||
99 | [ 42.402941] SyS_sendmsg+0x2d/0x50 | ||
100 | [ 42.403273] entry_SYSCALL_64_fastpath+0x1f/0xbe | ||
101 | [ 42.403718] | ||
102 | [ 42.403871] Freed by task 1794: | ||
103 | [ 42.404146] save_stack_trace+0x16/0x20 | ||
104 | [ 42.404515] save_stack+0x46/0xd0 | ||
105 | [ 42.404827] kasan_slab_free+0x72/0xc0 | ||
106 | [ 42.405167] kfree+0xe8/0x2b0 | ||
107 | [ 42.405462] skb_free_head+0x74/0xb0 | ||
108 | [ 42.405806] skb_release_data+0x30e/0x3a0 | ||
109 | [ 42.406198] skb_release_all+0x4a/0x60 | ||
110 | [ 42.406563] consume_skb+0x113/0x2e0 | ||
111 | [ 42.406910] skb_free_datagram+0x1a/0xe0 | ||
112 | [ 42.407288] netlink_recvmsg+0x60d/0xe40 | ||
113 | [ 42.407667] sock_recvmsg+0xd7/0x110 | ||
114 | [ 42.408022] ___sys_recvmsg+0x25c/0x580 | ||
115 | [ 42.408395] __sys_recvmsg+0xd6/0x190 | ||
116 | [ 42.408753] SyS_recvmsg+0x2d/0x50 | ||
117 | [ 42.409086] entry_SYSCALL_64_fastpath+0x1f/0xbe | ||
118 | [ 42.409513] | ||
119 | [ 42.409665] The buggy address belongs to the object at ffff88000969e780 | ||
120 | [ 42.409665] which belongs to the cache kmalloc-512 of size 512 | ||
121 | [ 42.410846] The buggy address is located 24 bytes inside of | ||
122 | [ 42.410846] 512-byte region [ffff88000969e780, ffff88000969e980) | ||
123 | [ 42.411941] The buggy address belongs to the page: | ||
124 | [ 42.412405] page:ffffea000025a780 count:1 mapcount:0 mapping: (null) index:0x0 compound_mapcount: 0 | ||
125 | [ 42.413298] flags: 0x100000000008100(slab|head) | ||
126 | [ 42.413729] raw: 0100000000008100 0000000000000000 0000000000000000 00000001800c000c | ||
127 | [ 42.414387] raw: ffffea00002a9500 0000000900000007 ffff88000c401280 0000000000000000 | ||
128 | [ 42.415074] page dumped because: kasan: bad access detected | ||
129 | [ 42.415604] | ||
130 | [ 42.415757] Memory state around the buggy address: | ||
131 | [ 42.416222] ffff88000969e880: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ||
132 | [ 42.416904] ffff88000969e900: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ||
133 | [ 42.417591] >ffff88000969e980: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc | ||
134 | [ 42.418273] ^ | ||
135 | [ 42.418588] ffff88000969ea00: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb | ||
136 | [ 42.419273] ffff88000969ea80: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb | ||
137 | [ 42.419882] ================================================================== | ||
138 | |||
139 | CVE: CVE-2017-9074 | ||
140 | Upstream-Status: Backport | ||
141 | |||
142 | Reported-by: Andrey Konovalov <andreyknvl@google.com> | ||
143 | Signed-off-by: Craig Gallek <kraig@google.com> | ||
144 | Signed-off-by: David S. Miller <davem@davemloft.net> | ||
145 | Signed-off-by: Sasha Levin <alexander.levin@verizon.com> | ||
146 | Signed-off-by: Andreas Wellving <andreas.wellving@enea.com> | ||
147 | --- | ||
148 | net/ipv6/ip6_offload.c | 2 ++ | ||
149 | net/ipv6/ip6_output.c | 4 ++++ | ||
150 | net/ipv6/output_core.c | 14 ++++++++------ | ||
151 | net/ipv6/udp_offload.c | 2 ++ | ||
152 | 4 files changed, 16 insertions(+), 6 deletions(-) | ||
153 | |||
154 | diff --git a/net/ipv6/ip6_offload.c b/net/ipv6/ip6_offload.c | ||
155 | index 9b01da5..ff400ad 100644 | ||
156 | --- a/net/ipv6/ip6_offload.c | ||
157 | +++ b/net/ipv6/ip6_offload.c | ||
158 | @@ -122,6 +122,8 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, | ||
159 | |||
160 | if (udpfrag) { | ||
161 | unfrag_ip6hlen = ip6_find_1stfragopt(skb, &prevhdr); | ||
162 | + if (unfrag_ip6hlen < 0) | ||
163 | + return ERR_PTR(unfrag_ip6hlen); | ||
164 | fptr = (struct frag_hdr *)((u8 *)ipv6h + unfrag_ip6hlen); | ||
165 | fptr->frag_off = htons(offset); | ||
166 | if (skb->next) | ||
167 | diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c | ||
168 | index 06bf401..5f4faa5 100644 | ||
169 | --- a/net/ipv6/ip6_output.c | ||
170 | +++ b/net/ipv6/ip6_output.c | ||
171 | @@ -560,6 +560,10 @@ int ip6_fragment(struct sock *sk, struct sk_buff *skb, | ||
172 | struct net *net = dev_net(skb_dst(skb)->dev); | ||
173 | |||
174 | hlen = ip6_find_1stfragopt(skb, &prevhdr); | ||
175 | + if (hlen < 0) { | ||
176 | + err = hlen; | ||
177 | + goto fail; | ||
178 | + } | ||
179 | nexthdr = *prevhdr; | ||
180 | |||
181 | mtu = ip6_skb_dst_mtu(skb); | ||
182 | diff --git a/net/ipv6/output_core.c b/net/ipv6/output_core.c | ||
183 | index 85892af..3f6ee41 100644 | ||
184 | --- a/net/ipv6/output_core.c | ||
185 | +++ b/net/ipv6/output_core.c | ||
186 | @@ -77,14 +77,13 @@ EXPORT_SYMBOL(ipv6_select_ident); | ||
187 | int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr) | ||
188 | { | ||
189 | u16 offset = sizeof(struct ipv6hdr); | ||
190 | - struct ipv6_opt_hdr *exthdr = | ||
191 | - (struct ipv6_opt_hdr *)(ipv6_hdr(skb) + 1); | ||
192 | unsigned int packet_len = skb_tail_pointer(skb) - | ||
193 | skb_network_header(skb); | ||
194 | int found_rhdr = 0; | ||
195 | *nexthdr = &ipv6_hdr(skb)->nexthdr; | ||
196 | |||
197 | - while (offset + 1 <= packet_len) { | ||
198 | + while (offset <= packet_len) { | ||
199 | + struct ipv6_opt_hdr *exthdr; | ||
200 | |||
201 | switch (**nexthdr) { | ||
202 | |||
203 | @@ -105,13 +104,16 @@ int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr) | ||
204 | return offset; | ||
205 | } | ||
206 | |||
207 | - offset += ipv6_optlen(exthdr); | ||
208 | - *nexthdr = &exthdr->nexthdr; | ||
209 | + if (offset + sizeof(struct ipv6_opt_hdr) > packet_len) | ||
210 | + return -EINVAL; | ||
211 | + | ||
212 | exthdr = (struct ipv6_opt_hdr *)(skb_network_header(skb) + | ||
213 | offset); | ||
214 | + offset += ipv6_optlen(exthdr); | ||
215 | + *nexthdr = &exthdr->nexthdr; | ||
216 | } | ||
217 | |||
218 | - return offset; | ||
219 | + return -EINVAL; | ||
220 | } | ||
221 | EXPORT_SYMBOL(ip6_find_1stfragopt); | ||
222 | |||
223 | diff --git a/net/ipv6/udp_offload.c b/net/ipv6/udp_offload.c | ||
224 | index 7441e1e..04ed91d 100644 | ||
225 | --- a/net/ipv6/udp_offload.c | ||
226 | +++ b/net/ipv6/udp_offload.c | ||
227 | @@ -98,6 +98,8 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb, | ||
228 | * bytes to insert fragment header. | ||
229 | */ | ||
230 | unfrag_ip6hlen = ip6_find_1stfragopt(skb, &prevhdr); | ||
231 | + if (unfrag_ip6hlen < 0) | ||
232 | + return ERR_PTR(unfrag_ip6hlen); | ||
233 | nexthdr = *prevhdr; | ||
234 | *prevhdr = NEXTHDR_FRAGMENT; | ||
235 | unfrag_len = (skb_network_header(skb) - skb_mac_header(skb)) + | ||
236 | -- | ||