|
|
9ae3a8 |
From 3d842d39e26560dfb7679d88746c314a3545ca18 Mon Sep 17 00:00:00 2001
|
|
|
9ae3a8 |
From: Gerd Hoffmann <kraxel@redhat.com>
|
|
|
9ae3a8 |
Date: Wed, 22 Feb 2017 12:36:24 +0100
|
|
|
9ae3a8 |
Subject: [PATCH 06/24] ui/vnc: fix potential memory corruption issues
|
|
|
9ae3a8 |
MIME-Version: 1.0
|
|
|
9ae3a8 |
Content-Type: text/plain; charset=UTF-8
|
|
|
9ae3a8 |
Content-Transfer-Encoding: 8bit
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
RH-Author: Gerd Hoffmann <kraxel@redhat.com>
|
|
|
9ae3a8 |
Message-id: <1487766986-6329-7-git-send-email-kraxel@redhat.com>
|
|
|
9ae3a8 |
Patchwork-id: 73977
|
|
|
9ae3a8 |
O-Subject: [RHEL-7.4 qemu-kvm PATCH 6/8] ui/vnc: fix potential memory corruption issues
|
|
|
9ae3a8 |
Bugzilla: 1377977
|
|
|
9ae3a8 |
RH-Acked-by: Thomas Huth <thuth@redhat.com>
|
|
|
9ae3a8 |
RH-Acked-by: Marc-André Lureau <mlureau@redhat.com>
|
|
|
9ae3a8 |
RH-Acked-by: Laurent Vivier <lvivier@redhat.com>
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
From: Peter Lieven <pl@kamp.de>
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
this patch makes the VNC server work correctly if the
|
|
|
9ae3a8 |
server surface and the guest surface have different sizes.
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
Basically the server surface is adjusted to not exceed VNC_MAX_WIDTH
|
|
|
9ae3a8 |
x VNC_MAX_HEIGHT and additionally the width is rounded up to multiple of
|
|
|
9ae3a8 |
VNC_DIRTY_PIXELS_PER_BIT.
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
If we have a resolution whose width is not dividable by VNC_DIRTY_PIXELS_PER_BIT
|
|
|
9ae3a8 |
we now get a small black bar on the right of the screen.
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
If the surface is too big to fit the limits only the upper left area is shown.
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
On top of that this fixes 2 memory corruption issues:
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
The first was actually discovered during playing
|
|
|
9ae3a8 |
around with a Windows 7 vServer. During resolution
|
|
|
9ae3a8 |
change in Windows 7 it happens sometimes that Windows
|
|
|
9ae3a8 |
changes to an intermediate resolution where
|
|
|
9ae3a8 |
server_stride % cmp_bytes != 0 (in vnc_refresh_server_surface).
|
|
|
9ae3a8 |
This happens only if width % VNC_DIRTY_PIXELS_PER_BIT != 0.
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
The second is a theoretical issue, but is maybe exploitable
|
|
|
9ae3a8 |
by the guest. If for some reason the guest surface size is bigger
|
|
|
9ae3a8 |
than VNC_MAX_WIDTH x VNC_MAX_HEIGHT we end up in severe corruption since
|
|
|
9ae3a8 |
this limit is nowhere enforced.
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
Signed-off-by: Peter Lieven <pl@kamp.de>
|
|
|
9ae3a8 |
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
|
|
|
9ae3a8 |
(cherry picked from commit bea60dd7679364493a0d7f5b54316c767cf894ef)
|
|
|
9ae3a8 |
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
Conflicts:
|
|
|
9ae3a8 |
ui/vnc.c [ in pointer_event, input subsystem differences ]
|
|
|
9ae3a8 |
---
|
|
|
9ae3a8 |
ui/vnc.c | 149 +++++++++++++++++++++++++++++----------------------------------
|
|
|
9ae3a8 |
ui/vnc.h | 14 +++---
|
|
|
9ae3a8 |
2 files changed, 77 insertions(+), 86 deletions(-)
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
diff --git a/ui/vnc.c b/ui/vnc.c
|
|
|
9ae3a8 |
index 51f95be..80b7792 100644
|
|
|
9ae3a8 |
--- a/ui/vnc.c
|
|
|
9ae3a8 |
+++ b/ui/vnc.c
|
|
|
9ae3a8 |
@@ -427,14 +427,10 @@ static void framebuffer_update_request(VncState *vs, int incremental,
|
|
|
9ae3a8 |
static void vnc_refresh(DisplayChangeListener *dcl);
|
|
|
9ae3a8 |
static int vnc_refresh_server_surface(VncDisplay *vd);
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
-static void vnc_dpy_update(DisplayChangeListener *dcl,
|
|
|
9ae3a8 |
- int x, int y, int w, int h)
|
|
|
9ae3a8 |
-{
|
|
|
9ae3a8 |
- VncDisplay *vd = container_of(dcl, VncDisplay, dcl);
|
|
|
9ae3a8 |
- struct VncSurface *s = &vd->guest;
|
|
|
9ae3a8 |
- int width = surface_width(vd->ds);
|
|
|
9ae3a8 |
- int height = surface_height(vd->ds);
|
|
|
9ae3a8 |
-
|
|
|
9ae3a8 |
+static void vnc_set_area_dirty(DECLARE_BITMAP(dirty[VNC_MAX_HEIGHT],
|
|
|
9ae3a8 |
+ VNC_MAX_WIDTH / VNC_DIRTY_PIXELS_PER_BIT),
|
|
|
9ae3a8 |
+ int width, int height,
|
|
|
9ae3a8 |
+ int x, int y, int w, int h) {
|
|
|
9ae3a8 |
/* this is needed this to ensure we updated all affected
|
|
|
9ae3a8 |
* blocks if x % VNC_DIRTY_PIXELS_PER_BIT != 0 */
|
|
|
9ae3a8 |
w += (x % VNC_DIRTY_PIXELS_PER_BIT);
|
|
|
9ae3a8 |
@@ -446,11 +442,22 @@ static void vnc_dpy_update(DisplayChangeListener *dcl,
|
|
|
9ae3a8 |
h = MIN(y + h, height);
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
for (; y < h; y++) {
|
|
|
9ae3a8 |
- bitmap_set(s->dirty[y], x / VNC_DIRTY_PIXELS_PER_BIT,
|
|
|
9ae3a8 |
+ bitmap_set(dirty[y], x / VNC_DIRTY_PIXELS_PER_BIT,
|
|
|
9ae3a8 |
DIV_ROUND_UP(w, VNC_DIRTY_PIXELS_PER_BIT));
|
|
|
9ae3a8 |
}
|
|
|
9ae3a8 |
}
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
+static void vnc_dpy_update(DisplayChangeListener *dcl,
|
|
|
9ae3a8 |
+ int x, int y, int w, int h)
|
|
|
9ae3a8 |
+{
|
|
|
9ae3a8 |
+ VncDisplay *vd = container_of(dcl, VncDisplay, dcl);
|
|
|
9ae3a8 |
+ struct VncSurface *s = &vd->guest;
|
|
|
9ae3a8 |
+ int width = pixman_image_get_width(vd->server);
|
|
|
9ae3a8 |
+ int height = pixman_image_get_height(vd->server);
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ vnc_set_area_dirty(s->dirty, width, height, x, y, w, h);
|
|
|
9ae3a8 |
+}
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h,
|
|
|
9ae3a8 |
int32_t encoding)
|
|
|
9ae3a8 |
{
|
|
|
9ae3a8 |
@@ -512,17 +519,15 @@ void buffer_advance(Buffer *buf, size_t len)
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
static void vnc_desktop_resize(VncState *vs)
|
|
|
9ae3a8 |
{
|
|
|
9ae3a8 |
- DisplaySurface *ds = vs->vd->ds;
|
|
|
9ae3a8 |
-
|
|
|
9ae3a8 |
if (vs->csock == -1 || !vnc_has_feature(vs, VNC_FEATURE_RESIZE)) {
|
|
|
9ae3a8 |
return;
|
|
|
9ae3a8 |
}
|
|
|
9ae3a8 |
- if (vs->client_width == surface_width(ds) &&
|
|
|
9ae3a8 |
- vs->client_height == surface_height(ds)) {
|
|
|
9ae3a8 |
+ if (vs->client_width == pixman_image_get_width(vs->vd->server) &&
|
|
|
9ae3a8 |
+ vs->client_height == pixman_image_get_height(vs->vd->server)) {
|
|
|
9ae3a8 |
return;
|
|
|
9ae3a8 |
}
|
|
|
9ae3a8 |
- vs->client_width = surface_width(ds);
|
|
|
9ae3a8 |
- vs->client_height = surface_height(ds);
|
|
|
9ae3a8 |
+ vs->client_width = pixman_image_get_width(vs->vd->server);
|
|
|
9ae3a8 |
+ vs->client_height = pixman_image_get_height(vs->vd->server);
|
|
|
9ae3a8 |
vnc_lock_output(vs);
|
|
|
9ae3a8 |
vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
|
|
|
9ae3a8 |
vnc_write_u8(vs, 0);
|
|
|
9ae3a8 |
@@ -566,31 +571,24 @@ void *vnc_server_fb_ptr(VncDisplay *vd, int x, int y)
|
|
|
9ae3a8 |
ptr += x * VNC_SERVER_FB_BYTES;
|
|
|
9ae3a8 |
return ptr;
|
|
|
9ae3a8 |
}
|
|
|
9ae3a8 |
-/* this sets only the visible pixels of a dirty bitmap */
|
|
|
9ae3a8 |
-#define VNC_SET_VISIBLE_PIXELS_DIRTY(bitmap, w, h) {\
|
|
|
9ae3a8 |
- int y;\
|
|
|
9ae3a8 |
- memset(bitmap, 0x00, sizeof(bitmap));\
|
|
|
9ae3a8 |
- for (y = 0; y < h; y++) {\
|
|
|
9ae3a8 |
- bitmap_set(bitmap[y], 0,\
|
|
|
9ae3a8 |
- DIV_ROUND_UP(w, VNC_DIRTY_PIXELS_PER_BIT));\
|
|
|
9ae3a8 |
- } \
|
|
|
9ae3a8 |
- }
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
static void vnc_dpy_switch(DisplayChangeListener *dcl,
|
|
|
9ae3a8 |
DisplaySurface *surface)
|
|
|
9ae3a8 |
{
|
|
|
9ae3a8 |
VncDisplay *vd = container_of(dcl, VncDisplay, dcl);
|
|
|
9ae3a8 |
VncState *vs;
|
|
|
9ae3a8 |
+ int width, height;
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
vnc_abort_display_jobs(vd);
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
/* server surface */
|
|
|
9ae3a8 |
qemu_pixman_image_unref(vd->server);
|
|
|
9ae3a8 |
vd->ds = surface;
|
|
|
9ae3a8 |
+ width = MIN(VNC_MAX_WIDTH, ROUND_UP(surface_width(vd->ds),
|
|
|
9ae3a8 |
+ VNC_DIRTY_PIXELS_PER_BIT));
|
|
|
9ae3a8 |
+ height = MIN(VNC_MAX_HEIGHT, surface_height(vd->ds));
|
|
|
9ae3a8 |
vd->server = pixman_image_create_bits(VNC_SERVER_FB_FORMAT,
|
|
|
9ae3a8 |
- surface_width(vd->ds),
|
|
|
9ae3a8 |
- surface_height(vd->ds),
|
|
|
9ae3a8 |
- NULL, 0);
|
|
|
9ae3a8 |
+ width, height, NULL, 0);
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
/* guest surface */
|
|
|
9ae3a8 |
#if 0 /* FIXME */
|
|
|
9ae3a8 |
@@ -600,9 +598,9 @@ static void vnc_dpy_switch(DisplayChangeListener *dcl,
|
|
|
9ae3a8 |
qemu_pixman_image_unref(vd->guest.fb);
|
|
|
9ae3a8 |
vd->guest.fb = pixman_image_ref(surface->image);
|
|
|
9ae3a8 |
vd->guest.format = surface->format;
|
|
|
9ae3a8 |
- VNC_SET_VISIBLE_PIXELS_DIRTY(vd->guest.dirty,
|
|
|
9ae3a8 |
- surface_width(vd->ds),
|
|
|
9ae3a8 |
- surface_height(vd->ds));
|
|
|
9ae3a8 |
+ memset(vd->guest.dirty, 0x00, sizeof(vd->guest.dirty));
|
|
|
9ae3a8 |
+ vnc_set_area_dirty(vd->guest.dirty, width, height, 0, 0,
|
|
|
9ae3a8 |
+ width, height);
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
QTAILQ_FOREACH(vs, &vd->clients, next) {
|
|
|
9ae3a8 |
vnc_colordepth(vs);
|
|
|
9ae3a8 |
@@ -610,9 +608,9 @@ static void vnc_dpy_switch(DisplayChangeListener *dcl,
|
|
|
9ae3a8 |
if (vs->vd->cursor) {
|
|
|
9ae3a8 |
vnc_cursor_define(vs);
|
|
|
9ae3a8 |
}
|
|
|
9ae3a8 |
- VNC_SET_VISIBLE_PIXELS_DIRTY(vs->dirty,
|
|
|
9ae3a8 |
- surface_width(vd->ds),
|
|
|
9ae3a8 |
- surface_height(vd->ds));
|
|
|
9ae3a8 |
+ memset(vs->dirty, 0x00, sizeof(vs->dirty));
|
|
|
9ae3a8 |
+ vnc_set_area_dirty(vs->dirty, width, height, 0, 0,
|
|
|
9ae3a8 |
+ width, height);
|
|
|
9ae3a8 |
}
|
|
|
9ae3a8 |
}
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
@@ -916,8 +914,8 @@ static int vnc_update_client(VncState *vs, int has_dirty)
|
|
|
9ae3a8 |
*/
|
|
|
9ae3a8 |
job = vnc_job_new(vs);
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
- height = MIN(pixman_image_get_height(vd->server), vs->client_height);
|
|
|
9ae3a8 |
- width = MIN(pixman_image_get_width(vd->server), vs->client_width);
|
|
|
9ae3a8 |
+ height = pixman_image_get_height(vd->server);
|
|
|
9ae3a8 |
+ width = pixman_image_get_width(vd->server);
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
y = 0;
|
|
|
9ae3a8 |
for (;;) {
|
|
|
9ae3a8 |
@@ -1500,8 +1498,8 @@ static void check_pointer_type_change(Notifier *notifier, void *data)
|
|
|
9ae3a8 |
vnc_write_u8(vs, 0);
|
|
|
9ae3a8 |
vnc_write_u16(vs, 1);
|
|
|
9ae3a8 |
vnc_framebuffer_update(vs, absolute, 0,
|
|
|
9ae3a8 |
- surface_width(vs->vd->ds),
|
|
|
9ae3a8 |
- surface_height(vs->vd->ds),
|
|
|
9ae3a8 |
+ pixman_image_get_width(vs->vd->server),
|
|
|
9ae3a8 |
+ pixman_image_get_height(vs->vd->server),
|
|
|
9ae3a8 |
VNC_ENCODING_POINTER_TYPE_CHANGE);
|
|
|
9ae3a8 |
vnc_unlock_output(vs);
|
|
|
9ae3a8 |
vnc_flush(vs);
|
|
|
9ae3a8 |
@@ -1513,8 +1511,8 @@ static void pointer_event(VncState *vs, int button_mask, int x, int y)
|
|
|
9ae3a8 |
{
|
|
|
9ae3a8 |
int buttons = 0;
|
|
|
9ae3a8 |
int dz = 0;
|
|
|
9ae3a8 |
- int width = surface_width(vs->vd->ds);
|
|
|
9ae3a8 |
- int height = surface_height(vs->vd->ds);
|
|
|
9ae3a8 |
+ int width = pixman_image_get_width(vs->vd->server);
|
|
|
9ae3a8 |
+ int height = pixman_image_get_height(vs->vd->server);
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
if (button_mask & 0x01)
|
|
|
9ae3a8 |
buttons |= MOUSE_EVENT_LBUTTON;
|
|
|
9ae3a8 |
@@ -1866,29 +1864,18 @@ static void ext_key_event(VncState *vs, int down,
|
|
|
9ae3a8 |
}
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
static void framebuffer_update_request(VncState *vs, int incremental,
|
|
|
9ae3a8 |
- int x_position, int y_position,
|
|
|
9ae3a8 |
- int w, int h)
|
|
|
9ae3a8 |
+ int x, int y, int w, int h)
|
|
|
9ae3a8 |
{
|
|
|
9ae3a8 |
- int i;
|
|
|
9ae3a8 |
- const size_t width = surface_width(vs->vd->ds) / VNC_DIRTY_PIXELS_PER_BIT;
|
|
|
9ae3a8 |
- const size_t height = surface_height(vs->vd->ds);
|
|
|
9ae3a8 |
-
|
|
|
9ae3a8 |
- if (y_position > height) {
|
|
|
9ae3a8 |
- y_position = height;
|
|
|
9ae3a8 |
- }
|
|
|
9ae3a8 |
- if (y_position + h >= height) {
|
|
|
9ae3a8 |
- h = height - y_position;
|
|
|
9ae3a8 |
- }
|
|
|
9ae3a8 |
+ int width = pixman_image_get_width(vs->vd->server);
|
|
|
9ae3a8 |
+ int height = pixman_image_get_height(vs->vd->server);
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
vs->need_update = 1;
|
|
|
9ae3a8 |
- if (!incremental) {
|
|
|
9ae3a8 |
- vs->force_update = 1;
|
|
|
9ae3a8 |
- for (i = 0; i < h; i++) {
|
|
|
9ae3a8 |
- bitmap_set(vs->dirty[y_position + i], 0, width);
|
|
|
9ae3a8 |
- bitmap_clear(vs->dirty[y_position + i], width,
|
|
|
9ae3a8 |
- VNC_DIRTY_BITS - width);
|
|
|
9ae3a8 |
- }
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ if (incremental) {
|
|
|
9ae3a8 |
+ return;
|
|
|
9ae3a8 |
}
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ vnc_set_area_dirty(vs->dirty, width, height, x, y, w, h);
|
|
|
9ae3a8 |
}
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
static void send_ext_key_event_ack(VncState *vs)
|
|
|
9ae3a8 |
@@ -1898,8 +1885,8 @@ static void send_ext_key_event_ack(VncState *vs)
|
|
|
9ae3a8 |
vnc_write_u8(vs, 0);
|
|
|
9ae3a8 |
vnc_write_u16(vs, 1);
|
|
|
9ae3a8 |
vnc_framebuffer_update(vs, 0, 0,
|
|
|
9ae3a8 |
- surface_width(vs->vd->ds),
|
|
|
9ae3a8 |
- surface_height(vs->vd->ds),
|
|
|
9ae3a8 |
+ pixman_image_get_width(vs->vd->server),
|
|
|
9ae3a8 |
+ pixman_image_get_height(vs->vd->server),
|
|
|
9ae3a8 |
VNC_ENCODING_EXT_KEY_EVENT);
|
|
|
9ae3a8 |
vnc_unlock_output(vs);
|
|
|
9ae3a8 |
vnc_flush(vs);
|
|
|
9ae3a8 |
@@ -1912,8 +1899,8 @@ static void send_ext_audio_ack(VncState *vs)
|
|
|
9ae3a8 |
vnc_write_u8(vs, 0);
|
|
|
9ae3a8 |
vnc_write_u16(vs, 1);
|
|
|
9ae3a8 |
vnc_framebuffer_update(vs, 0, 0,
|
|
|
9ae3a8 |
- surface_width(vs->vd->ds),
|
|
|
9ae3a8 |
- surface_height(vs->vd->ds),
|
|
|
9ae3a8 |
+ pixman_image_get_width(vs->vd->server),
|
|
|
9ae3a8 |
+ pixman_image_get_height(vs->vd->server),
|
|
|
9ae3a8 |
VNC_ENCODING_AUDIO);
|
|
|
9ae3a8 |
vnc_unlock_output(vs);
|
|
|
9ae3a8 |
vnc_flush(vs);
|
|
|
9ae3a8 |
@@ -2101,8 +2088,8 @@ static void vnc_colordepth(VncState *vs)
|
|
|
9ae3a8 |
vnc_write_u8(vs, 0);
|
|
|
9ae3a8 |
vnc_write_u16(vs, 1); /* number of rects */
|
|
|
9ae3a8 |
vnc_framebuffer_update(vs, 0, 0,
|
|
|
9ae3a8 |
- surface_width(vs->vd->ds),
|
|
|
9ae3a8 |
- surface_height(vs->vd->ds),
|
|
|
9ae3a8 |
+ pixman_image_get_width(vs->vd->server),
|
|
|
9ae3a8 |
+ pixman_image_get_height(vs->vd->server),
|
|
|
9ae3a8 |
VNC_ENCODING_WMVi);
|
|
|
9ae3a8 |
pixel_format_message(vs);
|
|
|
9ae3a8 |
vnc_unlock_output(vs);
|
|
|
9ae3a8 |
@@ -2317,8 +2304,8 @@ static int protocol_client_init(VncState *vs, uint8_t *data, size_t len)
|
|
|
9ae3a8 |
}
|
|
|
9ae3a8 |
vnc_set_share_mode(vs, mode);
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
- vs->client_width = surface_width(vs->vd->ds);
|
|
|
9ae3a8 |
- vs->client_height = surface_height(vs->vd->ds);
|
|
|
9ae3a8 |
+ vs->client_width = pixman_image_get_width(vs->vd->server);
|
|
|
9ae3a8 |
+ vs->client_height = pixman_image_get_height(vs->vd->server);
|
|
|
9ae3a8 |
vnc_write_u16(vs, vs->client_width);
|
|
|
9ae3a8 |
vnc_write_u16(vs, vs->client_height);
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
@@ -2685,12 +2672,12 @@ static void vnc_rect_updated(VncDisplay *vd, int x, int y, struct timeval * tv)
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
static int vnc_refresh_server_surface(VncDisplay *vd)
|
|
|
9ae3a8 |
{
|
|
|
9ae3a8 |
- int width = pixman_image_get_width(vd->guest.fb);
|
|
|
9ae3a8 |
- int height = pixman_image_get_height(vd->guest.fb);
|
|
|
9ae3a8 |
- int y;
|
|
|
9ae3a8 |
+ int width = MIN(pixman_image_get_width(vd->guest.fb),
|
|
|
9ae3a8 |
+ pixman_image_get_width(vd->server));
|
|
|
9ae3a8 |
+ int height = MIN(pixman_image_get_height(vd->guest.fb),
|
|
|
9ae3a8 |
+ pixman_image_get_height(vd->server));
|
|
|
9ae3a8 |
+ int cmp_bytes, server_stride, min_stride, guest_stride, y = 0;
|
|
|
9ae3a8 |
uint8_t *guest_row0 = NULL, *server_row0;
|
|
|
9ae3a8 |
- int guest_stride = 0, server_stride;
|
|
|
9ae3a8 |
- int cmp_bytes;
|
|
|
9ae3a8 |
VncState *vs;
|
|
|
9ae3a8 |
int has_dirty = 0;
|
|
|
9ae3a8 |
pixman_image_t *tmpbuf = NULL;
|
|
|
9ae3a8 |
@@ -2707,10 +2694,10 @@ static int vnc_refresh_server_surface(VncDisplay *vd)
|
|
|
9ae3a8 |
* Check and copy modified bits from guest to server surface.
|
|
|
9ae3a8 |
* Update server dirty map.
|
|
|
9ae3a8 |
*/
|
|
|
9ae3a8 |
- cmp_bytes = VNC_DIRTY_PIXELS_PER_BIT * VNC_SERVER_FB_BYTES;
|
|
|
9ae3a8 |
- if (cmp_bytes > vnc_server_fb_stride(vd)) {
|
|
|
9ae3a8 |
- cmp_bytes = vnc_server_fb_stride(vd);
|
|
|
9ae3a8 |
- }
|
|
|
9ae3a8 |
+ server_row0 = (uint8_t *)pixman_image_get_data(vd->server);
|
|
|
9ae3a8 |
+ server_stride = guest_stride = pixman_image_get_stride(vd->server);
|
|
|
9ae3a8 |
+ cmp_bytes = MIN(VNC_DIRTY_PIXELS_PER_BIT * VNC_SERVER_FB_BYTES,
|
|
|
9ae3a8 |
+ server_stride);
|
|
|
9ae3a8 |
if (vd->guest.format != VNC_SERVER_FB_FORMAT) {
|
|
|
9ae3a8 |
int width = pixman_image_get_width(vd->server);
|
|
|
9ae3a8 |
tmpbuf = qemu_pixman_linebuf_create(VNC_SERVER_FB_FORMAT, width);
|
|
|
9ae3a8 |
@@ -2718,10 +2705,8 @@ static int vnc_refresh_server_surface(VncDisplay *vd)
|
|
|
9ae3a8 |
guest_row0 = (uint8_t *)pixman_image_get_data(vd->guest.fb);
|
|
|
9ae3a8 |
guest_stride = pixman_image_get_stride(vd->guest.fb);
|
|
|
9ae3a8 |
}
|
|
|
9ae3a8 |
- server_row0 = (uint8_t *)pixman_image_get_data(vd->server);
|
|
|
9ae3a8 |
- server_stride = pixman_image_get_stride(vd->server);
|
|
|
9ae3a8 |
+ min_stride = MIN(server_stride, guest_stride);
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
- y = 0;
|
|
|
9ae3a8 |
for (;;) {
|
|
|
9ae3a8 |
int x;
|
|
|
9ae3a8 |
uint8_t *guest_ptr, *server_ptr;
|
|
|
9ae3a8 |
@@ -2747,13 +2732,17 @@ static int vnc_refresh_server_surface(VncDisplay *vd)
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
for (; x < DIV_ROUND_UP(width, VNC_DIRTY_PIXELS_PER_BIT);
|
|
|
9ae3a8 |
x++, guest_ptr += cmp_bytes, server_ptr += cmp_bytes) {
|
|
|
9ae3a8 |
+ int _cmp_bytes = cmp_bytes;
|
|
|
9ae3a8 |
if (!test_and_clear_bit(x, vd->guest.dirty[y])) {
|
|
|
9ae3a8 |
continue;
|
|
|
9ae3a8 |
}
|
|
|
9ae3a8 |
- if (memcmp(server_ptr, guest_ptr, cmp_bytes) == 0) {
|
|
|
9ae3a8 |
+ if ((x + 1) * cmp_bytes > min_stride) {
|
|
|
9ae3a8 |
+ _cmp_bytes = min_stride - x * cmp_bytes;
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
+ if (memcmp(server_ptr, guest_ptr, _cmp_bytes) == 0) {
|
|
|
9ae3a8 |
continue;
|
|
|
9ae3a8 |
}
|
|
|
9ae3a8 |
- memcpy(server_ptr, guest_ptr, cmp_bytes);
|
|
|
9ae3a8 |
+ memcpy(server_ptr, guest_ptr, _cmp_bytes);
|
|
|
9ae3a8 |
if (!vd->non_adaptive) {
|
|
|
9ae3a8 |
vnc_rect_updated(vd, x * VNC_DIRTY_PIXELS_PER_BIT,
|
|
|
9ae3a8 |
y, &tv;;
|
|
|
9ae3a8 |
diff --git a/ui/vnc.h b/ui/vnc.h
|
|
|
9ae3a8 |
index ebf4bdd..8d534b6 100644
|
|
|
9ae3a8 |
--- a/ui/vnc.h
|
|
|
9ae3a8 |
+++ b/ui/vnc.h
|
|
|
9ae3a8 |
@@ -77,14 +77,15 @@ typedef void VncSendHextileTile(VncState *vs,
|
|
|
9ae3a8 |
void *last_fg,
|
|
|
9ae3a8 |
int *has_bg, int *has_fg);
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
-/* VNC_MAX_WIDTH must be a multiple of 16. */
|
|
|
9ae3a8 |
-#define VNC_MAX_WIDTH 2560
|
|
|
9ae3a8 |
-#define VNC_MAX_HEIGHT 2048
|
|
|
9ae3a8 |
-
|
|
|
9ae3a8 |
/* VNC_DIRTY_PIXELS_PER_BIT is the number of dirty pixels represented
|
|
|
9ae3a8 |
- * by one bit in the dirty bitmap */
|
|
|
9ae3a8 |
+ * by one bit in the dirty bitmap, should be a power of 2 */
|
|
|
9ae3a8 |
#define VNC_DIRTY_PIXELS_PER_BIT 16
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
+/* VNC_MAX_WIDTH must be a multiple of VNC_DIRTY_PIXELS_PER_BIT. */
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+#define VNC_MAX_WIDTH ROUND_UP(2560, VNC_DIRTY_PIXELS_PER_BIT)
|
|
|
9ae3a8 |
+#define VNC_MAX_HEIGHT 2048
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
/* VNC_DIRTY_BITS is the number of bits in the dirty bitmap. */
|
|
|
9ae3a8 |
#define VNC_DIRTY_BITS (VNC_MAX_WIDTH / VNC_DIRTY_PIXELS_PER_BIT)
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
@@ -126,7 +127,8 @@ typedef struct VncRectStat VncRectStat;
|
|
|
9ae3a8 |
struct VncSurface
|
|
|
9ae3a8 |
{
|
|
|
9ae3a8 |
struct timeval last_freq_check;
|
|
|
9ae3a8 |
- DECLARE_BITMAP(dirty[VNC_MAX_HEIGHT], VNC_MAX_WIDTH / 16);
|
|
|
9ae3a8 |
+ DECLARE_BITMAP(dirty[VNC_MAX_HEIGHT],
|
|
|
9ae3a8 |
+ VNC_MAX_WIDTH / VNC_DIRTY_PIXELS_PER_BIT);
|
|
|
9ae3a8 |
VncRectStat stats[VNC_STAT_ROWS][VNC_STAT_COLS];
|
|
|
9ae3a8 |
pixman_image_t *fb;
|
|
|
9ae3a8 |
pixman_format_code_t format;
|
|
|
9ae3a8 |
--
|
|
|
9ae3a8 |
1.8.3.1
|
|
|
9ae3a8 |
|