9ae3a8
From 90ded2892509c6c62140b907c7d036964cf312d5 Mon Sep 17 00:00:00 2001
9ae3a8
From: Gerd Hoffmann <kraxel@redhat.com>
9ae3a8
Date: Fri, 20 Oct 2017 11:06:18 +0200
9ae3a8
Subject: [PATCH 3/7] vga: handle cirrus vbe mode wraparounds.
9ae3a8
9ae3a8
RH-Author: Gerd Hoffmann <kraxel@redhat.com>
9ae3a8
Message-id: <20171020110619.2541-11-kraxel@redhat.com>
9ae3a8
Patchwork-id: 77404
9ae3a8
O-Subject: [RHEL-7.5 qemu-kvm PATCH 10/11] vga: handle cirrus vbe mode wraparounds.
9ae3a8
Bugzilla: 1501295
9ae3a8
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
9ae3a8
RH-Acked-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
9ae3a8
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
9ae3a8
9ae3a8
Commit "3d90c62548 vga: stop passing pointers to vga_draw_line*
9ae3a8
functions" is incomplete.  It doesn't handle the case that the vga
9ae3a8
rendering code tries to create a shared surface, i.e. a pixman image
9ae3a8
backed by vga video memory.  That can not work in case the guest display
9ae3a8
wraps from end of video memory to the start.  So force shadowing in that
9ae3a8
case.  Also adjust the snapshot region calculation.
9ae3a8
9ae3a8
Can trigger with cirrus only, when programming vbe modes using the bochs
9ae3a8
api (stdvga, also qxl and virtio-vga in vga compat mode) wrap arounds
9ae3a8
can't happen.
9ae3a8
9ae3a8
Fixes: CVE-2017-13672
9ae3a8
Fixes: 3d90c6254863693a6b13d918d2b8682e08bbc681
9ae3a8
Cc: P J P <ppandit@redhat.com>
9ae3a8
Reported-by: David Buchanan <d@vidbuchanan.co.uk>
9ae3a8
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
9ae3a8
Message-id: 20171010141323.14049-3-kraxel@redhat.com
9ae3a8
(cherry picked from commit 28f77de26a4f9995458ddeb9d34bb06c0193bdc9)
9ae3a8
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
9ae3a8
---
9ae3a8
 hw/display/vga.c | 31 ++++++++++++++++++++++++-------
9ae3a8
 1 file changed, 24 insertions(+), 7 deletions(-)
9ae3a8
9ae3a8
diff --git a/hw/display/vga.c b/hw/display/vga.c
9ae3a8
index a343a0a..c40744f 100644
9ae3a8
--- a/hw/display/vga.c
9ae3a8
+++ b/hw/display/vga.c
9ae3a8
@@ -1505,12 +1505,12 @@ static void vga_draw_graphic(VGACommonState *s, int full_update)
9ae3a8
     DisplaySurface *surface = qemu_console_surface(s->con);
9ae3a8
     int y1, y, update, linesize, y_start, double_scan, mask, depth;
9ae3a8
     int width, height, shift_control, bwidth, bits;
9ae3a8
-    ram_addr_t page0, page1, page_min, page_max;
9ae3a8
+    ram_addr_t page0, page1, page_min, page_max, region_start, region_end;
9ae3a8
     int disp_width, multi_scan, multi_run;
9ae3a8
     uint8_t *d;
9ae3a8
     uint32_t v, addr1, addr;
9ae3a8
     vga_draw_line_func *vga_draw_line;
9ae3a8
-    bool share_surface;
9ae3a8
+    bool share_surface, force_shadow = false;
9ae3a8
 #if defined(TARGET_WORDS_BIGENDIAN)
9ae3a8
     static const bool big_endian_fb = true;
9ae3a8
 #else
9ae3a8
@@ -1530,6 +1530,15 @@ static void vga_draw_graphic(VGACommonState *s, int full_update)
9ae3a8
     s->get_resolution(s, &width, &height);
9ae3a8
     disp_width = width;
9ae3a8
 
9ae3a8
+    region_start = (s->start_addr * 4);
9ae3a8
+    region_end = region_start + s->line_offset * height;
9ae3a8
+    if (region_end > s->vbe_size) {
9ae3a8
+        /* wraps around (can happen with cirrus vbe modes) */
9ae3a8
+        region_start = 0;
9ae3a8
+        region_end = s->vbe_size;
9ae3a8
+        force_shadow = true;
9ae3a8
+    }
9ae3a8
+
9ae3a8
     shift_control = (s->gr[VGA_GFX_MODE] >> 5) & 3;
9ae3a8
     double_scan = (s->cr[VGA_CRTC_MAX_SCAN] >> 7);
9ae3a8
     if (shift_control != 1) {
9ae3a8
@@ -1560,7 +1569,7 @@ static void vga_draw_graphic(VGACommonState *s, int full_update)
9ae3a8
 
9ae3a8
     depth = s->get_bpp(s);
9ae3a8
 
9ae3a8
-    share_surface = (!s->force_shadow) &&
9ae3a8
+    share_surface = (!s->force_shadow) && !force_shadow &&
9ae3a8
             ( depth == 32 || (depth == 16 && !byteswap) );
9ae3a8
     if (s->line_offset != s->last_line_offset ||
9ae3a8
         disp_width != s->last_width ||
9ae3a8
@@ -1680,10 +1689,18 @@ static void vga_draw_graphic(VGACommonState *s, int full_update)
9ae3a8
             addr = (addr & ~0x8000) | ((y1 & 2) << 14);
9ae3a8
         }
9ae3a8
         update = full_update;
9ae3a8
-        page0 = addr;
9ae3a8
-        page1 = addr + bwidth - 1;
9ae3a8
-        update |= memory_region_get_dirty(&s->vram, page0, page1 - page0,
9ae3a8
-                                          DIRTY_MEMORY_VGA);
9ae3a8
+        page0 = addr & s->vbe_size_mask;
9ae3a8
+        page1 = (addr + bwidth - 1) & s->vbe_size_mask;
9ae3a8
+        if (page1 < page0) {
9ae3a8
+            /* scanline wraps from end of video memory to the start */
9ae3a8
+            update |= memory_region_get_dirty(&s->vram, page0, 0,
9ae3a8
+                                              DIRTY_MEMORY_VGA);
9ae3a8
+            update |= memory_region_get_dirty(&s->vram, page1, 0,
9ae3a8
+                                              DIRTY_MEMORY_VGA);
9ae3a8
+        } else {
9ae3a8
+            update |= memory_region_get_dirty(&s->vram, page0, page1 - page0,
9ae3a8
+                                              DIRTY_MEMORY_VGA);
9ae3a8
+        }
9ae3a8
         /* explicit invalidation for the hardware cursor */
9ae3a8
         update |= (s->invalidated_y_table[y >> 5] >> (y & 0x1f)) & 1;
9ae3a8
         if (update) {
9ae3a8
-- 
9ae3a8
1.8.3.1
9ae3a8