188e3980df
- Fix vnc screen corruption with e.g. xterm (#503156) - Rebase vnc sasl patches on top of these two vnc fixes
233 lines
7.1 KiB
Diff
233 lines
7.1 KiB
Diff
From 977b3b69b2e06f0aab0c48ff08a236bff8763f2e Mon Sep 17 00:00:00 2001
|
|
From: Chris Webb <chris@arachsys.com>
|
|
Date: Wed, 26 Aug 2009 22:52:43 +0000
|
|
Subject: [PATCH] vnc: rework VncState release workflow
|
|
|
|
Split socket closing and releasing of VncState into two steps. First close
|
|
the socket and set the variable to -1 to indicate shutdown in progress. Do
|
|
the actual release in a few places where we can be sure it doesn't cause
|
|
trouble in form of use-after-free. Add some checks for a valid socket handle
|
|
to make sure we don't try to use the closed socket.
|
|
|
|
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
|
|
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
|
|
|
|
Backported to 0.10-stable, removing references to vs->force_update and
|
|
changing vnc_disconnect_finish() to match the code in the 0.10 version of
|
|
vnc_client_io_error() in place of the master branch version.
|
|
|
|
(cherry picked from commit c2723a9606dae5af5c614a55296ee37e2ed7801a)
|
|
|
|
Signed-off-by: Chris Webb <chris@arachsys.com>
|
|
Signed-off-by: Glauber Costa <glommer@redhat.com>
|
|
Signed-off-by: Mark McLoughlin <markmc@redhat.com>
|
|
Fedora-patch: qemu-fix-vnc-disconnect-segfault.patch
|
|
---
|
|
vnc.c | 110 ++++++++++++++++++++++++++++++++++++++++++-----------------------
|
|
1 files changed, 71 insertions(+), 39 deletions(-)
|
|
|
|
diff --git a/vnc.c b/vnc.c
|
|
index c0700c0..28e8362 100644
|
|
--- a/vnc.c
|
|
+++ b/vnc.c
|
|
@@ -200,6 +200,8 @@ static void vnc_write_u16(VncState *vs, uint16_t value);
|
|
static void vnc_write_u8(VncState *vs, uint8_t value);
|
|
static void vnc_flush(VncState *vs);
|
|
static void vnc_update_client(void *opaque);
|
|
+static void vnc_disconnect_start(VncState *vs);
|
|
+static void vnc_disconnect_finish(VncState *vs);
|
|
static void vnc_client_read(void *opaque);
|
|
|
|
static void vnc_colordepth(VncState *vs);
|
|
@@ -670,13 +672,21 @@ static void vnc_copy(VncState *vs, int src_x, int src_y, int dst_x, int dst_y, i
|
|
static void vnc_dpy_copy(DisplayState *ds, int src_x, int src_y, int dst_x, int dst_y, int w, int h)
|
|
{
|
|
VncDisplay *vd = ds->opaque;
|
|
- VncState *vs = vd->clients;
|
|
- while (vs != NULL) {
|
|
+ VncState *vs, *vn;
|
|
+
|
|
+ for (vs = vd->clients; vs != NULL; vs = vn) {
|
|
+ vn = vs->next;
|
|
+ if (vnc_has_feature(vs, VNC_FEATURE_COPYRECT)) {
|
|
+ vnc_update_client(vs);
|
|
+ /* vs might be free()ed here */
|
|
+ }
|
|
+ }
|
|
+
|
|
+ for (vs = vd->clients; vs != NULL; vs = vs->next) {
|
|
if (vnc_has_feature(vs, VNC_FEATURE_COPYRECT))
|
|
vnc_copy(vs, src_x, src_y, dst_x, dst_y, w, h);
|
|
else /* TODO */
|
|
vnc_update(vs, dst_x, dst_y, w, h);
|
|
- vs = vs->next;
|
|
}
|
|
}
|
|
|
|
@@ -786,6 +796,8 @@ static void vnc_update_client(void *opaque)
|
|
|
|
if (vs->csock != -1) {
|
|
qemu_mod_timer(vs->timer, qemu_get_clock(rt_clock) + VNC_REFRESH_INTERVAL);
|
|
+ } else {
|
|
+ vnc_disconnect_finish(vs);
|
|
}
|
|
|
|
}
|
|
@@ -855,6 +867,47 @@ static void audio_del(VncState *vs)
|
|
}
|
|
}
|
|
|
|
+static void vnc_disconnect_start(VncState *vs)
|
|
+{
|
|
+ if (vs->csock == -1)
|
|
+ return;
|
|
+ qemu_set_fd_handler2(vs->csock, NULL, NULL, NULL, NULL);
|
|
+ closesocket(vs->csock);
|
|
+ vs->csock = -1;
|
|
+}
|
|
+
|
|
+static void vnc_disconnect_finish(VncState *vs)
|
|
+{
|
|
+ qemu_del_timer(vs->timer);
|
|
+ qemu_free_timer(vs->timer);
|
|
+ if (vs->input.buffer) qemu_free(vs->input.buffer);
|
|
+ if (vs->output.buffer) qemu_free(vs->output.buffer);
|
|
+#ifdef CONFIG_VNC_TLS
|
|
+ if (vs->tls_session) {
|
|
+ gnutls_deinit(vs->tls_session);
|
|
+ vs->tls_session = NULL;
|
|
+ }
|
|
+#endif /* CONFIG_VNC_TLS */
|
|
+ audio_del(vs);
|
|
+
|
|
+ VncState *p, *parent = NULL;
|
|
+ for (p = vs->vd->clients; p != NULL; p = p->next) {
|
|
+ if (p == vs) {
|
|
+ if (parent)
|
|
+ parent->next = p->next;
|
|
+ else
|
|
+ vs->vd->clients = p->next;
|
|
+ break;
|
|
+ }
|
|
+ parent = p;
|
|
+ }
|
|
+ if (!vs->vd->clients)
|
|
+ dcl->idle = 1;
|
|
+
|
|
+ qemu_free(vs->old_data);
|
|
+ qemu_free(vs);
|
|
+}
|
|
+
|
|
static int vnc_client_io_error(VncState *vs, int ret, int last_errno)
|
|
{
|
|
if (ret == 0 || ret == -1) {
|
|
@@ -872,36 +925,7 @@ static int vnc_client_io_error(VncState *vs, int ret, int last_errno)
|
|
}
|
|
|
|
VNC_DEBUG("Closing down client sock %d %d\n", ret, ret < 0 ? last_errno : 0);
|
|
- qemu_set_fd_handler2(vs->csock, NULL, NULL, NULL, NULL);
|
|
- closesocket(vs->csock);
|
|
- qemu_del_timer(vs->timer);
|
|
- qemu_free_timer(vs->timer);
|
|
- if (vs->input.buffer) qemu_free(vs->input.buffer);
|
|
- if (vs->output.buffer) qemu_free(vs->output.buffer);
|
|
-#ifdef CONFIG_VNC_TLS
|
|
- if (vs->tls_session) {
|
|
- gnutls_deinit(vs->tls_session);
|
|
- vs->tls_session = NULL;
|
|
- }
|
|
-#endif /* CONFIG_VNC_TLS */
|
|
- audio_del(vs);
|
|
-
|
|
- VncState *p, *parent = NULL;
|
|
- for (p = vs->vd->clients; p != NULL; p = p->next) {
|
|
- if (p == vs) {
|
|
- if (parent)
|
|
- parent->next = p->next;
|
|
- else
|
|
- vs->vd->clients = p->next;
|
|
- break;
|
|
- }
|
|
- parent = p;
|
|
- }
|
|
- if (!vs->vd->clients)
|
|
- dcl->idle = 1;
|
|
-
|
|
- qemu_free(vs->old_data);
|
|
- qemu_free(vs);
|
|
+ vnc_disconnect_start(vs);
|
|
|
|
return 0;
|
|
}
|
|
@@ -910,7 +934,8 @@ static int vnc_client_io_error(VncState *vs, int ret, int last_errno)
|
|
|
|
static void vnc_client_error(VncState *vs)
|
|
{
|
|
- vnc_client_io_error(vs, -1, EINVAL);
|
|
+ VNC_DEBUG("Closing down client sock: protocol error\n");
|
|
+ vnc_disconnect_start(vs);
|
|
}
|
|
|
|
static void vnc_client_write(void *opaque)
|
|
@@ -970,8 +995,11 @@ static void vnc_client_read(void *opaque)
|
|
#endif /* CONFIG_VNC_TLS */
|
|
ret = recv(vs->csock, buffer_end(&vs->input), 4096, 0);
|
|
ret = vnc_client_io_error(vs, ret, socket_error());
|
|
- if (!ret)
|
|
+ if (!ret) {
|
|
+ if (vs->csock == -1)
|
|
+ vnc_disconnect_finish(vs);
|
|
return;
|
|
+ }
|
|
|
|
vs->input.offset += ret;
|
|
|
|
@@ -980,8 +1008,10 @@ static void vnc_client_read(void *opaque)
|
|
int ret;
|
|
|
|
ret = vs->read_handler(vs, vs->input.buffer, len);
|
|
- if (vs->csock == -1)
|
|
+ if (vs->csock == -1) {
|
|
+ vnc_disconnect_finish(vs);
|
|
return;
|
|
+ }
|
|
|
|
if (!ret) {
|
|
memmove(vs->input.buffer, vs->input.buffer + len, (vs->input.offset - len));
|
|
@@ -996,7 +1026,7 @@ static void vnc_write(VncState *vs, const void *data, size_t len)
|
|
{
|
|
buffer_reserve(&vs->output, len);
|
|
|
|
- if (buffer_empty(&vs->output)) {
|
|
+ if (vs->csock != -1 && buffer_empty(&vs->output)) {
|
|
qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, vnc_client_write, vs);
|
|
}
|
|
|
|
@@ -1037,7 +1067,7 @@ static void vnc_write_u8(VncState *vs, uint8_t value)
|
|
|
|
static void vnc_flush(VncState *vs)
|
|
{
|
|
- if (vs->output.offset)
|
|
+ if (vs->csock != -1 && vs->output.offset)
|
|
vnc_client_write(vs);
|
|
}
|
|
|
|
@@ -2305,11 +2335,13 @@ static void vnc_connect(VncDisplay *vd, int csock)
|
|
vnc_read_when(vs, protocol_version, 12);
|
|
memset(vs->old_data, 0, ds_get_linesize(vs->ds) * ds_get_height(vs->ds));
|
|
memset(vs->dirty_row, 0xFF, sizeof(vs->dirty_row));
|
|
- vnc_update_client(vs);
|
|
reset_keys(vs);
|
|
|
|
vs->next = vd->clients;
|
|
vd->clients = vs;
|
|
+
|
|
+ vnc_update_client(vs);
|
|
+ /* vs might be free()ed here */
|
|
}
|
|
|
|
static void vnc_listen_read(void *opaque)
|
|
--
|
|
1.6.2.5
|
|
|