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