diff --git a/SOURCES/kvm-rtl8139-avoid-nested-ifs-in-IP-header-parsing-CVE-20.patch b/SOURCES/kvm-rtl8139-avoid-nested-ifs-in-IP-header-parsing-CVE-20.patch new file mode 100644 index 0000000..53483b8 --- /dev/null +++ b/SOURCES/kvm-rtl8139-avoid-nested-ifs-in-IP-header-parsing-CVE-20.patch @@ -0,0 +1,92 @@ +From ceffa7fb93c4ea570a0c854974a4993eedea52ca Mon Sep 17 00:00:00 2001 +From: Stefan Hajnoczi +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 +RH-Acked-by: Xiao Wang +RH-Acked-by: Laszlo Ersek + +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 +Signed-off-by: Miroslav Rezanina +--- + 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 + diff --git a/SOURCES/kvm-rtl8139-check-IP-Header-Length-field-CVE-2015-5165.patch b/SOURCES/kvm-rtl8139-check-IP-Header-Length-field-CVE-2015-5165.patch new file mode 100644 index 0000000..f415886 --- /dev/null +++ b/SOURCES/kvm-rtl8139-check-IP-Header-Length-field-CVE-2015-5165.patch @@ -0,0 +1,62 @@ +From 230b373704579a17a7a7e6890fe0dc1b789a2fa4 Mon Sep 17 00:00:00 2001 +From: Stefan Hajnoczi +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 +RH-Acked-by: Xiao Wang +RH-Acked-by: Laszlo Ersek + +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 +Signed-off-by: Miroslav Rezanina +--- + 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 (hleneth_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 + diff --git a/SOURCES/kvm-rtl8139-check-IP-Total-Length-field-CVE-2015-5165.patch b/SOURCES/kvm-rtl8139-check-IP-Total-Length-field-CVE-2015-5165.patch new file mode 100644 index 0000000..a9fa82a --- /dev/null +++ b/SOURCES/kvm-rtl8139-check-IP-Total-Length-field-CVE-2015-5165.patch @@ -0,0 +1,43 @@ +From 32e2f1ebf0d979c5ccc7e934ea9729e956d6aa52 Mon Sep 17 00:00:00 2001 +From: Stefan Hajnoczi +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 +RH-Acked-by: Xiao Wang +RH-Acked-by: Laszlo Ersek + +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 +Signed-off-by: Miroslav Rezanina +--- + 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 + diff --git a/SOURCES/kvm-rtl8139-check-TCP-Data-Offset-field-CVE-2015-5165.patch b/SOURCES/kvm-rtl8139-check-TCP-Data-Offset-field-CVE-2015-5165.patch new file mode 100644 index 0000000..9dfe885 --- /dev/null +++ b/SOURCES/kvm-rtl8139-check-TCP-Data-Offset-field-CVE-2015-5165.patch @@ -0,0 +1,41 @@ +From a8cef763585fecc8b89d006efca0e000df923d36 Mon Sep 17 00:00:00 2001 +From: Stefan Hajnoczi +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 +RH-Acked-by: Xiao Wang +RH-Acked-by: Laszlo Ersek + +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 +Signed-off-by: Miroslav Rezanina +--- + 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 + diff --git a/SOURCES/kvm-rtl8139-drop-tautologous-if-ip-.-statement-CVE-2015-.patch b/SOURCES/kvm-rtl8139-drop-tautologous-if-ip-.-statement-CVE-2015-.patch new file mode 100644 index 0000000..aaf4c06 --- /dev/null +++ b/SOURCES/kvm-rtl8139-drop-tautologous-if-ip-.-statement-CVE-2015-.patch @@ -0,0 +1,383 @@ +From 95ea90bc29b45f9b07b55c6cbb396fe854199079 Mon Sep 17 00:00:00 2001 +From: Stefan Hajnoczi +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 +RH-Acked-by: Xiao Wang +RH-Acked-by: Laszlo Ersek + +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 +Signed-off-by: Miroslav Rezanina +--- + 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 (hleneth_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 (hleneth_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 + diff --git a/SOURCES/kvm-rtl8139-skip-offload-on-short-Ethernet-IP-header-CVE.patch b/SOURCES/kvm-rtl8139-skip-offload-on-short-Ethernet-IP-header-CVE.patch new file mode 100644 index 0000000..2e52dd6 --- /dev/null +++ b/SOURCES/kvm-rtl8139-skip-offload-on-short-Ethernet-IP-header-CVE.patch @@ -0,0 +1,49 @@ +From 305025fe1852b99fcd84b6cd75ed7392d4f49efc Mon Sep 17 00:00:00 2001 +From: Stefan Hajnoczi +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 +RH-Acked-by: Xiao Wang +RH-Acked-by: Laszlo Ersek + +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 +Signed-off-by: Miroslav Rezanina +--- + 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 + diff --git a/SOURCES/kvm-rtl8139-skip-offload-on-short-TCP-header-CVE-2015-51.patch b/SOURCES/kvm-rtl8139-skip-offload-on-short-TCP-header-CVE-2015-51.patch new file mode 100644 index 0000000..b722c87 --- /dev/null +++ b/SOURCES/kvm-rtl8139-skip-offload-on-short-TCP-header-CVE-2015-51.patch @@ -0,0 +1,44 @@ +From 7940931aeb97ec1b27fade4f7863418a4afd828f Mon Sep 17 00:00:00 2001 +From: Stefan Hajnoczi +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 +RH-Acked-by: Xiao Wang +RH-Acked-by: Laszlo Ersek + +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 +Signed-off-by: Miroslav Rezanina +--- + 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 + diff --git a/SPECS/qemu-kvm.spec b/SPECS/qemu-kvm.spec index b2ab524..86f4a7a 100644 --- a/SPECS/qemu-kvm.spec +++ b/SPECS/qemu-kvm.spec @@ -72,7 +72,7 @@ Obsoletes: %1 < %{obsoletes_version} \ 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 @@ Patch1396: kvm-ide-atapi-Fix-START-STOP-UNIT-command-completion.patch 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 @@ cp %{SOURCE18} pc-bios # keep "make check" happy %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 @@ sh %{_sysconfdir}/sysconfig/modules/kvm.modules &> /dev/null || : %{_libdir}/pkgconfig/libcacard.pc %changelog +* Thu Aug 06 2015 Miroslav Rezanina - 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 - 1.5.3-86.el7_1.5 - kvm-i8254-fix-out-of-bounds-memory-access-in-pit_ioport_.patch [bz#1243726] - Resolves: bz#1243726