From 5e0a9247b57a35a1a55ab65a28ec69653dcbc01a Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Aug 11 2015 19:20:04 +0000 Subject: Fix crash in qemu_spice_create_display (bz #1163047) CVE-2015-3209: pcnet: multi-tmd buffer overflow in the tx path (bz #1230536) CVE-2015-3214: i8254: out-of-bounds memory access (bz #1243728) CVE-2015-5154: ide: atapi: heap overflow during I/O buffer memory access (bz #1247141) CVE-2015-5745: buffer overflow in virtio-serial (bz #1251160) CVE-2015-5165: rtl8139 uninitialized heap memory information leakage to guest (bz #1249755) --- diff --git a/0022-spice-display-fix-segfault-in-qemu_spice_create_upda.patch b/0022-spice-display-fix-segfault-in-qemu_spice_create_upda.patch new file mode 100644 index 0000000..805296a --- /dev/null +++ b/0022-spice-display-fix-segfault-in-qemu_spice_create_upda.patch @@ -0,0 +1,49 @@ +From: Gerd Hoffmann +Date: Tue, 9 Jun 2015 21:08:47 +0200 +Subject: [PATCH] spice-display: fix segfault in qemu_spice_create_update + +Although it is pretty unusual the stride for the guest image and the +mirror image maintained by spice-display can be different. So use +separate variables for them. + +https://bugzilla.redhat.com/show_bug.cgi?id=1163047 + +Cc: qemu-stable@nongnu.org +Reported-by: perrier vincent +Signed-off-by: Gerd Hoffmann +(cherry picked from commit c6e484707f28b3e115e64122a0570f6b3c585489) +--- + ui/spice-display.c | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +diff --git a/ui/spice-display.c b/ui/spice-display.c +index def7b52..ded5b72 100644 +--- a/ui/spice-display.c ++++ b/ui/spice-display.c +@@ -199,7 +199,7 @@ static void qemu_spice_create_update(SimpleSpiceDisplay *ssd) + static const int blksize = 32; + int blocks = (surface_width(ssd->ds) + blksize - 1) / blksize; + int dirty_top[blocks]; +- int y, yoff, x, xoff, blk, bw; ++ int y, yoff1, yoff2, x, xoff, blk, bw; + int bpp = surface_bytes_per_pixel(ssd->ds); + uint8_t *guest, *mirror; + +@@ -220,13 +220,14 @@ static void qemu_spice_create_update(SimpleSpiceDisplay *ssd) + guest = surface_data(ssd->ds); + mirror = (void *)pixman_image_get_data(ssd->mirror); + for (y = ssd->dirty.top; y < ssd->dirty.bottom; y++) { +- yoff = y * surface_stride(ssd->ds); ++ yoff1 = y * surface_stride(ssd->ds); ++ yoff2 = y * pixman_image_get_stride(ssd->mirror); + for (x = ssd->dirty.left; x < ssd->dirty.right; x += blksize) { + xoff = x * bpp; + blk = x / blksize; + bw = MIN(blksize, ssd->dirty.right - x); +- if (memcmp(guest + yoff + xoff, +- mirror + yoff + xoff, ++ if (memcmp(guest + yoff1 + xoff, ++ mirror + yoff2 + xoff, + bw * bpp) == 0) { + if (dirty_top[blk] != -1) { + QXLRect update = { diff --git a/0023-pcnet-fix-Negative-array-index-read.patch b/0023-pcnet-fix-Negative-array-index-read.patch new file mode 100644 index 0000000..694a8a7 --- /dev/null +++ b/0023-pcnet-fix-Negative-array-index-read.patch @@ -0,0 +1,96 @@ +From: Gonglei +Date: Thu, 20 Nov 2014 19:35:02 +0800 +Subject: [PATCH] pcnet: fix Negative array index read + +s->xmit_pos maybe assigned to a negative value (-1), +but in this branch variable s->xmit_pos as an index to +array s->buffer. Let's add a check for s->xmit_pos. + +Signed-off-by: Gonglei +Signed-off-by: Paolo Bonzini +Reviewed-by: Jason Wang +Reviewed-by: Jason Wang +Signed-off-by: Stefan Hajnoczi +(cherry picked from commit 7b50d00911ddd6d56a766ac5671e47304c20a21b) +--- + hw/net/pcnet.c | 55 ++++++++++++++++++++++++++++++------------------------- + 1 file changed, 30 insertions(+), 25 deletions(-) + +diff --git a/hw/net/pcnet.c b/hw/net/pcnet.c +index 5299d52..b18ac6c 100644 +--- a/hw/net/pcnet.c ++++ b/hw/net/pcnet.c +@@ -1212,7 +1212,7 @@ static void pcnet_transmit(PCNetState *s) + hwaddr xmit_cxda = 0; + int count = CSR_XMTRL(s)-1; + int add_crc = 0; +- ++ int bcnt; + s->xmit_pos = -1; + + if (!CSR_TXON(s)) { +@@ -1247,35 +1247,40 @@ static void pcnet_transmit(PCNetState *s) + s->xmit_pos = -1; + goto txdone; + } ++ ++ if (s->xmit_pos < 0) { ++ goto txdone; ++ } ++ ++ bcnt = 4096 - GET_FIELD(tmd.length, TMDL, BCNT); ++ s->phys_mem_read(s->dma_opaque, PHYSADDR(s, tmd.tbadr), ++ s->buffer + s->xmit_pos, bcnt, CSR_BSWP(s)); ++ s->xmit_pos += bcnt; ++ + if (!GET_FIELD(tmd.status, TMDS, ENP)) { +- int bcnt = 4096 - GET_FIELD(tmd.length, TMDL, BCNT); +- s->phys_mem_read(s->dma_opaque, PHYSADDR(s, tmd.tbadr), +- s->buffer + s->xmit_pos, bcnt, CSR_BSWP(s)); +- s->xmit_pos += bcnt; +- } else if (s->xmit_pos >= 0) { +- int bcnt = 4096 - GET_FIELD(tmd.length, TMDL, BCNT); +- s->phys_mem_read(s->dma_opaque, PHYSADDR(s, tmd.tbadr), +- s->buffer + s->xmit_pos, bcnt, CSR_BSWP(s)); +- s->xmit_pos += bcnt; ++ goto txdone; ++ } ++ + #ifdef PCNET_DEBUG +- printf("pcnet_transmit size=%d\n", s->xmit_pos); ++ printf("pcnet_transmit size=%d\n", s->xmit_pos); + #endif +- if (CSR_LOOP(s)) { +- if (BCR_SWSTYLE(s) == 1) +- add_crc = !GET_FIELD(tmd.status, TMDS, NOFCS); +- s->looptest = add_crc ? PCNET_LOOPTEST_CRC : PCNET_LOOPTEST_NOCRC; +- pcnet_receive(qemu_get_queue(s->nic), s->buffer, s->xmit_pos); +- s->looptest = 0; +- } else +- if (s->nic) +- qemu_send_packet(qemu_get_queue(s->nic), s->buffer, +- s->xmit_pos); +- +- s->csr[0] &= ~0x0008; /* clear TDMD */ +- s->csr[4] |= 0x0004; /* set TXSTRT */ +- s->xmit_pos = -1; ++ if (CSR_LOOP(s)) { ++ if (BCR_SWSTYLE(s) == 1) ++ add_crc = !GET_FIELD(tmd.status, TMDS, NOFCS); ++ s->looptest = add_crc ? PCNET_LOOPTEST_CRC : PCNET_LOOPTEST_NOCRC; ++ pcnet_receive(qemu_get_queue(s->nic), s->buffer, s->xmit_pos); ++ s->looptest = 0; ++ } else { ++ if (s->nic) { ++ qemu_send_packet(qemu_get_queue(s->nic), s->buffer, ++ s->xmit_pos); ++ } + } + ++ s->csr[0] &= ~0x0008; /* clear TDMD */ ++ s->csr[4] |= 0x0004; /* set TXSTRT */ ++ s->xmit_pos = -1; ++ + txdone: + SET_FIELD(&tmd.status, TMDS, OWN, 0); + TMDSTORE(&tmd, PHYSADDR(s,CSR_CXDA(s))); diff --git a/0024-pcnet-force-the-buffer-access-to-be-in-bounds-during.patch b/0024-pcnet-force-the-buffer-access-to-be-in-bounds-during.patch new file mode 100644 index 0000000..5ac76bd --- /dev/null +++ b/0024-pcnet-force-the-buffer-access-to-be-in-bounds-during.patch @@ -0,0 +1,46 @@ +From: Petr Matousek +Date: Sun, 24 May 2015 10:53:44 +0200 +Subject: [PATCH] pcnet: force the buffer access to be in bounds during tx + +4096 is the maximum length per TMD and it is also currently the size of +the relay buffer pcnet driver uses for sending the packet data to QEMU +for further processing. With packet spanning multiple TMDs it can +happen that the overall packet size will be bigger than sizeof(buffer), +which results in memory corruption. + +Fix this by only allowing to queue maximum sizeof(buffer) bytes. + +This is CVE-2015-3209. + +[Fixed 3-space indentation to QEMU's 4-space coding standard. +--Stefan] + +Signed-off-by: Petr Matousek +Reported-by: Matt Tait +Reviewed-by: Peter Maydell +Reviewed-by: Stefan Hajnoczi +Signed-off-by: Stefan Hajnoczi +(cherry picked from commit 9f7c594c006289ad41169b854d70f5da6e400a2a) +--- + hw/net/pcnet.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/hw/net/pcnet.c b/hw/net/pcnet.c +index b18ac6c..455845b 100644 +--- a/hw/net/pcnet.c ++++ b/hw/net/pcnet.c +@@ -1253,6 +1253,14 @@ static void pcnet_transmit(PCNetState *s) + } + + bcnt = 4096 - GET_FIELD(tmd.length, TMDL, BCNT); ++ ++ /* if multi-tmd packet outsizes s->buffer then skip it silently. ++ Note: this is not what real hw does */ ++ if (s->xmit_pos + bcnt > sizeof(s->buffer)) { ++ s->xmit_pos = -1; ++ goto txdone; ++ } ++ + s->phys_mem_read(s->dma_opaque, PHYSADDR(s, tmd.tbadr), + s->buffer + s->xmit_pos, bcnt, CSR_BSWP(s)); + s->xmit_pos += bcnt; diff --git a/0025-i8254-fix-out-of-bounds-memory-access-in-pit_ioport_.patch b/0025-i8254-fix-out-of-bounds-memory-access-in-pit_ioport_.patch new file mode 100644 index 0000000..f15d127 --- /dev/null +++ b/0025-i8254-fix-out-of-bounds-memory-access-in-pit_ioport_.patch @@ -0,0 +1,40 @@ +From: Petr Matousek +Date: Wed, 17 Jun 2015 12:46:11 +0200 +Subject: [PATCH] i8254: fix out-of-bounds memory access in pit_ioport_read() + +Due converting PIO to the new memory read/write api we no longer provide +separate I/O region lenghts for read and write operations. As a result, +reading from PIT Mode/Command register will end with accessing +pit->channels with invalid index. + +Fix this by ignoring read from the Mode/Command register. + +This is CVE-2015-3214. + +Reported-by: Matt Tait +Fixes: 0505bcdec8228d8de39ab1a02644e71999e7c052 +Cc: qemu-stable@nongnu.org +Signed-off-by: Petr Matousek +Signed-off-by: Paolo Bonzini +(cherry picked from commit d4862a87e31a51de9eb260f25c9e99a75efe3235) +--- + hw/timer/i8254.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/hw/timer/i8254.c b/hw/timer/i8254.c +index 3450c98..9b65a33 100644 +--- a/hw/timer/i8254.c ++++ b/hw/timer/i8254.c +@@ -196,6 +196,12 @@ static uint64_t pit_ioport_read(void *opaque, hwaddr addr, + PITChannelState *s; + + addr &= 3; ++ ++ if (addr == 3) { ++ /* Mode/Command register is write only, read is ignored */ ++ return 0; ++ } ++ + s = &pit->channels[addr]; + if (s->status_latched) { + s->status_latched = 0; diff --git a/0026-ide-Check-array-bounds-before-writing-to-io_buffer-C.patch b/0026-ide-Check-array-bounds-before-writing-to-io_buffer-C.patch new file mode 100644 index 0000000..2d7cc2d --- /dev/null +++ b/0026-ide-Check-array-bounds-before-writing-to-io_buffer-C.patch @@ -0,0 +1,75 @@ +From: Kevin Wolf +Date: Sun, 26 Jul 2015 23:42:53 -0400 +Subject: [PATCH] ide: Check array bounds before writing to io_buffer + (CVE-2015-5154) + +If the end_transfer_func of a command is called because enough data has +been read or written for the current PIO transfer, and it fails to +correctly call the command completion functions, the DRQ bit in the +status register and s->end_transfer_func may remain set. This allows the +guest to access further bytes in s->io_buffer beyond s->data_end, and +eventually overflowing the io_buffer. + +One case where this currently happens is emulation of the ATAPI command +START STOP UNIT. + +This patch fixes the problem by adding explicit array bounds checks +before accessing the buffer instead of relying on end_transfer_func to +function correctly. + +Cc: qemu-stable@nongnu.org +Signed-off-by: Kevin Wolf +Reviewed-by: John Snow +(cherry picked from commit d2ff85854512574e7209f295e87b0835d5b032c6) +--- + hw/ide/core.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/hw/ide/core.c b/hw/ide/core.c +index 50691fc..a5a62cd 100644 +--- a/hw/ide/core.c ++++ b/hw/ide/core.c +@@ -1931,6 +1931,10 @@ void ide_data_writew(void *opaque, uint32_t addr, uint32_t val) + } + + p = s->data_ptr; ++ if (p + 2 > s->data_end) { ++ return; ++ } ++ + *(uint16_t *)p = le16_to_cpu(val); + p += 2; + s->data_ptr = p; +@@ -1952,6 +1956,10 @@ uint32_t ide_data_readw(void *opaque, uint32_t addr) + } + + p = s->data_ptr; ++ if (p + 2 > s->data_end) { ++ return 0; ++ } ++ + ret = cpu_to_le16(*(uint16_t *)p); + p += 2; + s->data_ptr = p; +@@ -1973,6 +1981,10 @@ void ide_data_writel(void *opaque, uint32_t addr, uint32_t val) + } + + p = s->data_ptr; ++ if (p + 4 > s->data_end) { ++ return; ++ } ++ + *(uint32_t *)p = le32_to_cpu(val); + p += 4; + s->data_ptr = p; +@@ -1994,6 +2006,10 @@ uint32_t ide_data_readl(void *opaque, uint32_t addr) + } + + p = s->data_ptr; ++ if (p + 4 > s->data_end) { ++ return 0; ++ } ++ + ret = cpu_to_le32(*(uint32_t *)p); + p += 4; + s->data_ptr = p; diff --git a/0027-ide-atapi-Fix-START-STOP-UNIT-command-completion.patch b/0027-ide-atapi-Fix-START-STOP-UNIT-command-completion.patch new file mode 100644 index 0000000..e8089db --- /dev/null +++ b/0027-ide-atapi-Fix-START-STOP-UNIT-command-completion.patch @@ -0,0 +1,26 @@ +From: Kevin Wolf +Date: Sun, 26 Jul 2015 23:42:53 -0400 +Subject: [PATCH] ide/atapi: Fix START STOP UNIT command completion + +The command must be completed on all code paths. START STOP UNIT with +pwrcnd set should succeed without doing anything. + +Signed-off-by: Kevin Wolf +Reviewed-by: John Snow +(cherry picked from commit 03441c3a4a42beb25460dd11592539030337d0f8) +--- + hw/ide/atapi.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/hw/ide/atapi.c b/hw/ide/atapi.c +index f7d2009..be000545 100644 +--- a/hw/ide/atapi.c ++++ b/hw/ide/atapi.c +@@ -879,6 +879,7 @@ static void cmd_start_stop_unit(IDEState *s, uint8_t* buf) + + if (pwrcnd) { + /* eject/load only happens for power condition == 0 */ ++ ide_atapi_cmd_ok(s); + return; + } + diff --git a/0028-ide-Clear-DRQ-after-handling-all-expected-accesses.patch b/0028-ide-Clear-DRQ-after-handling-all-expected-accesses.patch new file mode 100644 index 0000000..6eef0a6 --- /dev/null +++ b/0028-ide-Clear-DRQ-after-handling-all-expected-accesses.patch @@ -0,0 +1,69 @@ +From: Kevin Wolf +Date: Sun, 26 Jul 2015 23:42:53 -0400 +Subject: [PATCH] ide: Clear DRQ after handling all expected accesses + +This is additional hardening against an end_transfer_func that fails to +clear the DRQ status bit. The bit must be unset as soon as the PIO +transfer has completed, so it's better to do this in a central place +instead of duplicating the code in all commands (and forgetting it in +some). + +Signed-off-by: Kevin Wolf +Reviewed-by: John Snow +(cherry picked from commit cb72cba83021fa42719e73a5249c12096a4d1cfc) +--- + hw/ide/core.c | 16 ++++++++++++---- + 1 file changed, 12 insertions(+), 4 deletions(-) + +diff --git a/hw/ide/core.c b/hw/ide/core.c +index a5a62cd..4ac3731 100644 +--- a/hw/ide/core.c ++++ b/hw/ide/core.c +@@ -1938,8 +1938,10 @@ void ide_data_writew(void *opaque, uint32_t addr, uint32_t val) + *(uint16_t *)p = le16_to_cpu(val); + p += 2; + s->data_ptr = p; +- if (p >= s->data_end) ++ if (p >= s->data_end) { ++ s->status &= ~DRQ_STAT; + s->end_transfer_func(s); ++ } + } + + uint32_t ide_data_readw(void *opaque, uint32_t addr) +@@ -1963,8 +1965,10 @@ uint32_t ide_data_readw(void *opaque, uint32_t addr) + ret = cpu_to_le16(*(uint16_t *)p); + p += 2; + s->data_ptr = p; +- if (p >= s->data_end) ++ if (p >= s->data_end) { ++ s->status &= ~DRQ_STAT; + s->end_transfer_func(s); ++ } + return ret; + } + +@@ -1988,8 +1992,10 @@ void ide_data_writel(void *opaque, uint32_t addr, uint32_t val) + *(uint32_t *)p = le32_to_cpu(val); + p += 4; + s->data_ptr = p; +- if (p >= s->data_end) ++ if (p >= s->data_end) { ++ s->status &= ~DRQ_STAT; + s->end_transfer_func(s); ++ } + } + + uint32_t ide_data_readl(void *opaque, uint32_t addr) +@@ -2013,8 +2019,10 @@ uint32_t ide_data_readl(void *opaque, uint32_t addr) + ret = cpu_to_le32(*(uint32_t *)p); + p += 4; + s->data_ptr = p; +- if (p >= s->data_end) ++ if (p >= s->data_end) { ++ s->status &= ~DRQ_STAT; + s->end_transfer_func(s); ++ } + return ret; + } + diff --git a/0029-virtio-serial-fix-ANY_LAYOUT.patch b/0029-virtio-serial-fix-ANY_LAYOUT.patch new file mode 100644 index 0000000..840e6bd --- /dev/null +++ b/0029-virtio-serial-fix-ANY_LAYOUT.patch @@ -0,0 +1,29 @@ +From: "Michael S. Tsirkin" +Date: Thu, 23 Jul 2015 17:52:02 +0300 +Subject: [PATCH] virtio-serial: fix ANY_LAYOUT + +Don't assume a specific layout for control messages. +Required by virtio 1. + +Signed-off-by: Michael S. Tsirkin +Reviewed-by: Amit Shah +Reviewed-by: Jason Wang +(cherry picked from commit 7882080388be5088e72c425b02223c02e6cb4295) +--- + hw/char/virtio-serial-bus.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c +index 23123b7..9b1fddf 100644 +--- a/hw/char/virtio-serial-bus.c ++++ b/hw/char/virtio-serial-bus.c +@@ -174,7 +174,8 @@ static size_t send_control_msg(VirtIOSerial *vser, void *buf, size_t len) + return 0; + } + +- memcpy(elem.in_sg[0].iov_base, buf, len); ++ /* TODO: detect a buffer that's too short, set NEEDS_RESET */ ++ iov_from_buf(elem.in_sg, elem.in_num, 0, buf, len); + + virtqueue_push(vq, &elem, len); + virtio_notify(VIRTIO_DEVICE(vser), vq); diff --git a/0030-rtl8139-avoid-nested-ifs-in-IP-header-parsing-CVE-20.patch b/0030-rtl8139-avoid-nested-ifs-in-IP-header-parsing-CVE-20.patch new file mode 100644 index 0000000..c98b28f --- /dev/null +++ b/0030-rtl8139-avoid-nested-ifs-in-IP-header-parsing-CVE-20.patch @@ -0,0 +1,85 @@ +From: Stefan Hajnoczi +Date: Wed, 15 Jul 2015 17:13:32 +0100 +Subject: [PATCH] rtl8139: avoid nested ifs in IP header parsing + (CVE-2015-5165) +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +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. + +Reported-by: 朱东海(启路) +Reviewed-by: Jason Wang +Signed-off-by: Stefan Hajnoczi +(cherry picked from commit 39b8e7dcaf04cbdb926b478f825b160d852752b5) +--- + 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 90bc5ec..b38fa97 100644 +--- a/hw/net/rtl8139.c ++++ b/hw/net/rtl8139.c +@@ -2170,28 +2170,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) +@@ -2387,6 +2389,7 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) + } + } + ++skip_offload: + /* update tally counter */ + ++s->tally_counters.TxOk; + diff --git a/0031-rtl8139-drop-tautologous-if-ip-.-statement-CVE-2015-.patch b/0031-rtl8139-drop-tautologous-if-ip-.-statement-CVE-2015-.patch new file mode 100644 index 0000000..7373968 --- /dev/null +++ b/0031-rtl8139-drop-tautologous-if-ip-.-statement-CVE-2015-.patch @@ -0,0 +1,376 @@ +From: Stefan Hajnoczi +Date: Wed, 15 Jul 2015 17:17:28 +0100 +Subject: [PATCH] rtl8139: drop tautologous if (ip) {...} statement + (CVE-2015-5165) +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +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. + +Reported-by: 朱东海(启路) +Reviewed-by: Jason Wang +Signed-off-by: Stefan Hajnoczi +(cherry picked from commit d6812d60e7932de3cd0f602c0ee63dd3d09f1847) +--- + 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 b38fa97..aec4dfc 100644 +--- a/hw/net/rtl8139.c ++++ b/hw/net/rtl8139.c +@@ -2194,198 +2194,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); + } + } + diff --git a/0032-rtl8139-skip-offload-on-short-Ethernet-IP-header-CVE.patch b/0032-rtl8139-skip-offload-on-short-Ethernet-IP-header-CVE.patch new file mode 100644 index 0000000..6514a03 --- /dev/null +++ b/0032-rtl8139-skip-offload-on-short-Ethernet-IP-header-CVE.patch @@ -0,0 +1,42 @@ +From: Stefan Hajnoczi +Date: Wed, 15 Jul 2015 14:30:37 +0100 +Subject: [PATCH] rtl8139: skip offload on short Ethernet/IP header + (CVE-2015-5165) +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +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) { + +Reported-by: 朱东海(启路) +Reviewed-by: Jason Wang +Signed-off-by: Stefan Hajnoczi +(cherry picked from commit e1c120a9c54872f8a538ff9129d928de4e865cbd) +--- + hw/net/rtl8139.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c +index aec4dfc..adba5d6 100644 +--- a/hw/net/rtl8139.c ++++ b/hw/net/rtl8139.c +@@ -2160,6 +2160,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; diff --git a/0033-rtl8139-check-IP-Header-Length-field-CVE-2015-5165.patch b/0033-rtl8139-check-IP-Header-Length-field-CVE-2015-5165.patch new file mode 100644 index 0000000..3d962c4 --- /dev/null +++ b/0033-rtl8139-check-IP-Header-Length-field-CVE-2015-5165.patch @@ -0,0 +1,55 @@ +From: Stefan Hajnoczi +Date: Wed, 15 Jul 2015 17:32:32 +0100 +Subject: [PATCH] rtl8139: check IP Header Length field (CVE-2015-5165) +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The IP Header Length field was only checked in the IP checksum case, but +is used in other cases too. + +Reported-by: 朱东海(启路) +Reviewed-by: Jason Wang +Signed-off-by: Stefan Hajnoczi +(cherry picked from commit 03247d43c577dfea8181cd40177ad5ba77c8db76) +--- + 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 adba5d6..14082cb 100644 +--- a/hw/net/rtl8139.c ++++ b/hw/net/rtl8139.c +@@ -2196,6 +2196,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; + +@@ -2203,17 +2207,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) diff --git a/0034-rtl8139-check-IP-Total-Length-field-CVE-2015-5165.patch b/0034-rtl8139-check-IP-Total-Length-field-CVE-2015-5165.patch new file mode 100644 index 0000000..ee35cea --- /dev/null +++ b/0034-rtl8139-check-IP-Total-Length-field-CVE-2015-5165.patch @@ -0,0 +1,36 @@ +From: Stefan Hajnoczi +Date: Wed, 15 Jul 2015 17:34:40 +0100 +Subject: [PATCH] rtl8139: check IP Total Length field (CVE-2015-5165) +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The IP Total Length field includes the IP header and data. Make sure it +is valid and does not exceed the Ethernet payload size. + +Reported-by: 朱东海(启路) +Reviewed-by: Jason Wang +Signed-off-by: Stefan Hajnoczi +(cherry picked from commit c6296ea88df040054ccd781f3945fe103f8c7c17) +--- + 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 14082cb..207c2e4 100644 +--- a/hw/net/rtl8139.c ++++ b/hw/net/rtl8139.c +@@ -2201,7 +2201,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) + { diff --git a/0035-rtl8139-skip-offload-on-short-TCP-header-CVE-2015-51.patch b/0035-rtl8139-skip-offload-on-short-TCP-header-CVE-2015-51.patch new file mode 100644 index 0000000..07570b1 --- /dev/null +++ b/0035-rtl8139-skip-offload-on-short-TCP-header-CVE-2015-51.patch @@ -0,0 +1,37 @@ +From: Stefan Hajnoczi +Date: Wed, 15 Jul 2015 17:36:15 +0100 +Subject: [PATCH] rtl8139: skip offload on short TCP header (CVE-2015-5165) +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +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); + +Reported-by: 朱东海(启路) +Reviewed-by: Jason Wang +Signed-off-by: Stefan Hajnoczi +(cherry picked from commit 4240be45632db7831129f124bcf53c1223825b0f) +--- + hw/net/rtl8139.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c +index 207c2e4..feaa06f 100644 +--- a/hw/net/rtl8139.c ++++ b/hw/net/rtl8139.c +@@ -2220,6 +2220,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 " diff --git a/0036-rtl8139-check-TCP-Data-Offset-field-CVE-2015-5165.patch b/0036-rtl8139-check-TCP-Data-Offset-field-CVE-2015-5165.patch new file mode 100644 index 0000000..093d1e4 --- /dev/null +++ b/0036-rtl8139-check-TCP-Data-Offset-field-CVE-2015-5165.patch @@ -0,0 +1,34 @@ +From: Stefan Hajnoczi +Date: Wed, 15 Jul 2015 17:39:29 +0100 +Subject: [PATCH] rtl8139: check TCP Data Offset field (CVE-2015-5165) +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The TCP Data Offset field contains the length of the header. Make sure +it is valid and does not exceed the IP data length. + +Reported-by: 朱东海(启路) +Reviewed-by: Jason Wang +Signed-off-by: Stefan Hajnoczi +(cherry picked from commit 8357946b15f0a31f73dd691b7da95f29318ed310) +--- + hw/net/rtl8139.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c +index feaa06f..1e5e827 100644 +--- a/hw/net/rtl8139.c ++++ b/hw/net/rtl8139.c +@@ -2249,6 +2249,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; diff --git a/qemu.spec b/qemu.spec index cac2815..5b8ab9c 100644 --- a/qemu.spec +++ b/qemu.spec @@ -152,7 +152,7 @@ Summary: QEMU is a FAST! processor emulator Name: qemu Version: 2.1.3 -Release: 8%{?dist} +Release: 9%{?dist} Epoch: 2 License: GPLv2+ and LGPLv2+ and BSD Group: Development/Tools @@ -232,6 +232,30 @@ Patch0019: 0019-qxl-keep-going-if-reaching-guest-bug-on-empty-area.patch Patch0020: 0020-slirp-use-less-predictable-directory-name-in-tmp-for.patch # Backport {Haswell,Broadwell}-noTSX cpu models (bz #1213053) Patch0021: 0021-target-i386-Haswell-noTSX-and-Broadwell-noTSX.patch +# Fix crash in qemu_spice_create_display (bz #1163047) +Patch0022: 0022-spice-display-fix-segfault-in-qemu_spice_create_upda.patch +# CVE-2015-3209: pcnet: multi-tmd buffer overflow in the tx path (bz +# #1230536) +Patch0023: 0023-pcnet-fix-Negative-array-index-read.patch +Patch0024: 0024-pcnet-force-the-buffer-access-to-be-in-bounds-during.patch +# CVE-2015-3214: i8254: out-of-bounds memory access (bz #1243728) +Patch0025: 0025-i8254-fix-out-of-bounds-memory-access-in-pit_ioport_.patch +# CVE-2015-5154: ide: atapi: heap overflow during I/O buffer memory +# access (bz #1247141) +Patch0026: 0026-ide-Check-array-bounds-before-writing-to-io_buffer-C.patch +Patch0027: 0027-ide-atapi-Fix-START-STOP-UNIT-command-completion.patch +Patch0028: 0028-ide-Clear-DRQ-after-handling-all-expected-accesses.patch +# CVE-2015-5745: buffer overflow in virtio-serial (bz #1251160) +Patch0029: 0029-virtio-serial-fix-ANY_LAYOUT.patch +# CVE-2015-5165: rtl8139 uninitialized heap memory information leakage +# to guest (bz #1249755) +Patch0030: 0030-rtl8139-avoid-nested-ifs-in-IP-header-parsing-CVE-20.patch +Patch0031: 0031-rtl8139-drop-tautologous-if-ip-.-statement-CVE-2015-.patch +Patch0032: 0032-rtl8139-skip-offload-on-short-Ethernet-IP-header-CVE.patch +Patch0033: 0033-rtl8139-check-IP-Header-Length-field-CVE-2015-5165.patch +Patch0034: 0034-rtl8139-check-IP-Total-Length-field-CVE-2015-5165.patch +Patch0035: 0035-rtl8139-skip-offload-on-short-TCP-header-CVE-2015-51.patch +Patch0036: 0036-rtl8139-check-TCP-Data-Offset-field-CVE-2015-5165.patch BuildRequires: SDL2-devel BuildRequires: zlib-devel @@ -798,6 +822,30 @@ CAC emulation development files. %patch0020 -p1 # Backport {Haswell,Broadwell}-noTSX cpu models (bz #1213053) %patch0021 -p1 +# Fix crash in qemu_spice_create_display (bz #1163047) +%patch0022 -p1 +# CVE-2015-3209: pcnet: multi-tmd buffer overflow in the tx path (bz +# #1230536) +%patch0023 -p1 +%patch0024 -p1 +# CVE-2015-3214: i8254: out-of-bounds memory access (bz #1243728) +%patch0025 -p1 +# CVE-2015-5154: ide: atapi: heap overflow during I/O buffer memory +# access (bz #1247141) +%patch0026 -p1 +%patch0027 -p1 +%patch0028 -p1 +# CVE-2015-5745: buffer overflow in virtio-serial (bz #1251160) +%patch0029 -p1 +# CVE-2015-5165: rtl8139 uninitialized heap memory information leakage +# to guest (bz #1249755) +%patch0030 -p1 +%patch0031 -p1 +%patch0032 -p1 +%patch0033 -p1 +%patch0034 -p1 +%patch0035 -p1 +%patch0036 -p1 %build @@ -1578,6 +1626,16 @@ getent passwd qemu >/dev/null || \ %endif %changelog +* Tue Aug 11 2015 Cole Robinson - 2:2.1.3-9 +- Fix crash in qemu_spice_create_display (bz #1163047) +- CVE-2015-3209: pcnet: multi-tmd buffer overflow in the tx path (bz #1230536) +- CVE-2015-3214: i8254: out-of-bounds memory access (bz #1243728) +- CVE-2015-5154: ide: atapi: heap overflow during I/O buffer memory access (bz + #1247141) +- CVE-2015-5745: buffer overflow in virtio-serial (bz #1251160) +- CVE-2015-5165: rtl8139 uninitialized heap memory information leakage to + guest (bz #1249755) + * Fri Jun 05 2015 Cole Robinson - 2:2.1.3-8 - User interface freezes when entering space character in Xfig (bz #1151253) - CVE-2015-4037: insecure temporary file use in /net/slirp.c (bz #1222894)