mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	net: usb: ax88179_178a: Fix out-of-bounds accesses in RX fixup
ax88179_rx_fixup() contains several out-of-bounds accesses that can be
triggered by a malicious (or defective) USB device, in particular:
 - The metadata array (hdr_off..hdr_off+2*pkt_cnt) can be out of bounds,
   causing OOB reads and (on big-endian systems) OOB endianness flips.
 - A packet can overlap the metadata array, causing a later OOB
   endianness flip to corrupt data used by a cloned SKB that has already
   been handed off into the network stack.
 - A packet SKB can be constructed whose tail is far beyond its end,
   causing out-of-bounds heap data to be considered part of the SKB's
   data.
I have tested that this can be used by a malicious USB device to send a
bogus ICMPv6 Echo Request and receive an ICMPv6 Echo Reply in response
that contains random kernel heap data.
It's probably also possible to get OOB writes from this on a
little-endian system somehow - maybe by triggering skb_cow() via IP
options processing -, but I haven't tested that.
Fixes: e2ca90c276 ("ax88179_178a: ASIX AX88179_178A USB 3.0/2.0 to gigabit ethernet adapter driver")
Cc: stable@kernel.org
Signed-off-by: Jann Horn <jannh@google.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
			
			
This commit is contained in:
		
							parent
							
								
									117b4e96c7
								
							
						
					
					
						commit
						57bc3d3ae8
					
				
					 1 changed files with 38 additions and 28 deletions
				
			
		| 
						 | 
				
			
			@ -1468,58 +1468,68 @@ static int ax88179_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
 | 
			
		|||
	u16 hdr_off;
 | 
			
		||||
	u32 *pkt_hdr;
 | 
			
		||||
 | 
			
		||||
	/* This check is no longer done by usbnet */
 | 
			
		||||
	if (skb->len < dev->net->hard_header_len)
 | 
			
		||||
	/* At the end of the SKB, there's a header telling us how many packets
 | 
			
		||||
	 * are bundled into this buffer and where we can find an array of
 | 
			
		||||
	 * per-packet metadata (which contains elements encoded into u16).
 | 
			
		||||
	 */
 | 
			
		||||
	if (skb->len < 4)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	skb_trim(skb, skb->len - 4);
 | 
			
		||||
	rx_hdr = get_unaligned_le32(skb_tail_pointer(skb));
 | 
			
		||||
 | 
			
		||||
	pkt_cnt = (u16)rx_hdr;
 | 
			
		||||
	hdr_off = (u16)(rx_hdr >> 16);
 | 
			
		||||
 | 
			
		||||
	if (pkt_cnt == 0)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	/* Make sure that the bounds of the metadata array are inside the SKB
 | 
			
		||||
	 * (and in front of the counter at the end).
 | 
			
		||||
	 */
 | 
			
		||||
	if (pkt_cnt * 2 + hdr_off > skb->len)
 | 
			
		||||
		return 0;
 | 
			
		||||
	pkt_hdr = (u32 *)(skb->data + hdr_off);
 | 
			
		||||
 | 
			
		||||
	while (pkt_cnt--) {
 | 
			
		||||
	/* Packets must not overlap the metadata array */
 | 
			
		||||
	skb_trim(skb, hdr_off);
 | 
			
		||||
 | 
			
		||||
	for (; ; pkt_cnt--, pkt_hdr++) {
 | 
			
		||||
		u16 pkt_len;
 | 
			
		||||
 | 
			
		||||
		le32_to_cpus(pkt_hdr);
 | 
			
		||||
		pkt_len = (*pkt_hdr >> 16) & 0x1fff;
 | 
			
		||||
 | 
			
		||||
		if (pkt_len > skb->len)
 | 
			
		||||
			return 0;
 | 
			
		||||
 | 
			
		||||
		/* Check CRC or runt packet */
 | 
			
		||||
		if ((*pkt_hdr & AX_RXHDR_CRC_ERR) ||
 | 
			
		||||
		    (*pkt_hdr & AX_RXHDR_DROP_ERR)) {
 | 
			
		||||
			skb_pull(skb, (pkt_len + 7) & 0xFFF8);
 | 
			
		||||
			pkt_hdr++;
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		if (((*pkt_hdr & (AX_RXHDR_CRC_ERR | AX_RXHDR_DROP_ERR)) == 0) &&
 | 
			
		||||
		    pkt_len >= 2 + ETH_HLEN) {
 | 
			
		||||
			bool last = (pkt_cnt == 0);
 | 
			
		||||
 | 
			
		||||
		if (pkt_cnt == 0) {
 | 
			
		||||
			skb->len = pkt_len;
 | 
			
		||||
			/* Skip IP alignment pseudo header */
 | 
			
		||||
			skb_pull(skb, 2);
 | 
			
		||||
			skb_set_tail_pointer(skb, skb->len);
 | 
			
		||||
			skb->truesize = pkt_len + sizeof(struct sk_buff);
 | 
			
		||||
			ax88179_rx_checksum(skb, pkt_hdr);
 | 
			
		||||
			return 1;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		ax_skb = skb_clone(skb, GFP_ATOMIC);
 | 
			
		||||
		if (ax_skb) {
 | 
			
		||||
			if (last) {
 | 
			
		||||
				ax_skb = skb;
 | 
			
		||||
			} else {
 | 
			
		||||
				ax_skb = skb_clone(skb, GFP_ATOMIC);
 | 
			
		||||
				if (!ax_skb)
 | 
			
		||||
					return 0;
 | 
			
		||||
			}
 | 
			
		||||
			ax_skb->len = pkt_len;
 | 
			
		||||
			/* Skip IP alignment pseudo header */
 | 
			
		||||
			skb_pull(ax_skb, 2);
 | 
			
		||||
			skb_set_tail_pointer(ax_skb, ax_skb->len);
 | 
			
		||||
			ax_skb->truesize = pkt_len + sizeof(struct sk_buff);
 | 
			
		||||
			ax88179_rx_checksum(ax_skb, pkt_hdr);
 | 
			
		||||
 | 
			
		||||
			if (last)
 | 
			
		||||
				return 1;
 | 
			
		||||
 | 
			
		||||
			usbnet_skb_return(dev, ax_skb);
 | 
			
		||||
		} else {
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		skb_pull(skb, (pkt_len + 7) & 0xFFF8);
 | 
			
		||||
		pkt_hdr++;
 | 
			
		||||
		/* Trim this packet away from the SKB */
 | 
			
		||||
		if (!skb_pull(skb, (pkt_len + 7) & 0xFFF8))
 | 
			
		||||
			return 0;
 | 
			
		||||
	}
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct sk_buff *
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue