9ae3a8
From 9bd81fb917c9ac22055e0dc7b3a89a22d5cfbfc1 Mon Sep 17 00:00:00 2001
9ae3a8
From: Gerd Hoffmann <kraxel@redhat.com>
9ae3a8
Date: Mon, 27 Mar 2017 10:01:17 +0200
9ae3a8
Subject: [PATCH 2/7] cirrus/vnc: zap bitblit support from console code.
9ae3a8
9ae3a8
RH-Author: Gerd Hoffmann <kraxel@redhat.com>
9ae3a8
Message-id: <1490608882-10242-3-git-send-email-kraxel@redhat.com>
9ae3a8
Patchwork-id: 74554
9ae3a8
O-Subject: [RHEL-7.4 qemu-kvm PATCH v2 2/7] cirrus/vnc: zap bitblit support from console code.
9ae3a8
Bugzilla: 1430060
9ae3a8
RH-Acked-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
9ae3a8
RH-Acked-by: Laurent Vivier <lvivier@redhat.com>
9ae3a8
RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com>
9ae3a8
9ae3a8
There is a special code path (dpy_gfx_copy) to allow graphic emulation
9ae3a8
notify user interface code about bitblit operations carryed out by
9ae3a8
guests.  It is supported by cirrus and vnc server.  The intended purpose
9ae3a8
is to optimize display scrolls and just send over the scroll op instead
9ae3a8
of a full display update.
9ae3a8
9ae3a8
This is rarely used these days though because modern guests simply don't
9ae3a8
use the cirrus blitter any more.  Any linux guest using the cirrus drm
9ae3a8
driver doesn't.  Any windows guest newer than winxp doesn't ship with a
9ae3a8
cirrus driver any more and thus uses the cirrus as simple framebuffer.
9ae3a8
9ae3a8
So this code tends to bitrot and bugs can go unnoticed for a long time.
9ae3a8
See for example commit "3e10c3e vnc: fix qemu crash because of SIGSEGV"
9ae3a8
which fixes a bug lingering in the code for almost a year, added by
9ae3a8
commit "c7628bf vnc: only alloc server surface with clients connected".
9ae3a8
9ae3a8
Also the vnc server will throttle the frame rate in case it figures the
9ae3a8
network can't keep up (send buffers are full).  This doesn't work with
9ae3a8
dpy_gfx_copy, for any copy operation sent to the vnc client we have to
9ae3a8
send all outstanding updates beforehand, otherwise the vnc client might
9ae3a8
run the client side blit on outdated data and thereby corrupt the
9ae3a8
display.  So this dpy_gfx_copy "optimization" might even make things
9ae3a8
worse on slow network links.
9ae3a8
9ae3a8
Lets kill it once for all.
9ae3a8
9ae3a8
Oh, and one more reason: Turns out (after writing the patch) we have a
9ae3a8
security bug in that code path ...
9ae3a8
9ae3a8
Fixes: CVE-2016-9603
9ae3a8
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
9ae3a8
Message-id: 1489494419-14340-1-git-send-email-kraxel@redhat.com
9ae3a8
(cherry picked from commit 50628d3479e4f9aa97e323506856e394fe7ad7a6)
9ae3a8
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
9ae3a8
9ae3a8
Conflicts:
9ae3a8
	include/ui/console.h
9ae3a8
	ui/vnc.c
9ae3a8
---
9ae3a8
 hw/display/cirrus_vga.c | 12 ++----
9ae3a8
 include/ui/console.h    |  7 ----
9ae3a8
 ui/console.c            | 28 --------------
9ae3a8
 ui/vnc.c                | 99 -------------------------------------------------
9ae3a8
 4 files changed, 3 insertions(+), 143 deletions(-)
9ae3a8
9ae3a8
diff --git a/hw/display/cirrus_vga.c b/hw/display/cirrus_vga.c
9ae3a8
index 1b972db..83cef70 100644
9ae3a8
--- a/hw/display/cirrus_vga.c
9ae3a8
+++ b/hw/display/cirrus_vga.c
9ae3a8
@@ -790,21 +790,15 @@ static int cirrus_do_copy(CirrusVGAState *s, int dst, int src, int w, int h)
9ae3a8
         }
9ae3a8
     }
9ae3a8
 
9ae3a8
-    /* we have to flush all pending changes so that the copy
9ae3a8
-       is generated at the appropriate moment in time */
9ae3a8
-    if (notify)
9ae3a8
-        graphic_hw_update(s->vga.con);
9ae3a8
-
9ae3a8
     (*s->cirrus_rop) (s, s->vga.vram_ptr + s->cirrus_blt_dstaddr,
9ae3a8
                       s->vga.vram_ptr + s->cirrus_blt_srcaddr,
9ae3a8
 		      s->cirrus_blt_dstpitch, s->cirrus_blt_srcpitch,
9ae3a8
 		      s->cirrus_blt_width, s->cirrus_blt_height);
9ae3a8
 
9ae3a8
     if (notify) {
9ae3a8
-        qemu_console_copy(s->vga.con,
9ae3a8
-			  sx, sy, dx, dy,
9ae3a8
-			  s->cirrus_blt_width / depth,
9ae3a8
-			  s->cirrus_blt_height);
9ae3a8
+        dpy_gfx_update(s->vga.con, dx, dy,
9ae3a8
+                       s->cirrus_blt_width / depth,
9ae3a8
+                       s->cirrus_blt_height);
9ae3a8
     }
9ae3a8
 
9ae3a8
     /* we don't have to notify the display that this portion has
9ae3a8
diff --git a/include/ui/console.h b/include/ui/console.h
9ae3a8
index 4307b5f..7f5fa66 100644
9ae3a8
--- a/include/ui/console.h
9ae3a8
+++ b/include/ui/console.h
9ae3a8
@@ -159,9 +159,6 @@ typedef struct DisplayChangeListenerOps {
9ae3a8
                            int x, int y, int w, int h);
9ae3a8
     void (*dpy_gfx_switch)(DisplayChangeListener *dcl,
9ae3a8
                            struct DisplaySurface *new_surface);
9ae3a8
-    void (*dpy_gfx_copy)(DisplayChangeListener *dcl,
9ae3a8
-                         int src_x, int src_y,
9ae3a8
-                         int dst_x, int dst_y, int w, int h);
9ae3a8
 
9ae3a8
     void (*dpy_text_cursor)(DisplayChangeListener *dcl,
9ae3a8
                             int x, int y);
9ae3a8
@@ -216,8 +213,6 @@ void unregister_displaychangelistener(DisplayChangeListener *dcl);
9ae3a8
 void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h);
9ae3a8
 void dpy_gfx_replace_surface(QemuConsole *con,
9ae3a8
                              DisplaySurface *surface);
9ae3a8
-void dpy_gfx_copy(QemuConsole *con, int src_x, int src_y,
9ae3a8
-                  int dst_x, int dst_y, int w, int h);
9ae3a8
 void dpy_text_cursor(QemuConsole *con, int x, int y);
9ae3a8
 void dpy_text_update(QemuConsole *con, int x, int y, int w, int h);
9ae3a8
 void dpy_text_resize(QemuConsole *con, int w, int h);
9ae3a8
@@ -295,8 +290,6 @@ void text_consoles_set_display(DisplayState *ds);
9ae3a8
 void console_select(unsigned int index);
9ae3a8
 void console_color_init(DisplayState *ds);
9ae3a8
 void qemu_console_resize(QemuConsole *con, int width, int height);
9ae3a8
-void qemu_console_copy(QemuConsole *con, int src_x, int src_y,
9ae3a8
-                       int dst_x, int dst_y, int w, int h);
9ae3a8
 DisplaySurface *qemu_console_surface(QemuConsole *con);
9ae3a8
 DisplayState *qemu_console_displaystate(QemuConsole *console);
9ae3a8
 
9ae3a8
diff --git a/ui/console.c b/ui/console.c
9ae3a8
index d422083..fb08ec0 100644
9ae3a8
--- a/ui/console.c
9ae3a8
+++ b/ui/console.c
9ae3a8
@@ -1461,27 +1461,6 @@ void dpy_refresh(DisplayState *s)
9ae3a8
     }
9ae3a8
 }
9ae3a8
 
9ae3a8
-void dpy_gfx_copy(QemuConsole *con, int src_x, int src_y,
9ae3a8
-                  int dst_x, int dst_y, int w, int h)
9ae3a8
-{
9ae3a8
-    DisplayState *s = con->ds;
9ae3a8
-    DisplayChangeListener *dcl;
9ae3a8
-
9ae3a8
-    if (!qemu_console_is_visible(con)) {
9ae3a8
-        return;
9ae3a8
-    }
9ae3a8
-    QLIST_FOREACH(dcl, &s->listeners, next) {
9ae3a8
-        if (con != (dcl->con ? dcl->con : active_console)) {
9ae3a8
-            continue;
9ae3a8
-        }
9ae3a8
-        if (dcl->ops->dpy_gfx_copy) {
9ae3a8
-            dcl->ops->dpy_gfx_copy(dcl, src_x, src_y, dst_x, dst_y, w, h);
9ae3a8
-        } else { /* TODO */
9ae3a8
-            dcl->ops->dpy_gfx_update(dcl, dst_x, dst_y, w, h);
9ae3a8
-        }
9ae3a8
-    }
9ae3a8
-}
9ae3a8
-
9ae3a8
 void dpy_text_cursor(QemuConsole *con, int x, int y)
9ae3a8
 {
9ae3a8
     DisplayState *s = con->ds;
9ae3a8
@@ -1843,13 +1822,6 @@ void qemu_console_resize(QemuConsole *s, int width, int height)
9ae3a8
     dpy_gfx_replace_surface(s, surface);
9ae3a8
 }
9ae3a8
 
9ae3a8
-void qemu_console_copy(QemuConsole *con, int src_x, int src_y,
9ae3a8
-                       int dst_x, int dst_y, int w, int h)
9ae3a8
-{
9ae3a8
-    assert(con->console_type == GRAPHIC_CONSOLE);
9ae3a8
-    dpy_gfx_copy(con, src_x, src_y, dst_x, dst_y, w, h);
9ae3a8
-}
9ae3a8
-
9ae3a8
 DisplaySurface *qemu_console_surface(QemuConsole *console)
9ae3a8
 {
9ae3a8
     return console->surface;
9ae3a8
diff --git a/ui/vnc.c b/ui/vnc.c
9ae3a8
index b68918e..1834db0 100644
9ae3a8
--- a/ui/vnc.c
9ae3a8
+++ b/ui/vnc.c
9ae3a8
@@ -417,7 +417,6 @@ out_error:
9ae3a8
 */
9ae3a8
 
9ae3a8
 static int vnc_update_client(VncState *vs, int has_dirty);
9ae3a8
-static int vnc_update_client_sync(VncState *vs, int has_dirty);
9ae3a8
 static void vnc_disconnect_start(VncState *vs);
9ae3a8
 
9ae3a8
 static void vnc_colordepth(VncState *vs);
9ae3a8
@@ -728,96 +727,6 @@ int vnc_send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
9ae3a8
     return n;
9ae3a8
 }
9ae3a8
 
9ae3a8
-static void vnc_copy(VncState *vs, int src_x, int src_y, int dst_x, int dst_y, int w, int h)
9ae3a8
-{
9ae3a8
-    /* send bitblit op to the vnc client */
9ae3a8
-    vnc_lock_output(vs);
9ae3a8
-    vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
9ae3a8
-    vnc_write_u8(vs, 0);
9ae3a8
-    vnc_write_u16(vs, 1); /* number of rects */
9ae3a8
-    vnc_framebuffer_update(vs, dst_x, dst_y, w, h, VNC_ENCODING_COPYRECT);
9ae3a8
-    vnc_write_u16(vs, src_x);
9ae3a8
-    vnc_write_u16(vs, src_y);
9ae3a8
-    vnc_unlock_output(vs);
9ae3a8
-    vnc_flush(vs);
9ae3a8
-}
9ae3a8
-
9ae3a8
-static void vnc_dpy_copy(DisplayChangeListener *dcl,
9ae3a8
-                         int src_x, int src_y,
9ae3a8
-                         int dst_x, int dst_y, int w, int h)
9ae3a8
-{
9ae3a8
-    VncDisplay *vd = container_of(dcl, VncDisplay, dcl);
9ae3a8
-    VncState *vs, *vn;
9ae3a8
-    uint8_t *src_row;
9ae3a8
-    uint8_t *dst_row;
9ae3a8
-    int i, x, y, pitch, inc, w_lim, s;
9ae3a8
-    int cmp_bytes;
9ae3a8
-
9ae3a8
-    vnc_refresh_server_surface(vd);
9ae3a8
-    QTAILQ_FOREACH_SAFE(vs, &vd->clients, next, vn) {
9ae3a8
-        if (vnc_has_feature(vs, VNC_FEATURE_COPYRECT)) {
9ae3a8
-            vs->force_update = 1;
9ae3a8
-            vnc_update_client_sync(vs, 1);
9ae3a8
-            /* vs might be free()ed here */
9ae3a8
-        }
9ae3a8
-    }
9ae3a8
-
9ae3a8
-    /* do bitblit op on the local surface too */
9ae3a8
-    pitch = vnc_server_fb_stride(vd);
9ae3a8
-    src_row = vnc_server_fb_ptr(vd, src_x, src_y);
9ae3a8
-    dst_row = vnc_server_fb_ptr(vd, dst_x, dst_y);
9ae3a8
-    y = dst_y;
9ae3a8
-    inc = 1;
9ae3a8
-    if (dst_y > src_y) {
9ae3a8
-        /* copy backwards */
9ae3a8
-        src_row += pitch * (h-1);
9ae3a8
-        dst_row += pitch * (h-1);
9ae3a8
-        pitch = -pitch;
9ae3a8
-        y = dst_y + h - 1;
9ae3a8
-        inc = -1;
9ae3a8
-    }
9ae3a8
-    w_lim = w - (VNC_DIRTY_PIXELS_PER_BIT - (dst_x % VNC_DIRTY_PIXELS_PER_BIT));
9ae3a8
-    if (w_lim < 0) {
9ae3a8
-        w_lim = w;
9ae3a8
-    } else {
9ae3a8
-        w_lim = w - (w_lim % VNC_DIRTY_PIXELS_PER_BIT);
9ae3a8
-    }
9ae3a8
-    for (i = 0; i < h; i++) {
9ae3a8
-        for (x = 0; x <= w_lim;
9ae3a8
-                x += s, src_row += cmp_bytes, dst_row += cmp_bytes) {
9ae3a8
-            if (x == w_lim) {
9ae3a8
-                if ((s = w - w_lim) == 0)
9ae3a8
-                    break;
9ae3a8
-            } else if (!x) {
9ae3a8
-                s = (VNC_DIRTY_PIXELS_PER_BIT -
9ae3a8
-                    (dst_x % VNC_DIRTY_PIXELS_PER_BIT));
9ae3a8
-                s = MIN(s, w_lim);
9ae3a8
-            } else {
9ae3a8
-                s = VNC_DIRTY_PIXELS_PER_BIT;
9ae3a8
-            }
9ae3a8
-            cmp_bytes = s * VNC_SERVER_FB_BYTES;
9ae3a8
-            if (memcmp(src_row, dst_row, cmp_bytes) == 0)
9ae3a8
-                continue;
9ae3a8
-            memmove(dst_row, src_row, cmp_bytes);
9ae3a8
-            QTAILQ_FOREACH(vs, &vd->clients, next) {
9ae3a8
-                if (!vnc_has_feature(vs, VNC_FEATURE_COPYRECT)) {
9ae3a8
-                    set_bit(((x + dst_x) / VNC_DIRTY_PIXELS_PER_BIT),
9ae3a8
-                            vs->dirty[y]);
9ae3a8
-                }
9ae3a8
-            }
9ae3a8
-        }
9ae3a8
-        src_row += pitch - w * VNC_SERVER_FB_BYTES;
9ae3a8
-        dst_row += pitch - w * VNC_SERVER_FB_BYTES;
9ae3a8
-        y += inc;
9ae3a8
-    }
9ae3a8
-
9ae3a8
-    QTAILQ_FOREACH(vs, &vd->clients, next) {
9ae3a8
-        if (vnc_has_feature(vs, VNC_FEATURE_COPYRECT)) {
9ae3a8
-            vnc_copy(vs, src_x, src_y, dst_x, dst_y, w, h);
9ae3a8
-        }
9ae3a8
-    }
9ae3a8
-}
9ae3a8
-
9ae3a8
 static void vnc_mouse_set(DisplayChangeListener *dcl,
9ae3a8
                           int x, int y, int visible)
9ae3a8
 {
9ae3a8
@@ -883,13 +792,6 @@ static int find_and_clear_dirty_height(struct VncState *vs,
9ae3a8
     return h;
9ae3a8
 }
9ae3a8
 
9ae3a8
-static int vnc_update_client_sync(VncState *vs, int has_dirty)
9ae3a8
-{
9ae3a8
-    int ret = vnc_update_client(vs, has_dirty);
9ae3a8
-    vnc_jobs_join(vs);
9ae3a8
-    return ret;
9ae3a8
-}
9ae3a8
-
9ae3a8
 static int vnc_update_client(VncState *vs, int has_dirty)
9ae3a8
 {
9ae3a8
     if (vs->need_update && vs->csock != -1) {
9ae3a8
@@ -2936,7 +2838,6 @@ static void vnc_listen_websocket_read(void *opaque)
9ae3a8
 static const DisplayChangeListenerOps dcl_ops = {
9ae3a8
     .dpy_name          = "vnc",
9ae3a8
     .dpy_refresh       = vnc_refresh,
9ae3a8
-    .dpy_gfx_copy      = vnc_dpy_copy,
9ae3a8
     .dpy_gfx_update    = vnc_dpy_update,
9ae3a8
     .dpy_gfx_switch    = vnc_dpy_switch,
9ae3a8
     .dpy_mouse_set     = vnc_mouse_set,
9ae3a8
-- 
9ae3a8
1.8.3.1
9ae3a8