Blob Blame History Raw
From ad4757373f2ade3e1b238c8967d1b5bf80654820 Mon Sep 17 00:00:00 2001
From: Yannick Cote <ycote@redhat.com>
Date: Wed, 7 Dec 2022 14:07:55 -0500
Subject: [KPATCH CVE-2022-2964] kpatch fixes for CVE-2022-2964

Kernels:
3.10.0-1160.66.1.el7
3.10.0-1160.71.1.el7
3.10.0-1160.76.1.el7
3.10.0-1160.80.1.el7


Kpatch-MR: https://gitlab.com/redhat/prdsc/rhel/src/kpatch/rhel-7/-/merge_requests/47
Approved-by: Joe Lawrence (@joe.lawrence)
Changes since last build:
arches: x86_64 ppc64le
ax88179_178a.o: changed function: ax88179_rx_fixup
ax88179_178a.o: changed function: ax88179_tx_fixup
---------------------------

Modifications: none

commit 6887c693d5b263d458cdae235b5ca4cb36d79466
Author: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>
Date:   Thu Oct 6 18:32:21 2022 +0200

    net: usb: ax88179_178a: fix packet alignment padding

    Bugzilla: https://bugzilla.redhat.com/2120504
    CVE: CVE-2022-2964

    commit e869e7a17798d85829fa7d4f9bbe1eebd4b2d3f6
    Author: Jeremy Kerr <jk@ozlabs.org>
    Date:   Mon Jun 15 10:54:56 2020 +0800

        net: usb: ax88179_178a: fix packet alignment padding

        Using a AX88179 device (0b95:1790), I see two bytes of appended data on
        every RX packet. For example, this 48-byte ping, using 0xff as a
        payload byte:

          04:20:22.528472 IP 192.168.1.1 > 192.168.1.2: ICMP echo request, id 2447, seq 1, length 64
                0x0000:  000a cd35 ea50 000a cd35 ea4f 0800 4500
                0x0010:  0054 c116 4000 4001 f63e c0a8 0101 c0a8
                0x0020:  0102 0800 b633 098f 0001 87ea cd5e 0000
                0x0030:  0000 dcf2 0600 0000 0000 ffff ffff ffff
                0x0040:  ffff ffff ffff ffff ffff ffff ffff ffff
                0x0050:  ffff ffff ffff ffff ffff ffff ffff ffff
                0x0060:  ffff 961f

        Those last two bytes - 96 1f - aren't part of the original packet.

        In the ax88179 RX path, the usbnet rx_fixup function trims a 2-byte
        'alignment pseudo header' from the start of the packet, and sets the
        length from a per-packet field populated by hardware. It looks like that
        length field *includes* the 2-byte header; the current driver assumes
        that it's excluded.

        This change trims the 2-byte alignment header after we've set the packet
        length, so the resulting packet length is correct. While we're moving
        the comment around, this also fixes the spelling of 'pseudo'.

        Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
        Signed-off-by: David S. Miller <davem@davemloft.net>

    Signed-off-by: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>

commit cb4cf1dd8820ba776a91ebf5b88ad1c3d83a0dfb
Author: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>
Date:   Thu Oct 6 18:32:21 2022 +0200

    ax88179_178a: Merge memcpy + le32_to_cpus to get_unaligned_le32

    Bugzilla: https://bugzilla.redhat.com/2120504
    CVE: CVE-2022-2964

    commit d1854d509d61d36af44f2130423bff8836e1592e
    Author: Chuhong Yuan <hslester96@gmail.com>
    Date:   Fri Jul 19 17:07:15 2019 +0800

        ax88179_178a: Merge memcpy + le32_to_cpus to get_unaligned_le32

        Merge the combo use of memcpy and le32_to_cpus.
        Use get_unaligned_le32 instead.
        This simplifies the code.

        Signed-off-by: Chuhong Yuan <hslester96@gmail.com>
        Signed-off-by: David S. Miller <davem@davemloft.net>

    Signed-off-by: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>

commit 4e6ea26989123e731329323492d30f826aa28010
Author: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>
Date:   Thu Oct 6 18:32:21 2022 +0200

    net: usb: Merge cpu_to_le32s + memcpy to put_unaligned_le32

    Bugzilla: https://bugzilla.redhat.com/2120504
    CVE: CVE-2022-2964

    Conflicts:
        Only ax88179 files

    commit 7e24b4ed5ac4321e41415b0c6f0f8a8ac14852b2
    Author: Chuhong Yuan <hslester96@gmail.com>
    Date:   Mon Jul 22 15:41:34 2019 +0800

        net: usb: Merge cpu_to_le32s + memcpy to put_unaligned_le32

        Merge the combo uses of cpu_to_le32s and memcpy.
        Use put_unaligned_le32 instead.
        This simplifies the code.

        Signed-off-by: Chuhong Yuan <hslester96@gmail.com>
        Signed-off-by: David S. Miller <davem@davemloft.net>

    Signed-off-by: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>

commit 7faaf2665dcbc3a4fdb31331de699045040ed213
Author: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>
Date:   Thu Oct 6 18:32:22 2022 +0200

    net: usb: ax88179_178a: Fix out-of-bounds accesses in RX fixup

    Bugzilla: https://bugzilla.redhat.com/2120504
    CVE: CVE-2022-2964

    commit 57bc3d3ae8c14df3ceb4e17d26ddf9eeab304581
    Author: Jann Horn <jannh@google.com>
    Date:   Wed Jan 26 14:14:52 2022 +0100

        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: e2ca90c276e1 ("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>

    Signed-off-by: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>

commit 4e8b1e4091c6dce2911ffe8b9a8a7c58f8b028bc
Author: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>
Date:   Thu Nov 17 08:53:37 2022 +0100

    net: usb: ax88179_178a: Fix packet receiving

    Bugzilla: https://bugzilla.redhat.com/2120504
    CVE: CVE-2022-2964

    commit f8ebb3ac881b17712e1d5967c97ab1806b16d3d6
    Author: Jose Alonso <joalonsof@gmail.com>
    Date:   Tue Jun 28 12:13:02 2022 -0300

        net: usb: ax88179_178a: Fix packet receiving

        This patch corrects packet receiving in ax88179_rx_fixup.

        - problem observed:
          ifconfig shows allways a lot of 'RX Errors' while packets
          are received normally.

          This occurs because ax88179_rx_fixup does not recognise properly
          the usb urb received.
          The packets are normally processed and at the end, the code exits
          with 'return 0', generating RX Errors.
          (pkt_cnt==-2 and ptk_hdr over field rx_hdr trying to identify
           another packet there)

          This is a usb urb received by "tcpdump -i usbmon2 -X" on a
          little-endian CPU:
          0x0000:  eeee f8e3 3b19 87a0 94de 80e3 daac 0800
                   ^         packet 1 start (pkt_len = 0x05ec)
                   ^^^^      IP alignment pseudo header
                        ^    ethernet packet start
                   last byte ethernet packet   v
                   padding (8-bytes aligned)     vvvv vvvv
          0x05e0:  c92d d444 1420 8a69 83dd 272f e82b 9811
          0x05f0:  eeee f8e3 3b19 87a0 94de 80e3 daac 0800
          ...      ^ packet 2
          0x0be0:  eeee f8e3 3b19 87a0 94de 80e3 daac 0800
          ...
          0x1130:  9d41 9171 8a38 0ec5 eeee f8e3 3b19 87a0
          ...
          0x1720:  8cfc 15ff 5e4c e85c eeee f8e3 3b19 87a0
          ...
          0x1d10:  ecfa 2a3a 19ab c78c eeee f8e3 3b19 87a0
          ...
          0x2070:  eeee f8e3 3b19 87a0 94de 80e3 daac 0800
          ...      ^ packet 7
          0x2120:  7c88 4ca5 5c57 7dcc 0d34 7577 f778 7e0a
          0x2130:  f032 e093 7489 0740 3008 ec05 0000 0080
                                       ====1==== ====2====
                   hdr_off             ^
                   pkt_len = 0x05ec         ^^^^
                   AX_RXHDR_*=0x00830  ^^^^   ^
                   pkt_len = 0                        ^^^^
                   AX_RXHDR_DROP_ERR=0x80000000  ^^^^   ^
          0x2140:  3008 ec05 0000 0080 3008 5805 0000 0080
          0x2150:  3008 ec05 0000 0080 3008 ec05 0000 0080
          0x2160:  3008 5803 0000 0080 3008 c800 0000 0080
                   ===11==== ===12==== ===13==== ===14====
          0x2170:  0000 0000 0e00 3821
                             ^^^^ ^^^^ rx_hdr
                             ^^^^      pkt_cnt=14
                                  ^^^^ hdr_off=0x2138
                   ^^^^ ^^^^           padding

          The dump shows that pkt_cnt is the number of entrys in the
          per-packet metadata. It is "2 * packet count".
          Each packet have two entrys. The first have a valid
          value (pkt_len and AX_RXHDR_*) and the second have a
          dummy-header 0x80000000 (pkt_len=0 with AX_RXHDR_DROP_ERR).
          Why exists dummy-header for each packet?!?
          My guess is that this was done probably to align the
          entry for each packet to 64-bits and maintain compatibility
          with old firmware.
          There is also a padding (0x00000000) before the rx_hdr to
          align the end of rx_hdr to 64-bit.
          Note that packets have a alignment of 64-bits (8-bytes).

          This patch assumes that the dummy-header and the last
          padding are optional. So it preserves semantics and
          recognises the same valid packets as the current code.

          This patch was made using only the dumpfile information and
          tested with only one device:
          0b95:1790 ASIX Electronics Corp. AX88179 Gigabit Ethernet

        Fixes: 57bc3d3ae8c1 ("net: usb: ax88179_178a: Fix out-of-bounds accesses in RX fixup")
        Fixes: e2ca90c276e1 ("ax88179_178a: ASIX AX88179_178A USB 3.0/2.0 to gigabit ethernet adapter driver")
        Signed-off-by: Jose Alonso <joalonsof@gmail.com>
        Acked-by: Paolo Abeni <pabeni@redhat.com>
        Link: https://lore.kernel.org/r/d6970bb04bf67598af4d316eaeb1792040b18cfd.camel@gmail.com
        Signed-off-by: Paolo Abeni <pabeni@redhat.com>

    Signed-off-by: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>

Signed-off-by: Yannick Cote <ycote@redhat.com>
---
 drivers/net/usb/ax88179_178a.c | 128 ++++++++++++++++++++++++---------
 1 file changed, 93 insertions(+), 35 deletions(-)

diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c
index 7f496ccdd272..1940f31facf0 100644
--- a/drivers/net/usb/ax88179_178a.c
+++ b/drivers/net/usb/ax88179_178a.c
@@ -1375,58 +1375,119 @@ 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).
+	 */
+
+	/* SKB contents for current firmware:
+	 *   <packet 1> <padding>
+	 *   ...
+	 *   <packet N> <padding>
+	 *   <per-packet metadata entry 1> <dummy header>
+	 *   ...
+	 *   <per-packet metadata entry N> <dummy header>
+	 *   <padding2> <rx_hdr>
+	 *
+	 * where:
+	 *   <packet N> contains pkt_len bytes:
+	 *		2 bytes of IP alignment pseudo header
+	 *		packet received
+	 *   <per-packet metadata entry N> contains 4 bytes:
+	 *		pkt_len and fields AX_RXHDR_*
+	 *   <padding>	0-7 bytes to terminate at
+	 *		8 bytes boundary (64-bit).
+	 *   <padding2> 4 bytes to make rx_hdr terminate at
+	 *		8 bytes boundary (64-bit)
+	 *   <dummy-header> contains 4 bytes:
+	 *		pkt_len=0 and AX_RXHDR_DROP_ERR
+	 *   <rx-hdr>	contains 4 bytes:
+	 *		pkt_cnt and hdr_off (offset of
+	 *		  <per-packet metadata entry 1>)
+	 *
+	 * pkt_cnt is number of entrys in the per-packet metadata.
+	 * In current firmware there is 2 entrys per packet.
+	 * The first points to the packet and the
+	 *  second is a dummy header.
+	 * This was done probably to align fields in 64-bit and
+	 *  maintain compatibility with old firmware.
+	 * This code assumes that <dummy header> and <padding2> are
+	 *  optional.
+	 */
+
+	if (skb->len < 4)
 		return 0;
-
 	skb_trim(skb, skb->len - 4);
-	memcpy(&rx_hdr, skb_tail_pointer(skb), 4);
-	le32_to_cpus(&rx_hdr);
-
+	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 * 4 + 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 > 0; pkt_cnt--, pkt_hdr++) {
+		u16 pkt_len_plus_padd;
 		u16 pkt_len;
 
 		le32_to_cpus(pkt_hdr);
 		pkt_len = (*pkt_hdr >> 16) & 0x1fff;
+		pkt_len_plus_padd = (pkt_len + 7) & 0xfff8;
+
+		/* Skip dummy header used for alignment
+		 */
+		if (pkt_len == 0)
+			continue;
+
+		if (pkt_len_plus_padd > 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++;
+		if ((*pkt_hdr & (AX_RXHDR_CRC_ERR | AX_RXHDR_DROP_ERR)) ||
+		    pkt_len < 2 + ETH_HLEN) {
+			dev->net->stats.rx_errors++;
+			skb_pull(skb, pkt_len_plus_padd);
 			continue;
 		}
 
-		if (pkt_cnt == 0) {
-			/* Skip IP alignment psudo header */
+		/* last packet */
+		if (pkt_len_plus_padd == skb->len) {
+			skb_trim(skb, pkt_len);
+
+			/* Skip IP alignment pseudo header */
 			skb_pull(skb, 2);
-			skb->len = pkt_len;
-			skb_set_tail_pointer(skb, pkt_len);
-			skb->truesize = pkt_len + sizeof(struct sk_buff);
+
+			skb->truesize = SKB_TRUESIZE(pkt_len_plus_padd);
 			ax88179_rx_checksum(skb, pkt_hdr);
 			return 1;
 		}
 
 		ax_skb = skb_clone(skb, GFP_ATOMIC);
-		if (ax_skb) {
-			ax_skb->len = pkt_len;
-			ax_skb->data = skb->data + 2;
-			skb_set_tail_pointer(ax_skb, pkt_len);
-			ax_skb->truesize = pkt_len + sizeof(struct sk_buff);
-			ax88179_rx_checksum(ax_skb, pkt_hdr);
-			usbnet_skb_return(dev, ax_skb);
-		} else {
+		if (!ax_skb)
 			return 0;
-		}
+		skb_trim(ax_skb, pkt_len);
 
-		skb_pull(skb, (pkt_len + 7) & 0xFFF8);
-		pkt_hdr++;
+		/* Skip IP alignment pseudo header */
+		skb_pull(ax_skb, 2);
+
+		skb->truesize = pkt_len_plus_padd +
+				SKB_DATA_ALIGN(sizeof(struct sk_buff));
+		ax88179_rx_checksum(ax_skb, pkt_hdr);
+		usbnet_skb_return(dev, ax_skb);
+
+		skb_pull(skb, pkt_len_plus_padd);
 	}
-	return 1;
+
+	return 0;
 }
 
 static struct sk_buff *
@@ -1436,6 +1497,7 @@ ax88179_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
 	int frame_size = dev->maxpacket;
 	int mss = skb_shinfo(skb)->gso_size;
 	int headroom;
+	void *ptr;
 
 	tx_hdr1 = skb->len;
 	tx_hdr2 = mss;
@@ -1450,13 +1512,9 @@ ax88179_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
 		return NULL;
 	}
 
-	skb_push(skb, 4);
-	cpu_to_le32s(&tx_hdr2);
-	skb_copy_to_linear_data(skb, &tx_hdr2, 4);
-
-	skb_push(skb, 4);
-	cpu_to_le32s(&tx_hdr1);
-	skb_copy_to_linear_data(skb, &tx_hdr1, 4);
+	ptr = skb_push(skb, 8);
+	put_unaligned_le32(tx_hdr1, ptr);
+	put_unaligned_le32(tx_hdr2, ptr + 4);
 
 	return skb;
 }
-- 
2.39.0