dcavalca / rpms / qemu

Forked from rpms/qemu 11 months ago
Clone

Blame 0019-vmsvga-shadow-fifo-registers.patch

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