958e1b
From 6febc393af5d81e2c3f6f0d2f0e5b7dd58d4974a Mon Sep 17 00:00:00 2001
958e1b
From: Gerd Hoffmann <kraxel@redhat.com>
958e1b
Date: Wed, 3 Dec 2014 09:17:55 +0100
958e1b
Subject: [PATCH 2/3] cirrus: fix blit region check
958e1b
958e1b
RH-Author: Gerd Hoffmann <kraxel@redhat.com>
958e1b
Message-id: <1417594676-5663-2-git-send-email-kraxel@redhat.com>
958e1b
O-Subject: [RHEL-7.1 qemu-kvm PATCH 1/2] cirrus: fix blit region check
958e1b
Bugzilla: 1169456
958e1b
RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com>
958e1b
RH-Acked-by: Laszlo Ersek <lersek@redhat.com>
958e1b
RH-Acked-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
958e1b
958e1b
Issues:
958e1b
 * Doesn't check pitches correctly in case it is negative.
958e1b
 * Doesn't check width at all.
958e1b
958e1b
Turn macro into functions while being at it, also factor out the check
958e1b
for one region which we then can simply call twice for src + dst.
958e1b
958e1b
This is CVE-2014-8106.
958e1b
958e1b
Reported-by: Paolo Bonzini <pbonzini@redhat.com>
958e1b
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
958e1b
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
958e1b
(cherry picked from commit d3532a0db02296e687711b8cdc7791924efccea0)
958e1b
---
958e1b
 hw/display/cirrus_vga.c | 61 +++++++++++++++++++++++++++++++++++--------------
958e1b
 1 file changed, 44 insertions(+), 17 deletions(-)
958e1b
958e1b
diff --git a/hw/display/cirrus_vga.c b/hw/display/cirrus_vga.c
958e1b
index bfaa0b0..9ae8313 100644
958e1b
--- a/hw/display/cirrus_vga.c
958e1b
+++ b/hw/display/cirrus_vga.c
958e1b
@@ -172,20 +172,6 @@
958e1b
 
958e1b
 #define CIRRUS_PNPMMIO_SIZE         0x1000
958e1b
 
958e1b
-#define BLTUNSAFE(s) \
958e1b
-    ( \
958e1b
-        ( /* check dst is within bounds */ \
958e1b
-            (s)->cirrus_blt_height * ABS((s)->cirrus_blt_dstpitch) \
958e1b
-                + ((s)->cirrus_blt_dstaddr & (s)->cirrus_addr_mask) > \
958e1b
-                    (s)->vga.vram_size \
958e1b
-        ) || \
958e1b
-        ( /* check src is within bounds */ \
958e1b
-            (s)->cirrus_blt_height * ABS((s)->cirrus_blt_srcpitch) \
958e1b
-                + ((s)->cirrus_blt_srcaddr & (s)->cirrus_addr_mask) > \
958e1b
-                    (s)->vga.vram_size \
958e1b
-        ) \
958e1b
-    )
958e1b
-
958e1b
 struct CirrusVGAState;
958e1b
 typedef void (*cirrus_bitblt_rop_t) (struct CirrusVGAState *s,
958e1b
                                      uint8_t * dst, const uint8_t * src,
958e1b
@@ -278,6 +264,46 @@ static void cirrus_update_memory_access(CirrusVGAState *s);
958e1b
  *
958e1b
  ***************************************/
958e1b
 
958e1b
+static bool blit_region_is_unsafe(struct CirrusVGAState *s,
958e1b
+                                  int32_t pitch, int32_t addr)
958e1b
+{
958e1b
+    if (pitch < 0) {
958e1b
+        int64_t min = addr
958e1b
+            + ((int64_t)s->cirrus_blt_height-1) * pitch;
958e1b
+        int32_t max = addr
958e1b
+            + s->cirrus_blt_width;
958e1b
+        if (min < 0 || max >= s->vga.vram_size) {
958e1b
+            return true;
958e1b
+        }
958e1b
+    } else {
958e1b
+        int64_t max = addr
958e1b
+            + ((int64_t)s->cirrus_blt_height-1) * pitch
958e1b
+            + s->cirrus_blt_width;
958e1b
+        if (max >= s->vga.vram_size) {
958e1b
+            return true;
958e1b
+        }
958e1b
+    }
958e1b
+    return false;
958e1b
+}
958e1b
+
958e1b
+static bool blit_is_unsafe(struct CirrusVGAState *s)
958e1b
+{
958e1b
+    /* should be the case, see cirrus_bitblt_start */
958e1b
+    assert(s->cirrus_blt_width > 0);
958e1b
+    assert(s->cirrus_blt_height > 0);
958e1b
+
958e1b
+    if (blit_region_is_unsafe(s, s->cirrus_blt_dstpitch,
958e1b
+                              s->cirrus_blt_dstaddr & s->cirrus_addr_mask)) {
958e1b
+        return true;
958e1b
+    }
958e1b
+    if (blit_region_is_unsafe(s, s->cirrus_blt_srcpitch,
958e1b
+                              s->cirrus_blt_srcaddr & s->cirrus_addr_mask)) {
958e1b
+        return true;
958e1b
+    }
958e1b
+
958e1b
+    return false;
958e1b
+}
958e1b
+
958e1b
 static void cirrus_bitblt_rop_nop(CirrusVGAState *s,
958e1b
                                   uint8_t *dst,const uint8_t *src,
958e1b
                                   int dstpitch,int srcpitch,
958e1b
@@ -635,7 +661,7 @@ static int cirrus_bitblt_common_patterncopy(CirrusVGAState * s,
958e1b
 
958e1b
     dst = s->vga.vram_ptr + (s->cirrus_blt_dstaddr & s->cirrus_addr_mask);
958e1b
 
958e1b
-    if (BLTUNSAFE(s))
958e1b
+    if (blit_is_unsafe(s))
958e1b
         return 0;
958e1b
 
958e1b
     (*s->cirrus_rop) (s, dst, src,
958e1b
@@ -653,8 +679,9 @@ static int cirrus_bitblt_solidfill(CirrusVGAState *s, int blt_rop)
958e1b
 {
958e1b
     cirrus_fill_t rop_func;
958e1b
 
958e1b
-    if (BLTUNSAFE(s))
958e1b
+    if (blit_is_unsafe(s)) {
958e1b
         return 0;
958e1b
+    }
958e1b
     rop_func = cirrus_fill[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1];
958e1b
     rop_func(s, s->vga.vram_ptr + (s->cirrus_blt_dstaddr & s->cirrus_addr_mask),
958e1b
              s->cirrus_blt_dstpitch,
958e1b
@@ -751,7 +778,7 @@ static void cirrus_do_copy(CirrusVGAState *s, int dst, int src, int w, int h)
958e1b
 
958e1b
 static int cirrus_bitblt_videotovideo_copy(CirrusVGAState * s)
958e1b
 {
958e1b
-    if (BLTUNSAFE(s))
958e1b
+    if (blit_is_unsafe(s))
958e1b
         return 0;
958e1b
 
958e1b
     cirrus_do_copy(s, s->cirrus_blt_dstaddr - s->vga.start_addr,
958e1b
-- 
958e1b
1.8.3.1
958e1b