Skip to content

Commit 57bc3d3

Browse files
thejhgregkh
authored andcommitted
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: e2ca90c ("ax88179_178a: ASIX AX88179_178A USB 3.0/2.0 to gigabit ethernet adapter driver") Cc: [email protected] Signed-off-by: Jann Horn <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 117b4e9 commit 57bc3d3

File tree

1 file changed

+39
-29
lines changed

1 file changed

+39
-29
lines changed

drivers/net/usb/ax88179_178a.c

Lines changed: 39 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1468,58 +1468,68 @@ static int ax88179_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
14681468
u16 hdr_off;
14691469
u32 *pkt_hdr;
14701470

1471-
/* This check is no longer done by usbnet */
1472-
if (skb->len < dev->net->hard_header_len)
1471+
/* At the end of the SKB, there's a header telling us how many packets
1472+
* are bundled into this buffer and where we can find an array of
1473+
* per-packet metadata (which contains elements encoded into u16).
1474+
*/
1475+
if (skb->len < 4)
14731476
return 0;
1474-
14751477
skb_trim(skb, skb->len - 4);
14761478
rx_hdr = get_unaligned_le32(skb_tail_pointer(skb));
1477-
14781479
pkt_cnt = (u16)rx_hdr;
14791480
hdr_off = (u16)(rx_hdr >> 16);
1481+
1482+
if (pkt_cnt == 0)
1483+
return 0;
1484+
1485+
/* Make sure that the bounds of the metadata array are inside the SKB
1486+
* (and in front of the counter at the end).
1487+
*/
1488+
if (pkt_cnt * 2 + hdr_off > skb->len)
1489+
return 0;
14801490
pkt_hdr = (u32 *)(skb->data + hdr_off);
14811491

1482-
while (pkt_cnt--) {
1492+
/* Packets must not overlap the metadata array */
1493+
skb_trim(skb, hdr_off);
1494+
1495+
for (; ; pkt_cnt--, pkt_hdr++) {
14831496
u16 pkt_len;
14841497

14851498
le32_to_cpus(pkt_hdr);
14861499
pkt_len = (*pkt_hdr >> 16) & 0x1fff;
14871500

1488-
/* Check CRC or runt packet */
1489-
if ((*pkt_hdr & AX_RXHDR_CRC_ERR) ||
1490-
(*pkt_hdr & AX_RXHDR_DROP_ERR)) {
1491-
skb_pull(skb, (pkt_len + 7) & 0xFFF8);
1492-
pkt_hdr++;
1493-
continue;
1494-
}
1495-
1496-
if (pkt_cnt == 0) {
1497-
skb->len = pkt_len;
1498-
/* Skip IP alignment pseudo header */
1499-
skb_pull(skb, 2);
1500-
skb_set_tail_pointer(skb, skb->len);
1501-
skb->truesize = pkt_len + sizeof(struct sk_buff);
1502-
ax88179_rx_checksum(skb, pkt_hdr);
1503-
return 1;
1504-
}
1501+
if (pkt_len > skb->len)
1502+
return 0;
15051503

1506-
ax_skb = skb_clone(skb, GFP_ATOMIC);
1507-
if (ax_skb) {
1504+
/* Check CRC or runt packet */
1505+
if (((*pkt_hdr & (AX_RXHDR_CRC_ERR | AX_RXHDR_DROP_ERR)) == 0) &&
1506+
pkt_len >= 2 + ETH_HLEN) {
1507+
bool last = (pkt_cnt == 0);
1508+
1509+
if (last) {
1510+
ax_skb = skb;
1511+
} else {
1512+
ax_skb = skb_clone(skb, GFP_ATOMIC);
1513+
if (!ax_skb)
1514+
return 0;
1515+
}
15081516
ax_skb->len = pkt_len;
15091517
/* Skip IP alignment pseudo header */
15101518
skb_pull(ax_skb, 2);
15111519
skb_set_tail_pointer(ax_skb, ax_skb->len);
15121520
ax_skb->truesize = pkt_len + sizeof(struct sk_buff);
15131521
ax88179_rx_checksum(ax_skb, pkt_hdr);
1522+
1523+
if (last)
1524+
return 1;
1525+
15141526
usbnet_skb_return(dev, ax_skb);
1515-
} else {
1516-
return 0;
15171527
}
15181528

1519-
skb_pull(skb, (pkt_len + 7) & 0xFFF8);
1520-
pkt_hdr++;
1529+
/* Trim this packet away from the SKB */
1530+
if (!skb_pull(skb, (pkt_len + 7) & 0xFFF8))
1531+
return 0;
15211532
}
1522-
return 1;
15231533
}
15241534

15251535
static struct sk_buff *

0 commit comments

Comments
 (0)