QEMU is a FAST! processor emulator
CentOS Sources
2015-09-15 b2d9fbd285a6bb720293ca1612f5bef98c2a9216
import qemu-kvm-1.5.3-86.el7_1.6
7 files added
1 files modified
748 ■■■■■ changed files
SOURCES/kvm-rtl8139-avoid-nested-ifs-in-IP-header-parsing-CVE-20.patch 92 ●●●●● patch | view | raw | blame | history
SOURCES/kvm-rtl8139-check-IP-Header-Length-field-CVE-2015-5165.patch 62 ●●●●● patch | view | raw | blame | history
SOURCES/kvm-rtl8139-check-IP-Total-Length-field-CVE-2015-5165.patch 43 ●●●●● patch | view | raw | blame | history
SOURCES/kvm-rtl8139-check-TCP-Data-Offset-field-CVE-2015-5165.patch 41 ●●●●● patch | view | raw | blame | history
SOURCES/kvm-rtl8139-drop-tautologous-if-ip-.-statement-CVE-2015-.patch 383 ●●●●● patch | view | raw | blame | history
SOURCES/kvm-rtl8139-skip-offload-on-short-Ethernet-IP-header-CVE.patch 49 ●●●●● patch | view | raw | blame | history
SOURCES/kvm-rtl8139-skip-offload-on-short-TCP-header-CVE-2015-51.patch 44 ●●●●● patch | view | raw | blame | history
SPECS/qemu-kvm.spec 34 ●●●●● patch | view | raw | blame | history
SOURCES/kvm-rtl8139-avoid-nested-ifs-in-IP-header-parsing-CVE-20.patch
New file
@@ -0,0 +1,92 @@
From ceffa7fb93c4ea570a0c854974a4993eedea52ca Mon Sep 17 00:00:00 2001
From: Stefan Hajnoczi <stefanha@redhat.com>
Date: Fri, 31 Jul 2015 15:59:42 +0200
Subject: [PATCH 1/7] rtl8139: avoid nested ifs in IP header parsing
 (CVE-2015-5165)
Message-id: <1438358388-10575-2-git-send-email-stefanha@redhat.com>
Patchwork-id: 67236
O-Subject: [RHEL-7.1.z qemu-kvm EMBARGOED PATCH 1/7] rtl8139: avoid nested ifs in IP header parsing (CVE-2015-5165)
Bugzilla: 1248764
RH-Acked-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
RH-Acked-by: Xiao Wang <jasowang@redhat.com>
RH-Acked-by: Laszlo Ersek <lersek@redhat.com>
Transmit offload needs to parse packet headers.  If header fields have
unexpected values the offload processing is skipped.
The code currently uses nested ifs because there is relatively little
input validation.  The next patches will add missing input validation
and a goto label is more appropriate to avoid deep if statement nesting.
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
---
 hw/net/rtl8139.c | 41 ++++++++++++++++++++++-------------------
 1 file changed, 22 insertions(+), 19 deletions(-)
diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c
index d08106b..e3b594f 100644
--- a/hw/net/rtl8139.c
+++ b/hw/net/rtl8139.c
@@ -2152,28 +2152,30 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s)
             size_t   eth_payload_len  = 0;
             int proto = be16_to_cpu(*(uint16_t *)(saved_buffer + 12));
-            if (proto == ETH_P_IP)
+            if (proto != ETH_P_IP)
             {
-                DPRINTF("+++ C+ mode has IP packet\n");
-
-                /* not aligned */
-                eth_payload_data = saved_buffer + ETH_HLEN;
-                eth_payload_len  = saved_size   - ETH_HLEN;
-
-                ip = (ip_header*)eth_payload_data;
-
-                if (IP_HEADER_VERSION(ip) != IP_HEADER_VERSION_4) {
-                    DPRINTF("+++ C+ mode packet has bad IP version %d "
-                        "expected %d\n", IP_HEADER_VERSION(ip),
-                        IP_HEADER_VERSION_4);
-                    ip = NULL;
-                } else {
-                    hlen = IP_HEADER_LENGTH(ip);
-                    ip_protocol = ip->ip_p;
-                    ip_data_len = be16_to_cpu(ip->ip_len) - hlen;
-                }
+                goto skip_offload;
             }
+            DPRINTF("+++ C+ mode has IP packet\n");
+
+            /* not aligned */
+            eth_payload_data = saved_buffer + ETH_HLEN;
+            eth_payload_len  = saved_size   - ETH_HLEN;
+
+            ip = (ip_header*)eth_payload_data;
+
+            if (IP_HEADER_VERSION(ip) != IP_HEADER_VERSION_4) {
+                DPRINTF("+++ C+ mode packet has bad IP version %d "
+                    "expected %d\n", IP_HEADER_VERSION(ip),
+                    IP_HEADER_VERSION_4);
+                goto skip_offload;
+            }
+
+            hlen = IP_HEADER_LENGTH(ip);
+            ip_protocol = ip->ip_p;
+            ip_data_len = be16_to_cpu(ip->ip_len) - hlen;
+
             if (ip)
             {
                 if (txdw0 & CP_TX_IPCS)
@@ -2369,6 +2371,7 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s)
             }
         }
+skip_offload:
         /* update tally counter */
         ++s->tally_counters.TxOk;
--
1.8.3.1
SOURCES/kvm-rtl8139-check-IP-Header-Length-field-CVE-2015-5165.patch
New file
@@ -0,0 +1,62 @@
From 230b373704579a17a7a7e6890fe0dc1b789a2fa4 Mon Sep 17 00:00:00 2001
From: Stefan Hajnoczi <stefanha@redhat.com>
Date: Fri, 31 Jul 2015 15:59:45 +0200
Subject: [PATCH 4/7] rtl8139: check IP Header Length field (CVE-2015-5165)
Message-id: <1438358388-10575-5-git-send-email-stefanha@redhat.com>
Patchwork-id: 67239
O-Subject: [RHEL-7.1.z qemu-kvm EMBARGOED PATCH 4/7] rtl8139: check IP Header Length field (CVE-2015-5165)
Bugzilla: 1248764
RH-Acked-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
RH-Acked-by: Xiao Wang <jasowang@redhat.com>
RH-Acked-by: Laszlo Ersek <lersek@redhat.com>
The IP Header Length field was only checked in the IP checksum case, but
is used in other cases too.
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
---
 hw/net/rtl8139.c | 19 ++++++++-----------
 1 file changed, 8 insertions(+), 11 deletions(-)
diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c
index 409029d..7efc0b5 100644
--- a/hw/net/rtl8139.c
+++ b/hw/net/rtl8139.c
@@ -2178,6 +2178,10 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s)
             }
             hlen = IP_HEADER_LENGTH(ip);
+            if (hlen < sizeof(ip_header) || hlen > eth_payload_len) {
+                goto skip_offload;
+            }
+
             ip_protocol = ip->ip_p;
             ip_data_len = be16_to_cpu(ip->ip_len) - hlen;
@@ -2185,17 +2189,10 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s)
             {
                 DPRINTF("+++ C+ mode need IP checksum\n");
-                if (hlen<sizeof(ip_header) || hlen>eth_payload_len) {/* min header length */
-                    /* bad packet header len */
-                    /* or packet too short */
-                }
-                else
-                {
-                    ip->ip_sum = 0;
-                    ip->ip_sum = ip_checksum(ip, hlen);
-                    DPRINTF("+++ C+ mode IP header len=%d checksum=%04x\n",
-                        hlen, ip->ip_sum);
-                }
+                ip->ip_sum = 0;
+                ip->ip_sum = ip_checksum(ip, hlen);
+                DPRINTF("+++ C+ mode IP header len=%d checksum=%04x\n",
+                    hlen, ip->ip_sum);
             }
             if ((txdw0 & CP_TX_LGSEN) && ip_protocol == IP_PROTO_TCP)
--
1.8.3.1
SOURCES/kvm-rtl8139-check-IP-Total-Length-field-CVE-2015-5165.patch
New file
@@ -0,0 +1,43 @@
From 32e2f1ebf0d979c5ccc7e934ea9729e956d6aa52 Mon Sep 17 00:00:00 2001
From: Stefan Hajnoczi <stefanha@redhat.com>
Date: Fri, 31 Jul 2015 15:59:46 +0200
Subject: [PATCH 5/7] rtl8139: check IP Total Length field (CVE-2015-5165)
Message-id: <1438358388-10575-6-git-send-email-stefanha@redhat.com>
Patchwork-id: 67240
O-Subject: [RHEL-7.1.z qemu-kvm EMBARGOED PATCH 5/7] rtl8139: check IP Total Length field (CVE-2015-5165)
Bugzilla: 1248764
RH-Acked-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
RH-Acked-by: Xiao Wang <jasowang@redhat.com>
RH-Acked-by: Laszlo Ersek <lersek@redhat.com>
The IP Total Length field includes the IP header and data.  Make sure it
is valid and does not exceed the Ethernet payload size.
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
---
 hw/net/rtl8139.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c
index 7efc0b5..61bc34c 100644
--- a/hw/net/rtl8139.c
+++ b/hw/net/rtl8139.c
@@ -2183,7 +2183,12 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s)
             }
             ip_protocol = ip->ip_p;
-            ip_data_len = be16_to_cpu(ip->ip_len) - hlen;
+
+            ip_data_len = be16_to_cpu(ip->ip_len);
+            if (ip_data_len < hlen || ip_data_len > eth_payload_len) {
+                goto skip_offload;
+            }
+            ip_data_len -= hlen;
             if (txdw0 & CP_TX_IPCS)
             {
--
1.8.3.1
SOURCES/kvm-rtl8139-check-TCP-Data-Offset-field-CVE-2015-5165.patch
New file
@@ -0,0 +1,41 @@
From a8cef763585fecc8b89d006efca0e000df923d36 Mon Sep 17 00:00:00 2001
From: Stefan Hajnoczi <stefanha@redhat.com>
Date: Fri, 31 Jul 2015 15:59:48 +0200
Subject: [PATCH 7/7] rtl8139: check TCP Data Offset field (CVE-2015-5165)
Message-id: <1438358388-10575-8-git-send-email-stefanha@redhat.com>
Patchwork-id: 67242
O-Subject: [RHEL-7.1.z qemu-kvm EMBARGOED PATCH 7/7] rtl8139: check TCP Data Offset field (CVE-2015-5165)
Bugzilla: 1248764
RH-Acked-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
RH-Acked-by: Xiao Wang <jasowang@redhat.com>
RH-Acked-by: Laszlo Ersek <lersek@redhat.com>
The TCP Data Offset field contains the length of the header.  Make sure
it is valid and does not exceed the IP data length.
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
---
 hw/net/rtl8139.c | 5 +++++
 1 file changed, 5 insertions(+)
diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c
index c9bf454..4f89328 100644
--- a/hw/net/rtl8139.c
+++ b/hw/net/rtl8139.c
@@ -2231,6 +2231,11 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s)
                 int tcp_hlen = TCP_HEADER_DATA_OFFSET(p_tcp_hdr);
+                /* Invalid TCP data offset? */
+                if (tcp_hlen < sizeof(tcp_header) || tcp_hlen > ip_data_len) {
+                    goto skip_offload;
+                }
+
                 /* ETH_MTU = ip header len + tcp header len + payload */
                 int tcp_data_len = ip_data_len - tcp_hlen;
                 int tcp_chunk_size = ETH_MTU - hlen - tcp_hlen;
--
1.8.3.1
SOURCES/kvm-rtl8139-drop-tautologous-if-ip-.-statement-CVE-2015-.patch
New file
@@ -0,0 +1,383 @@
From 95ea90bc29b45f9b07b55c6cbb396fe854199079 Mon Sep 17 00:00:00 2001
From: Stefan Hajnoczi <stefanha@redhat.com>
Date: Fri, 31 Jul 2015 15:59:43 +0200
Subject: [PATCH 2/7] rtl8139: drop tautologous if (ip) {...} statement
 (CVE-2015-5165)
Message-id: <1438358388-10575-3-git-send-email-stefanha@redhat.com>
Patchwork-id: 67238
O-Subject: [RHEL-7.1.z qemu-kvm EMBARGOED PATCH 2/7] rtl8139: drop tautologous if (ip) {...} statement (CVE-2015-5165)
Bugzilla: 1248764
RH-Acked-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
RH-Acked-by: Xiao Wang <jasowang@redhat.com>
RH-Acked-by: Laszlo Ersek <lersek@redhat.com>
The previous patch stopped using the ip pointer as an indicator that the
IP header is present.  When we reach the if (ip) {...} statement we know
ip is always non-NULL.
Remove the if statement to reduce nesting.
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
---
 hw/net/rtl8139.c | 305 +++++++++++++++++++++++++++----------------------------
 1 file changed, 151 insertions(+), 154 deletions(-)
diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c
index e3b594f..ab32714 100644
--- a/hw/net/rtl8139.c
+++ b/hw/net/rtl8139.c
@@ -2176,198 +2176,195 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s)
             ip_protocol = ip->ip_p;
             ip_data_len = be16_to_cpu(ip->ip_len) - hlen;
-            if (ip)
+            if (txdw0 & CP_TX_IPCS)
             {
-                if (txdw0 & CP_TX_IPCS)
-                {
-                    DPRINTF("+++ C+ mode need IP checksum\n");
+                DPRINTF("+++ C+ mode need IP checksum\n");
-                    if (hlen<sizeof(ip_header) || hlen>eth_payload_len) {/* min header length */
-                        /* bad packet header len */
-                        /* or packet too short */
-                    }
-                    else
-                    {
-                        ip->ip_sum = 0;
-                        ip->ip_sum = ip_checksum(ip, hlen);
-                        DPRINTF("+++ C+ mode IP header len=%d checksum=%04x\n",
-                            hlen, ip->ip_sum);
-                    }
+                if (hlen<sizeof(ip_header) || hlen>eth_payload_len) {/* min header length */
+                    /* bad packet header len */
+                    /* or packet too short */
                 }
-
-                if ((txdw0 & CP_TX_LGSEN) && ip_protocol == IP_PROTO_TCP)
+                else
                 {
-                    int large_send_mss = (txdw0 >> 16) & CP_TC_LGSEN_MSS_MASK;
+                    ip->ip_sum = 0;
+                    ip->ip_sum = ip_checksum(ip, hlen);
+                    DPRINTF("+++ C+ mode IP header len=%d checksum=%04x\n",
+                        hlen, ip->ip_sum);
+                }
+            }
-                    DPRINTF("+++ C+ mode offloaded task TSO MTU=%d IP data %d "
-                        "frame data %d specified MSS=%d\n", ETH_MTU,
-                        ip_data_len, saved_size - ETH_HLEN, large_send_mss);
+            if ((txdw0 & CP_TX_LGSEN) && ip_protocol == IP_PROTO_TCP)
+            {
+                int large_send_mss = (txdw0 >> 16) & CP_TC_LGSEN_MSS_MASK;
-                    int tcp_send_offset = 0;
-                    int send_count = 0;
+                DPRINTF("+++ C+ mode offloaded task TSO MTU=%d IP data %d "
+                    "frame data %d specified MSS=%d\n", ETH_MTU,
+                    ip_data_len, saved_size - ETH_HLEN, large_send_mss);
-                    /* maximum IP header length is 60 bytes */
-                    uint8_t saved_ip_header[60];
+                int tcp_send_offset = 0;
+                int send_count = 0;
-                    /* save IP header template; data area is used in tcp checksum calculation */
-                    memcpy(saved_ip_header, eth_payload_data, hlen);
+                /* maximum IP header length is 60 bytes */
+                uint8_t saved_ip_header[60];
-                    /* a placeholder for checksum calculation routine in tcp case */
-                    uint8_t *data_to_checksum     = eth_payload_data + hlen - 12;
-                    //                    size_t   data_to_checksum_len = eth_payload_len  - hlen + 12;
+                /* save IP header template; data area is used in tcp checksum calculation */
+                memcpy(saved_ip_header, eth_payload_data, hlen);
-                    /* pointer to TCP header */
-                    tcp_header *p_tcp_hdr = (tcp_header*)(eth_payload_data + hlen);
+                /* a placeholder for checksum calculation routine in tcp case */
+                uint8_t *data_to_checksum     = eth_payload_data + hlen - 12;
+                //                    size_t   data_to_checksum_len = eth_payload_len  - hlen + 12;
-                    int tcp_hlen = TCP_HEADER_DATA_OFFSET(p_tcp_hdr);
+                /* pointer to TCP header */
+                tcp_header *p_tcp_hdr = (tcp_header*)(eth_payload_data + hlen);
-                    /* ETH_MTU = ip header len + tcp header len + payload */
-                    int tcp_data_len = ip_data_len - tcp_hlen;
-                    int tcp_chunk_size = ETH_MTU - hlen - tcp_hlen;
+                int tcp_hlen = TCP_HEADER_DATA_OFFSET(p_tcp_hdr);
-                    DPRINTF("+++ C+ mode TSO IP data len %d TCP hlen %d TCP "
-                        "data len %d TCP chunk size %d\n", ip_data_len,
-                        tcp_hlen, tcp_data_len, tcp_chunk_size);
+                /* ETH_MTU = ip header len + tcp header len + payload */
+                int tcp_data_len = ip_data_len - tcp_hlen;
+                int tcp_chunk_size = ETH_MTU - hlen - tcp_hlen;
-                    /* note the cycle below overwrites IP header data,
-                       but restores it from saved_ip_header before sending packet */
+                DPRINTF("+++ C+ mode TSO IP data len %d TCP hlen %d TCP "
+                    "data len %d TCP chunk size %d\n", ip_data_len,
+                    tcp_hlen, tcp_data_len, tcp_chunk_size);
-                    int is_last_frame = 0;
+                /* note the cycle below overwrites IP header data,
+                   but restores it from saved_ip_header before sending packet */
-                    for (tcp_send_offset = 0; tcp_send_offset < tcp_data_len; tcp_send_offset += tcp_chunk_size)
-                    {
-                        uint16_t chunk_size = tcp_chunk_size;
-
-                        /* check if this is the last frame */
-                        if (tcp_send_offset + tcp_chunk_size >= tcp_data_len)
-                        {
-                            is_last_frame = 1;
-                            chunk_size = tcp_data_len - tcp_send_offset;
-                        }
-
-                        DPRINTF("+++ C+ mode TSO TCP seqno %08x\n",
-                            be32_to_cpu(p_tcp_hdr->th_seq));
-
-                        /* add 4 TCP pseudoheader fields */
-                        /* copy IP source and destination fields */
-                        memcpy(data_to_checksum, saved_ip_header + 12, 8);
-
-                        DPRINTF("+++ C+ mode TSO calculating TCP checksum for "
-                            "packet with %d bytes data\n", tcp_hlen +
-                            chunk_size);
-
-                        if (tcp_send_offset)
-                        {
-                            memcpy((uint8_t*)p_tcp_hdr + tcp_hlen, (uint8_t*)p_tcp_hdr + tcp_hlen + tcp_send_offset, chunk_size);
-                        }
-
-                        /* keep PUSH and FIN flags only for the last frame */
-                        if (!is_last_frame)
-                        {
-                            TCP_HEADER_CLEAR_FLAGS(p_tcp_hdr, TCP_FLAG_PUSH|TCP_FLAG_FIN);
-                        }
-
-                        /* recalculate TCP checksum */
-                        ip_pseudo_header *p_tcpip_hdr = (ip_pseudo_header *)data_to_checksum;
-                        p_tcpip_hdr->zeros      = 0;
-                        p_tcpip_hdr->ip_proto   = IP_PROTO_TCP;
-                        p_tcpip_hdr->ip_payload = cpu_to_be16(tcp_hlen + chunk_size);
-
-                        p_tcp_hdr->th_sum = 0;
-
-                        int tcp_checksum = ip_checksum(data_to_checksum, tcp_hlen + chunk_size + 12);
-                        DPRINTF("+++ C+ mode TSO TCP checksum %04x\n",
-                            tcp_checksum);
-
-                        p_tcp_hdr->th_sum = tcp_checksum;
-
-                        /* restore IP header */
-                        memcpy(eth_payload_data, saved_ip_header, hlen);
-
-                        /* set IP data length and recalculate IP checksum */
-                        ip->ip_len = cpu_to_be16(hlen + tcp_hlen + chunk_size);
-
-                        /* increment IP id for subsequent frames */
-                        ip->ip_id = cpu_to_be16(tcp_send_offset/tcp_chunk_size + be16_to_cpu(ip->ip_id));
-
-                        ip->ip_sum = 0;
-                        ip->ip_sum = ip_checksum(eth_payload_data, hlen);
-                        DPRINTF("+++ C+ mode TSO IP header len=%d "
-                            "checksum=%04x\n", hlen, ip->ip_sum);
-
-                        int tso_send_size = ETH_HLEN + hlen + tcp_hlen + chunk_size;
-                        DPRINTF("+++ C+ mode TSO transferring packet size "
-                            "%d\n", tso_send_size);
-                        rtl8139_transfer_frame(s, saved_buffer, tso_send_size,
-                            0, (uint8_t *) dot1q_buffer);
-
-                        /* add transferred count to TCP sequence number */
-                        p_tcp_hdr->th_seq = cpu_to_be32(chunk_size + be32_to_cpu(p_tcp_hdr->th_seq));
-                        ++send_count;
-                    }
+                int is_last_frame = 0;
-                    /* Stop sending this frame */
-                    saved_size = 0;
-                }
-                else if (txdw0 & (CP_TX_TCPCS|CP_TX_UDPCS))
+                for (tcp_send_offset = 0; tcp_send_offset < tcp_data_len; tcp_send_offset += tcp_chunk_size)
                 {
-                    DPRINTF("+++ C+ mode need TCP or UDP checksum\n");
+                    uint16_t chunk_size = tcp_chunk_size;
-                    /* maximum IP header length is 60 bytes */
-                    uint8_t saved_ip_header[60];
-                    memcpy(saved_ip_header, eth_payload_data, hlen);
+                    /* check if this is the last frame */
+                    if (tcp_send_offset + tcp_chunk_size >= tcp_data_len)
+                    {
+                        is_last_frame = 1;
+                        chunk_size = tcp_data_len - tcp_send_offset;
+                    }
-                    uint8_t *data_to_checksum     = eth_payload_data + hlen - 12;
-                    //                    size_t   data_to_checksum_len = eth_payload_len  - hlen + 12;
+                    DPRINTF("+++ C+ mode TSO TCP seqno %08x\n",
+                        be32_to_cpu(p_tcp_hdr->th_seq));
                     /* add 4 TCP pseudoheader fields */
                     /* copy IP source and destination fields */
                     memcpy(data_to_checksum, saved_ip_header + 12, 8);
-                    if ((txdw0 & CP_TX_TCPCS) && ip_protocol == IP_PROTO_TCP)
+                    DPRINTF("+++ C+ mode TSO calculating TCP checksum for "
+                        "packet with %d bytes data\n", tcp_hlen +
+                        chunk_size);
+
+                    if (tcp_send_offset)
                     {
-                        DPRINTF("+++ C+ mode calculating TCP checksum for "
-                            "packet with %d bytes data\n", ip_data_len);
+                        memcpy((uint8_t*)p_tcp_hdr + tcp_hlen, (uint8_t*)p_tcp_hdr + tcp_hlen + tcp_send_offset, chunk_size);
+                    }
-                        ip_pseudo_header *p_tcpip_hdr = (ip_pseudo_header *)data_to_checksum;
-                        p_tcpip_hdr->zeros      = 0;
-                        p_tcpip_hdr->ip_proto   = IP_PROTO_TCP;
-                        p_tcpip_hdr->ip_payload = cpu_to_be16(ip_data_len);
+                    /* keep PUSH and FIN flags only for the last frame */
+                    if (!is_last_frame)
+                    {
+                        TCP_HEADER_CLEAR_FLAGS(p_tcp_hdr, TCP_FLAG_PUSH|TCP_FLAG_FIN);
+                    }
-                        tcp_header* p_tcp_hdr = (tcp_header *) (data_to_checksum+12);
+                    /* recalculate TCP checksum */
+                    ip_pseudo_header *p_tcpip_hdr = (ip_pseudo_header *)data_to_checksum;
+                    p_tcpip_hdr->zeros      = 0;
+                    p_tcpip_hdr->ip_proto   = IP_PROTO_TCP;
+                    p_tcpip_hdr->ip_payload = cpu_to_be16(tcp_hlen + chunk_size);
-                        p_tcp_hdr->th_sum = 0;
+                    p_tcp_hdr->th_sum = 0;
-                        int tcp_checksum = ip_checksum(data_to_checksum, ip_data_len + 12);
-                        DPRINTF("+++ C+ mode TCP checksum %04x\n",
-                            tcp_checksum);
+                    int tcp_checksum = ip_checksum(data_to_checksum, tcp_hlen + chunk_size + 12);
+                    DPRINTF("+++ C+ mode TSO TCP checksum %04x\n",
+                        tcp_checksum);
-                        p_tcp_hdr->th_sum = tcp_checksum;
-                    }
-                    else if ((txdw0 & CP_TX_UDPCS) && ip_protocol == IP_PROTO_UDP)
-                    {
-                        DPRINTF("+++ C+ mode calculating UDP checksum for "
-                            "packet with %d bytes data\n", ip_data_len);
+                    p_tcp_hdr->th_sum = tcp_checksum;
-                        ip_pseudo_header *p_udpip_hdr = (ip_pseudo_header *)data_to_checksum;
-                        p_udpip_hdr->zeros      = 0;
-                        p_udpip_hdr->ip_proto   = IP_PROTO_UDP;
-                        p_udpip_hdr->ip_payload = cpu_to_be16(ip_data_len);
+                    /* restore IP header */
+                    memcpy(eth_payload_data, saved_ip_header, hlen);
-                        udp_header *p_udp_hdr = (udp_header *) (data_to_checksum+12);
+                    /* set IP data length and recalculate IP checksum */
+                    ip->ip_len = cpu_to_be16(hlen + tcp_hlen + chunk_size);
-                        p_udp_hdr->uh_sum = 0;
+                    /* increment IP id for subsequent frames */
+                    ip->ip_id = cpu_to_be16(tcp_send_offset/tcp_chunk_size + be16_to_cpu(ip->ip_id));
-                        int udp_checksum = ip_checksum(data_to_checksum, ip_data_len + 12);
-                        DPRINTF("+++ C+ mode UDP checksum %04x\n",
-                            udp_checksum);
+                    ip->ip_sum = 0;
+                    ip->ip_sum = ip_checksum(eth_payload_data, hlen);
+                    DPRINTF("+++ C+ mode TSO IP header len=%d "
+                        "checksum=%04x\n", hlen, ip->ip_sum);
-                        p_udp_hdr->uh_sum = udp_checksum;
-                    }
+                    int tso_send_size = ETH_HLEN + hlen + tcp_hlen + chunk_size;
+                    DPRINTF("+++ C+ mode TSO transferring packet size "
+                        "%d\n", tso_send_size);
+                    rtl8139_transfer_frame(s, saved_buffer, tso_send_size,
+                        0, (uint8_t *) dot1q_buffer);
-                    /* restore IP header */
-                    memcpy(eth_payload_data, saved_ip_header, hlen);
+                    /* add transferred count to TCP sequence number */
+                    p_tcp_hdr->th_seq = cpu_to_be32(chunk_size + be32_to_cpu(p_tcp_hdr->th_seq));
+                    ++send_count;
                 }
+
+                /* Stop sending this frame */
+                saved_size = 0;
+            }
+            else if (txdw0 & (CP_TX_TCPCS|CP_TX_UDPCS))
+            {
+                DPRINTF("+++ C+ mode need TCP or UDP checksum\n");
+
+                /* maximum IP header length is 60 bytes */
+                uint8_t saved_ip_header[60];
+                memcpy(saved_ip_header, eth_payload_data, hlen);
+
+                uint8_t *data_to_checksum     = eth_payload_data + hlen - 12;
+                //                    size_t   data_to_checksum_len = eth_payload_len  - hlen + 12;
+
+                /* add 4 TCP pseudoheader fields */
+                /* copy IP source and destination fields */
+                memcpy(data_to_checksum, saved_ip_header + 12, 8);
+
+                if ((txdw0 & CP_TX_TCPCS) && ip_protocol == IP_PROTO_TCP)
+                {
+                    DPRINTF("+++ C+ mode calculating TCP checksum for "
+                        "packet with %d bytes data\n", ip_data_len);
+
+                    ip_pseudo_header *p_tcpip_hdr = (ip_pseudo_header *)data_to_checksum;
+                    p_tcpip_hdr->zeros      = 0;
+                    p_tcpip_hdr->ip_proto   = IP_PROTO_TCP;
+                    p_tcpip_hdr->ip_payload = cpu_to_be16(ip_data_len);
+
+                    tcp_header* p_tcp_hdr = (tcp_header *) (data_to_checksum+12);
+
+                    p_tcp_hdr->th_sum = 0;
+
+                    int tcp_checksum = ip_checksum(data_to_checksum, ip_data_len + 12);
+                    DPRINTF("+++ C+ mode TCP checksum %04x\n",
+                        tcp_checksum);
+
+                    p_tcp_hdr->th_sum = tcp_checksum;
+                }
+                else if ((txdw0 & CP_TX_UDPCS) && ip_protocol == IP_PROTO_UDP)
+                {
+                    DPRINTF("+++ C+ mode calculating UDP checksum for "
+                        "packet with %d bytes data\n", ip_data_len);
+
+                    ip_pseudo_header *p_udpip_hdr = (ip_pseudo_header *)data_to_checksum;
+                    p_udpip_hdr->zeros      = 0;
+                    p_udpip_hdr->ip_proto   = IP_PROTO_UDP;
+                    p_udpip_hdr->ip_payload = cpu_to_be16(ip_data_len);
+
+                    udp_header *p_udp_hdr = (udp_header *) (data_to_checksum+12);
+
+                    p_udp_hdr->uh_sum = 0;
+
+                    int udp_checksum = ip_checksum(data_to_checksum, ip_data_len + 12);
+                    DPRINTF("+++ C+ mode UDP checksum %04x\n",
+                        udp_checksum);
+
+                    p_udp_hdr->uh_sum = udp_checksum;
+                }
+
+                /* restore IP header */
+                memcpy(eth_payload_data, saved_ip_header, hlen);
             }
         }
--
1.8.3.1
SOURCES/kvm-rtl8139-skip-offload-on-short-Ethernet-IP-header-CVE.patch
New file
@@ -0,0 +1,49 @@
From 305025fe1852b99fcd84b6cd75ed7392d4f49efc Mon Sep 17 00:00:00 2001
From: Stefan Hajnoczi <stefanha@redhat.com>
Date: Fri, 31 Jul 2015 15:59:44 +0200
Subject: [PATCH 3/7] rtl8139: skip offload on short Ethernet/IP header
 (CVE-2015-5165)
Message-id: <1438358388-10575-4-git-send-email-stefanha@redhat.com>
Patchwork-id: 67237
O-Subject: [RHEL-7.1.z qemu-kvm EMBARGOED PATCH 3/7] rtl8139: skip offload on short Ethernet/IP header (CVE-2015-5165)
Bugzilla: 1248764
RH-Acked-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
RH-Acked-by: Xiao Wang <jasowang@redhat.com>
RH-Acked-by: Laszlo Ersek <lersek@redhat.com>
Transmit offload features access Ethernet and IP headers the packet.  If
the packet is too short we must not attempt to access header fields:
  int proto = be16_to_cpu(*(uint16_t *)(saved_buffer + 12));
  ...
  eth_payload_data = saved_buffer + ETH_HLEN;
  ...
  ip = (ip_header*)eth_payload_data;
  if (IP_HEADER_VERSION(ip) != IP_HEADER_VERSION_4) {
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
---
 hw/net/rtl8139.c | 5 +++++
 1 file changed, 5 insertions(+)
diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c
index ab32714..409029d 100644
--- a/hw/net/rtl8139.c
+++ b/hw/net/rtl8139.c
@@ -2142,6 +2142,11 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s)
         {
             DPRINTF("+++ C+ mode offloaded task checksum\n");
+            /* Large enough for Ethernet and IP headers? */
+            if (saved_size < ETH_HLEN + sizeof(ip_header)) {
+                goto skip_offload;
+            }
+
             /* ip packet header */
             ip_header *ip = NULL;
             int hlen = 0;
--
1.8.3.1
SOURCES/kvm-rtl8139-skip-offload-on-short-TCP-header-CVE-2015-51.patch
New file
@@ -0,0 +1,44 @@
From 7940931aeb97ec1b27fade4f7863418a4afd828f Mon Sep 17 00:00:00 2001
From: Stefan Hajnoczi <stefanha@redhat.com>
Date: Fri, 31 Jul 2015 15:59:47 +0200
Subject: [PATCH 6/7] rtl8139: skip offload on short TCP header (CVE-2015-5165)
Message-id: <1438358388-10575-7-git-send-email-stefanha@redhat.com>
Patchwork-id: 67241
O-Subject: [RHEL-7.1.z qemu-kvm EMBARGOED PATCH 6/7] rtl8139: skip offload on short TCP header (CVE-2015-5165)
Bugzilla: 1248764
RH-Acked-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
RH-Acked-by: Xiao Wang <jasowang@redhat.com>
RH-Acked-by: Laszlo Ersek <lersek@redhat.com>
TCP Large Segment Offload accesses the TCP header in the packet.  If the
packet is too short we must not attempt to access header fields:
  tcp_header *p_tcp_hdr = (tcp_header*)(eth_payload_data + hlen);
  int tcp_hlen = TCP_HEADER_DATA_OFFSET(p_tcp_hdr);
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
---
 hw/net/rtl8139.c | 5 +++++
 1 file changed, 5 insertions(+)
diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c
index 61bc34c..c9bf454 100644
--- a/hw/net/rtl8139.c
+++ b/hw/net/rtl8139.c
@@ -2202,6 +2202,11 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s)
             if ((txdw0 & CP_TX_LGSEN) && ip_protocol == IP_PROTO_TCP)
             {
+                /* Large enough for the TCP header? */
+                if (ip_data_len < sizeof(tcp_header)) {
+                    goto skip_offload;
+                }
+
                 int large_send_mss = (txdw0 >> 16) & CP_TC_LGSEN_MSS_MASK;
                 DPRINTF("+++ C+ mode offloaded task TSO MTU=%d IP data %d "
--
1.8.3.1
SPECS/qemu-kvm.spec
@@ -72,7 +72,7 @@
Summary: QEMU is a FAST! processor emulator
Name: %{pkgname}%{?pkgsuffix}
Version: 1.5.3
Release: 86%{?dist}.5
Release: 86%{?dist}.6
# Epoch because we pushed a qemu-1.0 package. AIUI this can't ever be dropped
Epoch: 10
License: GPLv2+ and LGPLv2+ and BSD
@@ -2845,6 +2845,20 @@
Patch1397: kvm-ide-Clear-DRQ-after-handling-all-expected-accesses.patch
# For bz#1243726 - CVE-2015-3214 qemu-kvm: qemu: i8254: out-of-bounds memory access in pit_ioport_read function [rhel-7.1.z]
Patch1398: kvm-i8254-fix-out-of-bounds-memory-access-in-pit_ioport_.patch
# For bz#1248764 - CVE-2015-5165 qemu-kvm: Qemu: rtl8139 uninitialized heap memory information leakage to guest [rhel-7.1.z]
Patch1399: kvm-rtl8139-avoid-nested-ifs-in-IP-header-parsing-CVE-20.patch
# For bz#1248764 - CVE-2015-5165 qemu-kvm: Qemu: rtl8139 uninitialized heap memory information leakage to guest [rhel-7.1.z]
Patch1400: kvm-rtl8139-drop-tautologous-if-ip-.-statement-CVE-2015-.patch
# For bz#1248764 - CVE-2015-5165 qemu-kvm: Qemu: rtl8139 uninitialized heap memory information leakage to guest [rhel-7.1.z]
Patch1401: kvm-rtl8139-skip-offload-on-short-Ethernet-IP-header-CVE.patch
# For bz#1248764 - CVE-2015-5165 qemu-kvm: Qemu: rtl8139 uninitialized heap memory information leakage to guest [rhel-7.1.z]
Patch1402: kvm-rtl8139-check-IP-Header-Length-field-CVE-2015-5165.patch
# For bz#1248764 - CVE-2015-5165 qemu-kvm: Qemu: rtl8139 uninitialized heap memory information leakage to guest [rhel-7.1.z]
Patch1403: kvm-rtl8139-check-IP-Total-Length-field-CVE-2015-5165.patch
# For bz#1248764 - CVE-2015-5165 qemu-kvm: Qemu: rtl8139 uninitialized heap memory information leakage to guest [rhel-7.1.z]
Patch1404: kvm-rtl8139-skip-offload-on-short-TCP-header-CVE-2015-51.patch
# For bz#1248764 - CVE-2015-5165 qemu-kvm: Qemu: rtl8139 uninitialized heap memory information leakage to guest [rhel-7.1.z]
Patch1405: kvm-rtl8139-check-TCP-Data-Offset-field-CVE-2015-5165.patch
BuildRequires: zlib-devel
@@ -4451,6 +4465,13 @@
%patch1396 -p1
%patch1397 -p1
%patch1398 -p1
%patch1399 -p1
%patch1400 -p1
%patch1401 -p1
%patch1402 -p1
%patch1403 -p1
%patch1404 -p1
%patch1405 -p1
%build
buildarch="%{kvm_target}-softmmu"
@@ -4895,6 +4916,17 @@
%{_libdir}/pkgconfig/libcacard.pc
%changelog
* Thu Aug 06 2015 Miroslav Rezanina <mrezanin@redhat.com> - 1.5.3-86.el7_1.6
- kvm-rtl8139-avoid-nested-ifs-in-IP-header-parsing-CVE-20.patch [bz#1248764]
- kvm-rtl8139-drop-tautologous-if-ip-.-statement-CVE-2015-.patch [bz#1248764]
- kvm-rtl8139-skip-offload-on-short-Ethernet-IP-header-CVE.patch [bz#1248764]
- kvm-rtl8139-check-IP-Header-Length-field-CVE-2015-5165.patch [bz#1248764]
- kvm-rtl8139-check-IP-Total-Length-field-CVE-2015-5165.patch [bz#1248764]
- kvm-rtl8139-skip-offload-on-short-TCP-header-CVE-2015-51.patch [bz#1248764]
- kvm-rtl8139-check-TCP-Data-Offset-field-CVE-2015-5165.patch [bz#1248764]
- Resolves: bz#1248764
  (CVE-2015-5165 qemu-kvm: Qemu: rtl8139 uninitialized heap memory information leakage to guest [rhel-7.1.z])
* Mon Jul 20 2015 Miroslav Rezanina <mrezanin@redhat.com> - 1.5.3-86.el7_1.5
- kvm-i8254-fix-out-of-bounds-memory-access-in-pit_ioport_.patch [bz#1243726]
- Resolves: bz#1243726