9b4860
From: Gerd Hoffmann <kraxel@redhat.com>
9b4860
Date: Mon, 30 May 2016 09:09:20 +0200
9b4860
Subject: [PATCH] vmsvga: shadow fifo registers
9b4860
9b4860
The fifo is normal ram.  So kvm vcpu threads and qemu iothread can
9b4860
access the fifo in parallel without syncronization.  Which in turn
9b4860
implies we can't use the fifo pointers in-place because the guest
9b4860
can try changing them underneath us.  So add shadows for them, to
9b4860
make sure the guest can't modify them after we've applied sanity
9b4860
checks.
9b4860
9b4860
Fixes: CVE-2016-4454
9b4860
Cc: qemu-stable@nongnu.org
9b4860
Cc: P J P <ppandit@redhat.com>
9b4860
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
9b4860
Message-id: 1464592161-18348-4-git-send-email-kraxel@redhat.com
9b4860
(cherry picked from commit 7e486f7577764a07aa35588e119903c80a5c30a2)
9b4860
---
9b4860
 hw/display/vmware_vga.c | 57 ++++++++++++++++++++++++-------------------------
9b4860
 1 file changed, 28 insertions(+), 29 deletions(-)
9b4860
9b4860
diff --git a/hw/display/vmware_vga.c b/hw/display/vmware_vga.c
9b4860
index 0329d9f..8b9feb8 100644
9b4860
--- a/hw/display/vmware_vga.c
9b4860
+++ b/hw/display/vmware_vga.c
9b4860
@@ -64,17 +64,11 @@ struct vmsvga_state_s {
9b4860
     uint8_t *fifo_ptr;
9b4860
     unsigned int fifo_size;
9b4860
 
9b4860
-    union {
9b4860
-        uint32_t *fifo;
9b4860
-        struct QEMU_PACKED {
9b4860
-            uint32_t min;
9b4860
-            uint32_t max;
9b4860
-            uint32_t next_cmd;
9b4860
-            uint32_t stop;
9b4860
-            /* Add registers here when adding capabilities.  */
9b4860
-            uint32_t fifo[0];
9b4860
-        } *cmd;
9b4860
-    };
9b4860
+    uint32_t *fifo;
9b4860
+    uint32_t fifo_min;
9b4860
+    uint32_t fifo_max;
9b4860
+    uint32_t fifo_next;
9b4860
+    uint32_t fifo_stop;
9b4860
 
9b4860
 #define REDRAW_FIFO_LEN  512
9b4860
     struct vmsvga_rect_s {
9b4860
@@ -196,7 +190,7 @@ enum {
9b4860
      */
9b4860
     SVGA_FIFO_MIN = 0,
9b4860
     SVGA_FIFO_MAX,      /* The distance from MIN to MAX must be at least 10K */
9b4860
-    SVGA_FIFO_NEXT_CMD,
9b4860
+    SVGA_FIFO_NEXT,
9b4860
     SVGA_FIFO_STOP,
9b4860
 
9b4860
     /*
9b4860
@@ -544,8 +538,6 @@ static inline void vmsvga_cursor_define(struct vmsvga_state_s *s,
9b4860
 }
9b4860
 #endif
9b4860
 
9b4860
-#define CMD(f)  le32_to_cpu(s->cmd->f)
9b4860
-
9b4860
 static inline int vmsvga_fifo_length(struct vmsvga_state_s *s)
9b4860
 {
9b4860
     int num;
9b4860
@@ -554,38 +546,44 @@ static inline int vmsvga_fifo_length(struct vmsvga_state_s *s)
9b4860
         return 0;
9b4860
     }
9b4860
 
9b4860
+    s->fifo_min  = le32_to_cpu(s->fifo[SVGA_FIFO_MIN]);
9b4860
+    s->fifo_max  = le32_to_cpu(s->fifo[SVGA_FIFO_MAX]);
9b4860
+    s->fifo_next = le32_to_cpu(s->fifo[SVGA_FIFO_NEXT]);
9b4860
+    s->fifo_stop = le32_to_cpu(s->fifo[SVGA_FIFO_STOP]);
9b4860
+
9b4860
     /* Check range and alignment.  */
9b4860
-    if ((CMD(min) | CMD(max) | CMD(next_cmd) | CMD(stop)) & 3) {
9b4860
+    if ((s->fifo_min | s->fifo_max | s->fifo_next | s->fifo_stop) & 3) {
9b4860
         return 0;
9b4860
     }
9b4860
-    if (CMD(min) < (uint8_t *) s->cmd->fifo - (uint8_t *) s->fifo) {
9b4860
+    if (s->fifo_min < sizeof(uint32_t) * 4) {
9b4860
         return 0;
9b4860
     }
9b4860
-    if (CMD(max) > SVGA_FIFO_SIZE ||
9b4860
-        CMD(min) >= SVGA_FIFO_SIZE ||
9b4860
-        CMD(stop) >= SVGA_FIFO_SIZE ||
9b4860
-        CMD(next_cmd) >= SVGA_FIFO_SIZE) {
9b4860
+    if (s->fifo_max > SVGA_FIFO_SIZE ||
9b4860
+        s->fifo_min >= SVGA_FIFO_SIZE ||
9b4860
+        s->fifo_stop >= SVGA_FIFO_SIZE ||
9b4860
+        s->fifo_next >= SVGA_FIFO_SIZE) {
9b4860
         return 0;
9b4860
     }
9b4860
-    if (CMD(max) < CMD(min) + 10 * 1024) {
9b4860
+    if (s->fifo_max < s->fifo_min + 10 * 1024) {
9b4860
         return 0;
9b4860
     }
9b4860
 
9b4860
-    num = CMD(next_cmd) - CMD(stop);
9b4860
+    num = s->fifo_next - s->fifo_stop;
9b4860
     if (num < 0) {
9b4860
-        num += CMD(max) - CMD(min);
9b4860
+        num += s->fifo_max - s->fifo_min;
9b4860
     }
9b4860
     return num >> 2;
9b4860
 }
9b4860
 
9b4860
 static inline uint32_t vmsvga_fifo_read_raw(struct vmsvga_state_s *s)
9b4860
 {
9b4860
-    uint32_t cmd = s->fifo[CMD(stop) >> 2];
9b4860
+    uint32_t cmd = s->fifo[s->fifo_stop >> 2];
9b4860
 
9b4860
-    s->cmd->stop = cpu_to_le32(CMD(stop) + 4);
9b4860
-    if (CMD(stop) >= CMD(max)) {
9b4860
-        s->cmd->stop = s->cmd->min;
9b4860
+    s->fifo_stop += 4;
9b4860
+    if (s->fifo_stop >= s->fifo_max) {
9b4860
+        s->fifo_stop = s->fifo_min;
9b4860
     }
9b4860
+    s->fifo[SVGA_FIFO_STOP] = cpu_to_le32(s->fifo_stop);
9b4860
     return cmd;
9b4860
 }
9b4860
 
9b4860
@@ -605,7 +603,7 @@ static void vmsvga_fifo_run(struct vmsvga_state_s *s)
9b4860
     len = vmsvga_fifo_length(s);
9b4860
     while (len > 0) {
9b4860
         /* May need to go back to the start of the command if incomplete */
9b4860
-        cmd_start = s->cmd->stop;
9b4860
+        cmd_start = s->fifo_stop;
9b4860
 
9b4860
         switch (cmd = vmsvga_fifo_read(s)) {
9b4860
         case SVGA_CMD_UPDATE:
9b4860
@@ -761,7 +759,8 @@ static void vmsvga_fifo_run(struct vmsvga_state_s *s)
9b4860
             break;
9b4860
 
9b4860
         rewind:
9b4860
-            s->cmd->stop = cmd_start;
9b4860
+            s->fifo_stop = cmd_start;
9b4860
+            s->fifo[SVGA_FIFO_STOP] = cpu_to_le32(s->fifo_stop);
9b4860
             break;
9b4860
         }
9b4860
     }