34b321
From 4fef3479339001ef3ea529fb0552533fae422240 Mon Sep 17 00:00:00 2001
34b321
From: Laszlo Ersek <lersek@redhat.com>
34b321
Date: Fri, 5 Feb 2016 14:26:18 +0100
34b321
Subject: [PATCH 1/5] e1000: eliminate infinite loops on out-of-bounds transfer
34b321
 start
34b321
34b321
RH-Author: Laszlo Ersek <lersek@redhat.com>
34b321
Message-id: <1454682378-29144-2-git-send-email-lersek@redhat.com>
34b321
Patchwork-id: 69116
34b321
O-Subject: [RHEL-7.3 qemu-kvm PATCH 1/1] e1000: eliminate infinite loops on out-of-bounds transfer start
34b321
Bugzilla: 1296044
34b321
RH-Acked-by: Xiao Wang <jasowang@redhat.com>
34b321
RH-Acked-by: P J P <ppandit@redhat.com>
34b321
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
34b321
34b321
The start_xmit() and e1000_receive_iov() functions implement DMA transfers
34b321
iterating over a set of descriptors that the guest's e1000 driver
34b321
prepares:
34b321
34b321
- the TDLEN and RDLEN registers store the total size of the descriptor
34b321
  area,
34b321
34b321
- while the TDH and RDH registers store the offset (in whole tx / rx
34b321
  descriptors) into the area where the transfer is supposed to start.
34b321
34b321
Each time a descriptor is processed, the TDH and RDH register is bumped
34b321
(as appropriate for the transfer direction).
34b321
34b321
QEMU already contains logic to deal with bogus transfers submitted by the
34b321
guest:
34b321
34b321
- Normally, the transmit case wants to increase TDH from its initial value
34b321
  to TDT. (TDT is allowed to be numerically smaller than the initial TDH
34b321
  value; wrapping at or above TDLEN bytes to zero is normal.) The failsafe
34b321
  that QEMU currently has here is a check against reaching the original
34b321
  TDH value again -- a complete wraparound, which should never happen.
34b321
34b321
- In the receive case RDH is increased from its initial value until
34b321
  "total_size" bytes have been received; preferably in a single step, or
34b321
  in "s->rxbuf_size" byte steps, if the latter is smaller. However, null
34b321
  RX descriptors are skipped without receiving data, while RDH is
34b321
  incremented just the same. QEMU tries to prevent an infinite loop
34b321
  (processing only null RX descriptors) by detecting whether RDH assumes
34b321
  its original value during the loop. (Again, wrapping from RDLEN to 0 is
34b321
  normal.)
34b321
34b321
What both directions miss is that the guest could program TDLEN and RDLEN
34b321
so low, and the initial TDH and RDH so high, that these registers will
34b321
immediately be truncated to zero, and then never reassume their initial
34b321
values in the loop -- a full wraparound will never occur.
34b321
34b321
The condition that expresses this is:
34b321
34b321
  xdh_start >= s->mac_reg[XDLEN] / sizeof(desc)
34b321
34b321
i.e., TDH or RDH start out after the last whole rx or tx descriptor that
34b321
fits into the TDLEN or RDLEN sized area.
34b321
34b321
This condition could be checked before we enter the loops, but
34b321
pci_dma_read() / pci_dma_write() knows how to fill in buffers safely for
34b321
bogus DMA addresses, so we just extend the existing failsafes with the
34b321
above condition.
34b321
34b321
This is CVE-2016-1981.
34b321
34b321
Cc: "Michael S. Tsirkin" <mst@redhat.com>
34b321
Cc: Petr Matousek <pmatouse@redhat.com>
34b321
Cc: Stefano Stabellini <stefano.stabellini@eu.citrix.com>
34b321
Cc: Prasad Pandit <ppandit@redhat.com>
34b321
Cc: Michael Roth <mdroth@linux.vnet.ibm.com>
34b321
Cc: Jason Wang <jasowang@redhat.com>
34b321
Cc: qemu-stable@nongnu.org
34b321
RHBZ: https://bugzilla.redhat.com/show_bug.cgi?id=1296044
34b321
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
34b321
Reviewed-by: Jason Wang <jasowang@redhat.com>
34b321
Signed-off-by: Jason Wang <jasowang@redhat.com>
34b321
(cherry picked from commit dd793a74882477ca38d49e191110c17dfee51dcc)
34b321
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
34b321
---
34b321
 hw/net/e1000.c | 6 ++++--
34b321
 1 file changed, 4 insertions(+), 2 deletions(-)
34b321
34b321
diff --git a/hw/net/e1000.c b/hw/net/e1000.c
34b321
index 87a84a7..2cd38bc 100644
34b321
--- a/hw/net/e1000.c
34b321
+++ b/hw/net/e1000.c
34b321
@@ -697,7 +697,8 @@ start_xmit(E1000State *s)
34b321
          * bogus values to TDT/TDLEN.
34b321
          * there's nothing too intelligent we could do about this.
34b321
          */
34b321
-        if (s->mac_reg[TDH] == tdh_start) {
34b321
+        if (s->mac_reg[TDH] == tdh_start ||
34b321
+            tdh_start >= s->mac_reg[TDLEN] / sizeof(desc)) {
34b321
             DBGOUT(TXERR, "TDH wraparound @%x, TDT %x, TDLEN %x\n",
34b321
                    tdh_start, s->mac_reg[TDT], s->mac_reg[TDLEN]);
34b321
             break;
34b321
@@ -902,7 +903,8 @@ e1000_receive(NetClientState *nc, const uint8_t *buf, size_t size)
34b321
         if (++s->mac_reg[RDH] * sizeof(desc) >= s->mac_reg[RDLEN])
34b321
             s->mac_reg[RDH] = 0;
34b321
         /* see comment in start_xmit; same here */
34b321
-        if (s->mac_reg[RDH] == rdh_start) {
34b321
+        if (s->mac_reg[RDH] == rdh_start ||
34b321
+            rdh_start >= s->mac_reg[RDLEN] / sizeof(desc)) {
34b321
             DBGOUT(RXERR, "RDH wraparound @%x, RDT %x, RDLEN %x\n",
34b321
                    rdh_start, s->mac_reg[RDT], s->mac_reg[RDLEN]);
34b321
             set_ics(s, 0, E1000_ICS_RXO);
34b321
-- 
34b321
1.8.3.1
34b321