Skip to content

Commit 55888df

Browse files
nhormandavem330
authored andcommitted
AF_RAW: Augment raw_send_hdrinc to expand skb to fit iphdr->ihl (v2)
Augment raw_send_hdrinc to correct for incorrect ip header length values A series of oopses was reported to me recently. Apparently when using AF_RAW sockets to send data to peers that were reachable via ipsec encapsulation, people could panic or BUG halt their systems. I've tracked the problem down to user space sending an invalid ip header over an AF_RAW socket with IP_HDRINCL set to 1. Basically what happens is that userspace sends down an ip frame that includes only the header (no data), but sets the ip header ihl value to a large number, one that is larger than the total amount of data passed to the sendmsg call. In raw_send_hdrincl, we allocate an skb based on the size of the data in the msghdr that was passed in, but assume the data is all valid. Later during ipsec encapsulation, xfrm4_tranport_output moves the entire frame back in the skbuff to provide headroom for the ipsec headers. During this operation, the skb->transport_header is repointed to a spot computed by skb->network_header + the ip header length (ihl). Since so little data was passed in relative to the value of ihl provided by the raw socket, we point transport header to an unknown location, resulting in various crashes. This fix for this is pretty straightforward, simply validate the value of of iph->ihl when sending over a raw socket. If (iph->ihl*4U) > user data buffer size, drop the frame and return -EINVAL. I just confirmed this fixes the reported crashes. Signed-off-by: Neil Horman <[email protected]> Acked-by: Eric Dumazet <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent f552ce5 commit 55888df

File tree

1 file changed

+17
-7
lines changed

1 file changed

+17
-7
lines changed

net/ipv4/raw.c

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -352,13 +352,24 @@ static int raw_send_hdrinc(struct sock *sk, void *from, size_t length,
352352
skb->ip_summed = CHECKSUM_NONE;
353353

354354
skb->transport_header = skb->network_header;
355-
err = memcpy_fromiovecend((void *)iph, from, 0, length);
356-
if (err)
357-
goto error_fault;
355+
err = -EFAULT;
356+
if (memcpy_fromiovecend((void *)iph, from, 0, length))
357+
goto error_free;
358358

359-
/* We don't modify invalid header */
360359
iphlen = iph->ihl * 4;
361-
if (iphlen >= sizeof(*iph) && iphlen <= length) {
360+
361+
/*
362+
* We don't want to modify the ip header, but we do need to
363+
* be sure that it won't cause problems later along the network
364+
* stack. Specifically we want to make sure that iph->ihl is a
365+
* sane value. If ihl points beyond the length of the buffer passed
366+
* in, reject the frame as invalid
367+
*/
368+
err = -EINVAL;
369+
if (iphlen > length)
370+
goto error_free;
371+
372+
if (iphlen >= sizeof(*iph)) {
362373
if (!iph->saddr)
363374
iph->saddr = rt->rt_src;
364375
iph->check = 0;
@@ -381,8 +392,7 @@ static int raw_send_hdrinc(struct sock *sk, void *from, size_t length,
381392
out:
382393
return 0;
383394

384-
error_fault:
385-
err = -EFAULT;
395+
error_free:
386396
kfree_skb(skb);
387397
error:
388398
IP_INC_STATS(net, IPSTATS_MIB_OUTDISCARDS);

0 commit comments

Comments
 (0)