|
|
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 |
|