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