summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRoss Burton <ross.burton@intel.com>2018-02-08 22:59:01 +0000
committerRichard Purdie <richard.purdie@linuxfoundation.org>2018-02-16 17:56:34 +0000
commita93d8ed1bc97595492abfca92d606e20dbdfa617 (patch)
tree77a83c1410b7f32f2f88d8f2f62ef2fbff9e326a
parent4b0a6ac87a9d1ef0ce8e84b56208d847718f12fd (diff)
downloadopenembedded-core-a93d8ed1bc97595492abfca92d606e20dbdfa617.tar.gz
openembedded-core-a93d8ed1bc97595492abfca92d606e20dbdfa617.tar.bz2
openembedded-core-a93d8ed1bc97595492abfca92d606e20dbdfa617.zip
qemu: fix CVE-2017-15124
VNC server implementation in Quick Emulator (QEMU) 2.11.0 and older was found to be vulnerable to an unbounded memory allocation issue, as it did not throttle the framebuffer updates sent to its client. If the client did not consume these updates, VNC server allocates growing memory to hold onto this data. A malicious remote VNC client could use this flaw to cause DoS to the server host. Backport a series of patches from upstream to resolve this. Signed-off-by: Ross Burton <ross.burton@intel.com>
-rw-r--r--meta/recipes-devtools/qemu/qemu/CVE-2017-15124.patch1476
-rw-r--r--meta/recipes-devtools/qemu/qemu_2.11.0.bb1
2 files changed, 1477 insertions, 0 deletions
diff --git a/meta/recipes-devtools/qemu/qemu/CVE-2017-15124.patch b/meta/recipes-devtools/qemu/qemu/CVE-2017-15124.patch
new file mode 100644
index 0000000000..a47b6d0510
--- /dev/null
+++ b/meta/recipes-devtools/qemu/qemu/CVE-2017-15124.patch
@@ -0,0 +1,1476 @@
+VNC server implementation in Quick Emulator (QEMU) 2.11.0 and older was found to
+be vulnerable to an unbounded memory allocation issue, as it did not throttle
+the framebuffer updates sent to its client. If the client did not consume these
+updates, VNC server allocates growing memory to hold onto this data. A malicious
+remote VNC client could use this flaw to cause DoS to the server host.
+
+CVE: CVE-2017-15124
+Upstream-Status: Backport
+Signed-off-by: Ross Burton <ross.burton@intel.com>
+
+From 090fdc83b0960f68d204624a73c6814780da52d9 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= <marcandre.lureau@redhat.com>
+Date: Wed, 20 Dec 2017 15:06:18 +0100
+Subject: [PATCH 01/14] vnc: fix debug spelling
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
+Message-id: 20171220140618.12701-1-marcandre.lureau@redhat.com
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ ui/vnc.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/ui/vnc.c b/ui/vnc.c
+index 9f8d5a1b1f..7d537b5c6b 100644
+--- a/ui/vnc.c
++++ b/ui/vnc.c
+@@ -2255,7 +2255,7 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len)
+ }
+ vs->as.nchannels = read_u8(data, 5);
+ if (vs->as.nchannels != 1 && vs->as.nchannels != 2) {
+- VNC_DEBUG("Invalid audio channel coount %d\n",
++ VNC_DEBUG("Invalid audio channel count %d\n",
+ read_u8(data, 5));
+ vnc_client_error(vs);
+ break;
+--
+2.11.0
+
+
+From 6af998db05aec9af95a06f84ad94f1b96785e667 Mon Sep 17 00:00:00 2001
+From: "Daniel P. Berrange" <berrange@redhat.com>
+Date: Mon, 18 Dec 2017 19:12:16 +0000
+Subject: [PATCH 02/14] ui: remove 'sync' parameter from vnc_update_client
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+There is only one caller of vnc_update_client and that always passes false
+for the 'sync' parameter.
+
+Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
+Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
+Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
+Message-id: 20171218191228.31018-2-berrange@redhat.com
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ ui/vnc.c | 11 +++--------
+ 1 file changed, 3 insertions(+), 8 deletions(-)
+
+diff --git a/ui/vnc.c b/ui/vnc.c
+index 7d537b5c6b..d72a61bde3 100644
+--- a/ui/vnc.c
++++ b/ui/vnc.c
+@@ -596,7 +596,7 @@ VncInfo2List *qmp_query_vnc_servers(Error **errp)
+ 3) resolutions > 1024
+ */
+
+-static int vnc_update_client(VncState *vs, int has_dirty, bool sync);
++static int vnc_update_client(VncState *vs, int has_dirty);
+ static void vnc_disconnect_start(VncState *vs);
+
+ static void vnc_colordepth(VncState *vs);
+@@ -961,7 +961,7 @@ static int find_and_clear_dirty_height(VncState *vs,
+ return h;
+ }
+
+-static int vnc_update_client(VncState *vs, int has_dirty, bool sync)
++static int vnc_update_client(VncState *vs, int has_dirty)
+ {
+ if (vs->disconnecting) {
+ vnc_disconnect_finish(vs);
+@@ -1025,9 +1025,6 @@ static int vnc_update_client(VncState *vs, int has_dirty, bool sync)
+ }
+
+ vnc_job_push(job);
+- if (sync) {
+- vnc_jobs_join(vs);
+- }
+ vs->force_update = 0;
+ vs->has_dirty = 0;
+ return n;
+@@ -1035,8 +1032,6 @@ static int vnc_update_client(VncState *vs, int has_dirty, bool sync)
+
+ if (vs->disconnecting) {
+ vnc_disconnect_finish(vs);
+- } else if (sync) {
+- vnc_jobs_join(vs);
+ }
+
+ return 0;
+@@ -2863,7 +2858,7 @@ static void vnc_refresh(DisplayChangeListener *dcl)
+ vnc_unlock_display(vd);
+
+ QTAILQ_FOREACH_SAFE(vs, &vd->clients, next, vn) {
+- rects += vnc_update_client(vs, has_dirty, false);
++ rects += vnc_update_client(vs, has_dirty);
+ /* vs might be free()ed here */
+ }
+
+--
+2.11.0
+
+
+From c53df961617736f94731d94b62c2954c261d2bae Mon Sep 17 00:00:00 2001
+From: "Daniel P. Berrange" <berrange@redhat.com>
+Date: Mon, 18 Dec 2017 19:12:17 +0000
+Subject: [PATCH 03/14] ui: remove unreachable code in vnc_update_client
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+A previous commit:
+
+ commit 5a8be0f73d6f60ff08746377eb09ca459f39deab
+ Author: Gerd Hoffmann <kraxel@redhat.com>
+ Date: Wed Jul 13 12:21:20 2016 +0200
+
+ vnc: make sure we finish disconnect
+
+Added a check for vs->disconnecting at the very start of the
+vnc_update_client method. This means that the very next "if"
+statement check for !vs->disconnecting always evaluates true,
+and is thus redundant. This in turn means the vs->disconnecting
+check at the very end of the method never evaluates true, and
+is thus unreachable code.
+
+Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
+Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
+Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
+Message-id: 20171218191228.31018-3-berrange@redhat.com
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ ui/vnc.c | 6 +-----
+ 1 file changed, 1 insertion(+), 5 deletions(-)
+
+diff --git a/ui/vnc.c b/ui/vnc.c
+index d72a61bde3..29a7208475 100644
+--- a/ui/vnc.c
++++ b/ui/vnc.c
+@@ -969,7 +969,7 @@ static int vnc_update_client(VncState *vs, int has_dirty)
+ }
+
+ vs->has_dirty += has_dirty;
+- if (vs->need_update && !vs->disconnecting) {
++ if (vs->need_update) {
+ VncDisplay *vd = vs->vd;
+ VncJob *job;
+ int y;
+@@ -1030,10 +1030,6 @@ static int vnc_update_client(VncState *vs, int has_dirty)
+ return n;
+ }
+
+- if (vs->disconnecting) {
+- vnc_disconnect_finish(vs);
+- }
+-
+ return 0;
+ }
+
+--
+2.11.0
+
+
+From b939eb89b6f320544a9328fa908d881d0024c1ee Mon Sep 17 00:00:00 2001
+From: "Daniel P. Berrange" <berrange@redhat.com>
+Date: Mon, 18 Dec 2017 19:12:18 +0000
+Subject: [PATCH 04/14] ui: remove redundant indentation in vnc_client_update
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Now that previous dead / unreachable code has been removed, we can simplify
+the indentation in the vnc_client_update method.
+
+Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
+Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
+Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
+Message-id: 20171218191228.31018-4-berrange@redhat.com
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ ui/vnc.c | 112 ++++++++++++++++++++++++++++++++-------------------------------
+ 1 file changed, 57 insertions(+), 55 deletions(-)
+
+diff --git a/ui/vnc.c b/ui/vnc.c
+index 29a7208475..7582111ca6 100644
+--- a/ui/vnc.c
++++ b/ui/vnc.c
+@@ -963,74 +963,76 @@ static int find_and_clear_dirty_height(VncState *vs,
+
+ static int vnc_update_client(VncState *vs, int has_dirty)
+ {
++ VncDisplay *vd = vs->vd;
++ VncJob *job;
++ int y;
++ int height, width;
++ int n = 0;
++
+ if (vs->disconnecting) {
+ vnc_disconnect_finish(vs);
+ return 0;
+ }
+
+ vs->has_dirty += has_dirty;
+- if (vs->need_update) {
+- VncDisplay *vd = vs->vd;
+- VncJob *job;
+- int y;
+- int height, width;
+- int n = 0;
+-
+- if (vs->output.offset && !vs->audio_cap && !vs->force_update)
+- /* kernel send buffers are full -> drop frames to throttle */
+- return 0;
++ if (!vs->need_update) {
++ return 0;
++ }
+
+- if (!vs->has_dirty && !vs->audio_cap && !vs->force_update)
+- return 0;
++ if (vs->output.offset && !vs->audio_cap && !vs->force_update) {
++ /* kernel send buffers are full -> drop frames to throttle */
++ return 0;
++ }
+
+- /*
+- * Send screen updates to the vnc client using the server
+- * surface and server dirty map. guest surface updates
+- * happening in parallel don't disturb us, the next pass will
+- * send them to the client.
+- */
+- job = vnc_job_new(vs);
+-
+- height = pixman_image_get_height(vd->server);
+- width = pixman_image_get_width(vd->server);
+-
+- y = 0;
+- for (;;) {
+- int x, h;
+- unsigned long x2;
+- unsigned long offset = find_next_bit((unsigned long *) &vs->dirty,
+- height * VNC_DIRTY_BPL(vs),
+- y * VNC_DIRTY_BPL(vs));
+- if (offset == height * VNC_DIRTY_BPL(vs)) {
+- /* no more dirty bits */
++ if (!vs->has_dirty && !vs->audio_cap && !vs->force_update) {
++ return 0;
++ }
++
++ /*
++ * Send screen updates to the vnc client using the server
++ * surface and server dirty map. guest surface updates
++ * happening in parallel don't disturb us, the next pass will
++ * send them to the client.
++ */
++ job = vnc_job_new(vs);
++
++ height = pixman_image_get_height(vd->server);
++ width = pixman_image_get_width(vd->server);
++
++ y = 0;
++ for (;;) {
++ int x, h;
++ unsigned long x2;
++ unsigned long offset = find_next_bit((unsigned long *) &vs->dirty,
++ height * VNC_DIRTY_BPL(vs),
++ y * VNC_DIRTY_BPL(vs));
++ if (offset == height * VNC_DIRTY_BPL(vs)) {
++ /* no more dirty bits */
++ break;
++ }
++ y = offset / VNC_DIRTY_BPL(vs);
++ x = offset % VNC_DIRTY_BPL(vs);
++ x2 = find_next_zero_bit((unsigned long *) &vs->dirty[y],
++ VNC_DIRTY_BPL(vs), x);
++ bitmap_clear(vs->dirty[y], x, x2 - x);
++ h = find_and_clear_dirty_height(vs, y, x, x2, height);
++ x2 = MIN(x2, width / VNC_DIRTY_PIXELS_PER_BIT);
++ if (x2 > x) {
++ n += vnc_job_add_rect(job, x * VNC_DIRTY_PIXELS_PER_BIT, y,
++ (x2 - x) * VNC_DIRTY_PIXELS_PER_BIT, h);
++ }
++ if (!x && x2 == width / VNC_DIRTY_PIXELS_PER_BIT) {
++ y += h;
++ if (y == height) {
+ break;
+ }
+- y = offset / VNC_DIRTY_BPL(vs);
+- x = offset % VNC_DIRTY_BPL(vs);
+- x2 = find_next_zero_bit((unsigned long *) &vs->dirty[y],
+- VNC_DIRTY_BPL(vs), x);
+- bitmap_clear(vs->dirty[y], x, x2 - x);
+- h = find_and_clear_dirty_height(vs, y, x, x2, height);
+- x2 = MIN(x2, width / VNC_DIRTY_PIXELS_PER_BIT);
+- if (x2 > x) {
+- n += vnc_job_add_rect(job, x * VNC_DIRTY_PIXELS_PER_BIT, y,
+- (x2 - x) * VNC_DIRTY_PIXELS_PER_BIT, h);
+- }
+- if (!x && x2 == width / VNC_DIRTY_PIXELS_PER_BIT) {
+- y += h;
+- if (y == height) {
+- break;
+- }
+- }
+ }
+-
+- vnc_job_push(job);
+- vs->force_update = 0;
+- vs->has_dirty = 0;
+- return n;
+ }
+
+- return 0;
++ vnc_job_push(job);
++ vs->force_update = 0;
++ vs->has_dirty = 0;
++ return n;
+ }
+
+ /* audio */
+--
+2.11.0
+
+
+From 3541b08475d51bddf8aded36576a0ff5a547a978 Mon Sep 17 00:00:00 2001
+From: "Daniel P. Berrange" <berrange@redhat.com>
+Date: Mon, 18 Dec 2017 19:12:19 +0000
+Subject: [PATCH 05/14] ui: avoid pointless VNC updates if framebuffer isn't
+ dirty
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The vnc_update_client() method checks the 'has_dirty' flag to see if there are
+dirty regions that are pending to send to the client. Regardless of this flag,
+if a forced update is requested, updates must be sent. For unknown reasons
+though, the code also tries to sent updates if audio capture is enabled. This
+makes no sense as audio capture state does not impact framebuffer contents, so
+this check is removed.
+
+Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
+Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
+Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
+Message-id: 20171218191228.31018-5-berrange@redhat.com
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ ui/vnc.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/ui/vnc.c b/ui/vnc.c
+index 7582111ca6..a79848f083 100644
+--- a/ui/vnc.c
++++ b/ui/vnc.c
+@@ -984,7 +984,7 @@ static int vnc_update_client(VncState *vs, int has_dirty)
+ return 0;
+ }
+
+- if (!vs->has_dirty && !vs->audio_cap && !vs->force_update) {
++ if (!vs->has_dirty && !vs->force_update) {
+ return 0;
+ }
+
+--
+2.11.0
+
+
+From 8f61f1c5a6bc06438a1172efa80bc7606594fa07 Mon Sep 17 00:00:00 2001
+From: "Daniel P. Berrange" <berrange@redhat.com>
+Date: Mon, 18 Dec 2017 19:12:20 +0000
+Subject: [PATCH 06/14] ui: track how much decoded data we consumed when doing
+ SASL encoding
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+When we encode data for writing with SASL, we encode the entire pending output
+buffer. The subsequent write, however, may not be able to send the full encoded
+data in one go though, particularly with a slow network. So we delay setting the
+output buffer offset back to zero until all the SASL encoded data is sent.
+
+Between encoding the data and completing sending of the SASL encoded data,
+however, more data might have been placed on the pending output buffer. So it
+is not valid to set offset back to zero. Instead we must keep track of how much
+data we consumed during encoding and subtract only that amount.
+
+With the current bug we would be throwing away some pending data without having
+sent it at all. By sheer luck this did not previously cause any serious problem
+because appending data to the send buffer is always an atomic action, so we
+only ever throw away complete RFB protocol messages. In the case of frame buffer
+updates we'd catch up fairly quickly, so no obvious problem was visible.
+
+Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
+Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
+Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
+Message-id: 20171218191228.31018-6-berrange@redhat.com
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ ui/vnc-auth-sasl.c | 3 ++-
+ ui/vnc-auth-sasl.h | 1 +
+ 2 files changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/ui/vnc-auth-sasl.c b/ui/vnc-auth-sasl.c
+index 23f28280e7..761493b9b2 100644
+--- a/ui/vnc-auth-sasl.c
++++ b/ui/vnc-auth-sasl.c
+@@ -67,6 +67,7 @@ long vnc_client_write_sasl(VncState *vs)
+ if (err != SASL_OK)
+ return vnc_client_io_error(vs, -1, NULL);
+
++ vs->sasl.encodedRawLength = vs->output.offset;
+ vs->sasl.encodedOffset = 0;
+ }
+
+@@ -78,7 +79,7 @@ long vnc_client_write_sasl(VncState *vs)
+
+ vs->sasl.encodedOffset += ret;
+ if (vs->sasl.encodedOffset == vs->sasl.encodedLength) {
+- vs->output.offset = 0;
++ vs->output.offset -= vs->sasl.encodedRawLength;
+ vs->sasl.encoded = NULL;
+ vs->sasl.encodedOffset = vs->sasl.encodedLength = 0;
+ }
+diff --git a/ui/vnc-auth-sasl.h b/ui/vnc-auth-sasl.h
+index cb42745a6b..b9d8de1c10 100644
+--- a/ui/vnc-auth-sasl.h
++++ b/ui/vnc-auth-sasl.h
+@@ -53,6 +53,7 @@ struct VncStateSASL {
+ */
+ const uint8_t *encoded;
+ unsigned int encodedLength;
++ unsigned int encodedRawLength;
+ unsigned int encodedOffset;
+ char *username;
+ char *mechlist;
+--
+2.11.0
+
+
+From fef1bbadfb2c3027208eb3d14b43e1bdb51166ca Mon Sep 17 00:00:00 2001
+From: "Daniel P. Berrange" <berrange@redhat.com>
+Date: Mon, 18 Dec 2017 19:12:21 +0000
+Subject: [PATCH 07/14] ui: introduce enum to track VNC client framebuffer
+ update request state
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Currently the VNC servers tracks whether a client has requested an incremental
+or forced update with two boolean flags. There are only really 3 distinct
+states to track, so create an enum to more accurately reflect permitted states.
+
+Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
+Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
+Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
+Message-id: 20171218191228.31018-7-berrange@redhat.com
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ ui/vnc.c | 21 +++++++++++----------
+ ui/vnc.h | 9 +++++++--
+ 2 files changed, 18 insertions(+), 12 deletions(-)
+
+diff --git a/ui/vnc.c b/ui/vnc.c
+index a79848f083..30e2feeae3 100644
+--- a/ui/vnc.c
++++ b/ui/vnc.c
+@@ -975,16 +975,17 @@ static int vnc_update_client(VncState *vs, int has_dirty)
+ }
+
+ vs->has_dirty += has_dirty;
+- if (!vs->need_update) {
++ if (vs->update == VNC_STATE_UPDATE_NONE) {
+ return 0;
+ }
+
+- if (vs->output.offset && !vs->audio_cap && !vs->force_update) {
++ if (vs->output.offset && !vs->audio_cap &&
++ vs->update != VNC_STATE_UPDATE_FORCE) {
+ /* kernel send buffers are full -> drop frames to throttle */
+ return 0;
+ }
+
+- if (!vs->has_dirty && !vs->force_update) {
++ if (!vs->has_dirty && vs->update != VNC_STATE_UPDATE_FORCE) {
+ return 0;
+ }
+
+@@ -1030,7 +1031,7 @@ static int vnc_update_client(VncState *vs, int has_dirty)
+ }
+
+ vnc_job_push(job);
+- vs->force_update = 0;
++ vs->update = VNC_STATE_UPDATE_INCREMENTAL;
+ vs->has_dirty = 0;
+ return n;
+ }
+@@ -1869,14 +1870,14 @@ static void ext_key_event(VncState *vs, int down,
+ static void framebuffer_update_request(VncState *vs, int incremental,
+ int x, int y, int w, int h)
+ {
+- vs->need_update = 1;
+-
+ if (incremental) {
+- return;
++ if (vs->update != VNC_STATE_UPDATE_FORCE) {
++ vs->update = VNC_STATE_UPDATE_INCREMENTAL;
++ }
++ } else {
++ vs->update = VNC_STATE_UPDATE_FORCE;
++ vnc_set_area_dirty(vs->dirty, vs->vd, x, y, w, h);
+ }
+-
+- vs->force_update = 1;
+- vnc_set_area_dirty(vs->dirty, vs->vd, x, y, w, h);
+ }
+
+ static void send_ext_key_event_ack(VncState *vs)
+diff --git a/ui/vnc.h b/ui/vnc.h
+index 694cf32ca9..b9d310e640 100644
+--- a/ui/vnc.h
++++ b/ui/vnc.h
+@@ -252,6 +252,12 @@ struct VncJob
+ QTAILQ_ENTRY(VncJob) next;
+ };
+
++typedef enum {
++ VNC_STATE_UPDATE_NONE,
++ VNC_STATE_UPDATE_INCREMENTAL,
++ VNC_STATE_UPDATE_FORCE,
++} VncStateUpdate;
++
+ struct VncState
+ {
+ QIOChannelSocket *sioc; /* The underlying socket */
+@@ -264,8 +270,7 @@ struct VncState
+ * vnc-jobs-async.c */
+
+ VncDisplay *vd;
+- int need_update;
+- int force_update;
++ VncStateUpdate update; /* Most recent pending request from client */
+ int has_dirty;
+ uint32_t features;
+ int absolute;
+--
+2.11.0
+
+
+From 728a7ac95484a7ba5e624ccbac4c1326571576b0 Mon Sep 17 00:00:00 2001
+From: "Daniel P. Berrange" <berrange@redhat.com>
+Date: Mon, 18 Dec 2017 19:12:22 +0000
+Subject: [PATCH 08/14] ui: correctly reset framebuffer update state after
+ processing dirty regions
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+According to the RFB protocol, a client sends one or more framebuffer update
+requests to the server. The server can reply with a single framebuffer update
+response, that covers all previously received requests. Once the client has
+read this update from the server, it may send further framebuffer update
+requests to monitor future changes. The client is free to delay sending the
+framebuffer update request if it needs to throttle the amount of data it is
+reading from the server.
+
+The QEMU VNC server, however, has never correctly handled the framebuffer
+update requests. Once QEMU has received an update request, it will continue to
+send client updates forever, even if the client hasn't asked for further
+updates. This prevents the client from throttling back data it gets from the
+server. This change fixes the flawed logic such that after a set of updates are
+sent out, QEMU waits for a further update request before sending more data.
+
+Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
+Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
+Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
+Message-id: 20171218191228.31018-8-berrange@redhat.com
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ ui/vnc.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/ui/vnc.c b/ui/vnc.c
+index 30e2feeae3..243c72be13 100644
+--- a/ui/vnc.c
++++ b/ui/vnc.c
+@@ -1031,7 +1031,7 @@ static int vnc_update_client(VncState *vs, int has_dirty)
+ }
+
+ vnc_job_push(job);
+- vs->update = VNC_STATE_UPDATE_INCREMENTAL;
++ vs->update = VNC_STATE_UPDATE_NONE;
+ vs->has_dirty = 0;
+ return n;
+ }
+--
+2.11.0
+
+
+From 0bad834228b9ee63e4239108d02dcb94568254d0 Mon Sep 17 00:00:00 2001
+From: "Daniel P. Berrange" <berrange@redhat.com>
+Date: Mon, 18 Dec 2017 19:12:23 +0000
+Subject: [PATCH 09/14] ui: refactor code for determining if an update should
+ be sent to the client
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The logic for determining if it is possible to send an update to the client
+will become more complicated shortly, so pull it out into a separate method
+for easier extension later.
+
+Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
+Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
+Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
+Message-id: 20171218191228.31018-9-berrange@redhat.com
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ ui/vnc.c | 27 ++++++++++++++++++++-------
+ 1 file changed, 20 insertions(+), 7 deletions(-)
+
+diff --git a/ui/vnc.c b/ui/vnc.c
+index 243c72be13..4ba7fc076a 100644
+--- a/ui/vnc.c
++++ b/ui/vnc.c
+@@ -961,6 +961,25 @@ static int find_and_clear_dirty_height(VncState *vs,
+ return h;
+ }
+
++static bool vnc_should_update(VncState *vs)
++{
++ switch (vs->update) {
++ case VNC_STATE_UPDATE_NONE:
++ break;
++ case VNC_STATE_UPDATE_INCREMENTAL:
++ /* Only allow incremental updates if the output buffer
++ * is empty, or if audio capture is enabled.
++ */
++ if (!vs->output.offset || vs->audio_cap) {
++ return true;
++ }
++ break;
++ case VNC_STATE_UPDATE_FORCE:
++ return true;
++ }
++ return false;
++}
++
+ static int vnc_update_client(VncState *vs, int has_dirty)
+ {
+ VncDisplay *vd = vs->vd;
+@@ -975,13 +994,7 @@ static int vnc_update_client(VncState *vs, int has_dirty)
+ }
+
+ vs->has_dirty += has_dirty;
+- if (vs->update == VNC_STATE_UPDATE_NONE) {
+- return 0;
+- }
+-
+- if (vs->output.offset && !vs->audio_cap &&
+- vs->update != VNC_STATE_UPDATE_FORCE) {
+- /* kernel send buffers are full -> drop frames to throttle */
++ if (!vnc_should_update(vs)) {
+ return 0;
+ }
+
+--
+2.11.0
+
+
+From e2b72cb6e0443d90d7ab037858cb6834b6cca852 Mon Sep 17 00:00:00 2001
+From: "Daniel P. Berrange" <berrange@redhat.com>
+Date: Mon, 18 Dec 2017 19:12:24 +0000
+Subject: [PATCH 10/14] ui: fix VNC client throttling when audio capture is
+ active
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The VNC server must throttle data sent to the client to prevent the 'output'
+buffer size growing without bound, if the client stops reading data off the
+socket (either maliciously or due to stalled/slow network connection).
+
+The current throttling is very crude because it simply checks whether the
+output buffer offset is zero. This check must be disabled if audio capture is
+enabled, because when streaming audio the output buffer offset will rarely be
+zero due to queued audio data, and so this would starve framebuffer updates.
+
+As a result, the VNC client can cause QEMU to allocate arbitrary amounts of RAM.
+They can first start something in the guest that triggers lots of framebuffer
+updates eg play a youtube video. Then enable audio capture, and simply never
+read data back from the server. This can easily make QEMU's VNC server send
+buffer consume 100MB of RAM per second, until the OOM killer starts reaping
+processes (hopefully the rogue QEMU process, but it might pick others...).
+
+To address this we make the throttling more intelligent, so we can throttle
+when audio capture is active too. To determine how to throttle incremental
+updates or audio data, we calculate a size threshold. Normally the threshold is
+the approximate number of bytes associated with a single complete framebuffer
+update. ie width * height * bytes per pixel. We'll send incremental updates
+until we hit this threshold, at which point we'll stop sending updates until
+data has been written to the wire, causing the output buffer offset to fall
+back below the threshold.
+
+If audio capture is enabled, we increase the size of the threshold to also
+allow for upto 1 seconds worth of audio data samples. ie nchannels * bytes
+per sample * frequency. This allows the output buffer to have a mixture of
+incremental framebuffer updates and audio data queued, but once the threshold
+is exceeded, audio data will be dropped and incremental updates will be
+throttled.
+
+This unbounded memory growth affects all VNC server configurations supported by
+QEMU, with no workaround possible. The mitigating factor is that it can only be
+triggered by a client that has authenticated with the VNC server, and who is
+able to trigger a large quantity of framebuffer updates or audio samples from
+the guest OS. Mostly they'll just succeed in getting the OOM killer to kill
+their own QEMU process, but its possible other processes can get taken out as
+collateral damage.
+
+This is a more general variant of the similar unbounded memory usage flaw in
+the websockets server, that was previously assigned CVE-2017-15268, and fixed
+in 2.11 by:
+
+ commit a7b20a8efa28e5f22c26c06cd06c2f12bc863493
+ Author: Daniel P. Berrange <berrange@redhat.com>
+ Date: Mon Oct 9 14:43:42 2017 +0100
+
+ io: monitor encoutput buffer size from websocket GSource
+
+This new general memory usage flaw has been assigned CVE-2017-15124, and is
+partially fixed by this patch.
+
+Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
+Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
+Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
+Message-id: 20171218191228.31018-10-berrange@redhat.com
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ ui/vnc.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
+ ui/vnc.h | 6 ++++++
+ 2 files changed, 70 insertions(+), 8 deletions(-)
+
+diff --git a/ui/vnc.c b/ui/vnc.c
+index 4ba7fc076a..9e03cc7c01 100644
+--- a/ui/vnc.c
++++ b/ui/vnc.c
+@@ -60,6 +60,7 @@ static QTAILQ_HEAD(, VncDisplay) vnc_displays =
+
+ static int vnc_cursor_define(VncState *vs);
+ static void vnc_release_modifiers(VncState *vs);
++static void vnc_update_throttle_offset(VncState *vs);
+
+ static void vnc_set_share_mode(VncState *vs, VncShareMode mode)
+ {
+@@ -766,6 +767,7 @@ static void vnc_dpy_switch(DisplayChangeListener *dcl,
+ vnc_set_area_dirty(vs->dirty, vd, 0, 0,
+ vnc_width(vd),
+ vnc_height(vd));
++ vnc_update_throttle_offset(vs);
+ }
+ }
+
+@@ -961,16 +963,67 @@ static int find_and_clear_dirty_height(VncState *vs,
+ return h;
+ }
+
++/*
++ * Figure out how much pending data we should allow in the output
++ * buffer before we throttle incremental display updates, and/or
++ * drop audio samples.
++ *
++ * We allow for equiv of 1 full display's worth of FB updates,
++ * and 1 second of audio samples. If audio backlog was larger
++ * than that the client would already suffering awful audio
++ * glitches, so dropping samples is no worse really).
++ */
++static void vnc_update_throttle_offset(VncState *vs)
++{
++ size_t offset =
++ vs->client_width * vs->client_height * vs->client_pf.bytes_per_pixel;
++
++ if (vs->audio_cap) {
++ int freq = vs->as.freq;
++ /* We don't limit freq when reading settings from client, so
++ * it could be upto MAX_INT in size. 48khz is a sensible
++ * upper bound for trustworthy clients */
++ int bps;
++ if (freq > 48000) {
++ freq = 48000;
++ }
++ switch (vs->as.fmt) {
++ default:
++ case AUD_FMT_U8:
++ case AUD_FMT_S8:
++ bps = 1;
++ break;
++ case AUD_FMT_U16:
++ case AUD_FMT_S16:
++ bps = 2;
++ break;
++ case AUD_FMT_U32:
++ case AUD_FMT_S32:
++ bps = 4;
++ break;
++ }
++ offset += freq * bps * vs->as.nchannels;
++ }
++
++ /* Put a floor of 1MB on offset, so that if we have a large pending
++ * buffer and the display is resized to a small size & back again
++ * we don't suddenly apply a tiny send limit
++ */
++ offset = MAX(offset, 1024 * 1024);
++
++ vs->throttle_output_offset = offset;
++}
++
+ static bool vnc_should_update(VncState *vs)
+ {
+ switch (vs->update) {
+ case VNC_STATE_UPDATE_NONE:
+ break;
+ case VNC_STATE_UPDATE_INCREMENTAL:
+- /* Only allow incremental updates if the output buffer
+- * is empty, or if audio capture is enabled.
++ /* Only allow incremental updates if the pending send queue
++ * is less than the permitted threshold
+ */
+- if (!vs->output.offset || vs->audio_cap) {
++ if (vs->output.offset < vs->throttle_output_offset) {
+ return true;
+ }
+ break;
+@@ -1084,11 +1137,13 @@ static void audio_capture(void *opaque, void *buf, int size)
+ VncState *vs = opaque;
+
+ vnc_lock_output(vs);
+- vnc_write_u8(vs, VNC_MSG_SERVER_QEMU);
+- vnc_write_u8(vs, VNC_MSG_SERVER_QEMU_AUDIO);
+- vnc_write_u16(vs, VNC_MSG_SERVER_QEMU_AUDIO_DATA);
+- vnc_write_u32(vs, size);
+- vnc_write(vs, buf, size);
++ if (vs->output.offset < vs->throttle_output_offset) {
++ vnc_write_u8(vs, VNC_MSG_SERVER_QEMU);
++ vnc_write_u8(vs, VNC_MSG_SERVER_QEMU_AUDIO);
++ vnc_write_u16(vs, VNC_MSG_SERVER_QEMU_AUDIO_DATA);
++ vnc_write_u32(vs, size);
++ vnc_write(vs, buf, size);
++ }
+ vnc_unlock_output(vs);
+ vnc_flush(vs);
+ }
+@@ -2288,6 +2343,7 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len)
+ break;
+ }
+
++ vnc_update_throttle_offset(vs);
+ vnc_read_when(vs, protocol_client_msg, 1);
+ return 0;
+ }
+diff --git a/ui/vnc.h b/ui/vnc.h
+index b9d310e640..8fe69595c6 100644
+--- a/ui/vnc.h
++++ b/ui/vnc.h
+@@ -298,6 +298,12 @@ struct VncState
+
+ VncClientInfo *info;
+
++ /* We allow multiple incremental updates or audio capture
++ * samples to be queued in output buffer, provided the
++ * buffer size doesn't exceed this threshold. The value
++ * is calculating dynamically based on framebuffer size
++ * and audio sample settings in vnc_update_throttle_offset() */
++ size_t throttle_output_offset;
+ Buffer output;
+ Buffer input;
+ /* current output mode information */
+--
+2.11.0
+
+
+From ada8d2e4369ea49677d8672ac81bce73eefd5b54 Mon Sep 17 00:00:00 2001
+From: "Daniel P. Berrange" <berrange@redhat.com>
+Date: Mon, 18 Dec 2017 19:12:25 +0000
+Subject: [PATCH 11/14] ui: fix VNC client throttling when forced update is
+ requested
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The VNC server must throttle data sent to the client to prevent the 'output'
+buffer size growing without bound, if the client stops reading data off the
+socket (either maliciously or due to stalled/slow network connection).
+
+The current throttling is very crude because it simply checks whether the
+output buffer offset is zero. This check is disabled if the client has requested
+a forced update, because we want to send these as soon as possible.
+
+As a result, the VNC client can cause QEMU to allocate arbitrary amounts of RAM.
+They can first start something in the guest that triggers lots of framebuffer
+updates eg play a youtube video. Then repeatedly send full framebuffer update
+requests, but never read data back from the server. This can easily make QEMU's
+VNC server send buffer consume 100MB of RAM per second, until the OOM killer
+starts reaping processes (hopefully the rogue QEMU process, but it might pick
+others...).
+
+To address this we make the throttling more intelligent, so we can throttle
+full updates. When we get a forced update request, we keep track of exactly how
+much data we put on the output buffer. We will not process a subsequent forced
+update request until this data has been fully sent on the wire. We always allow
+one forced update request to be in flight, regardless of what data is queued
+for incremental updates or audio data. The slight complication is that we do
+not initially know how much data an update will send, as this is done in the
+background by the VNC job thread. So we must track the fact that the job thread
+has an update pending, and not process any further updates until this job is
+has been completed & put data on the output buffer.
+
+This unbounded memory growth affects all VNC server configurations supported by
+QEMU, with no workaround possible. The mitigating factor is that it can only be
+triggered by a client that has authenticated with the VNC server, and who is
+able to trigger a large quantity of framebuffer updates or audio samples from
+the guest OS. Mostly they'll just succeed in getting the OOM killer to kill
+their own QEMU process, but its possible other processes can get taken out as
+collateral damage.
+
+This is a more general variant of the similar unbounded memory usage flaw in
+the websockets server, that was previously assigned CVE-2017-15268, and fixed
+in 2.11 by:
+
+ commit a7b20a8efa28e5f22c26c06cd06c2f12bc863493
+ Author: Daniel P. Berrange <berrange@redhat.com>
+ Date: Mon Oct 9 14:43:42 2017 +0100
+
+ io: monitor encoutput buffer size from websocket GSource
+
+This new general memory usage flaw has been assigned CVE-2017-15124, and is
+partially fixed by this patch.
+
+Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
+Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
+Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
+Message-id: 20171218191228.31018-11-berrange@redhat.com
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ ui/vnc-auth-sasl.c | 5 +++++
+ ui/vnc-jobs.c | 5 +++++
+ ui/vnc.c | 28 ++++++++++++++++++++++++----
+ ui/vnc.h | 7 +++++++
+ 4 files changed, 41 insertions(+), 4 deletions(-)
+
+diff --git a/ui/vnc-auth-sasl.c b/ui/vnc-auth-sasl.c
+index 761493b9b2..8c1cdde3db 100644
+--- a/ui/vnc-auth-sasl.c
++++ b/ui/vnc-auth-sasl.c
+@@ -79,6 +79,11 @@ long vnc_client_write_sasl(VncState *vs)
+
+ vs->sasl.encodedOffset += ret;
+ if (vs->sasl.encodedOffset == vs->sasl.encodedLength) {
++ if (vs->sasl.encodedRawLength >= vs->force_update_offset) {
++ vs->force_update_offset = 0;
++ } else {
++ vs->force_update_offset -= vs->sasl.encodedRawLength;
++ }
+ vs->output.offset -= vs->sasl.encodedRawLength;
+ vs->sasl.encoded = NULL;
+ vs->sasl.encodedOffset = vs->sasl.encodedLength = 0;
+diff --git a/ui/vnc-jobs.c b/ui/vnc-jobs.c
+index f7867771ae..e326679dd0 100644
+--- a/ui/vnc-jobs.c
++++ b/ui/vnc-jobs.c
+@@ -152,6 +152,11 @@ void vnc_jobs_consume_buffer(VncState *vs)
+ vs->ioc, G_IO_IN | G_IO_OUT, vnc_client_io, vs, NULL);
+ }
+ buffer_move(&vs->output, &vs->jobs_buffer);
++
++ if (vs->job_update == VNC_STATE_UPDATE_FORCE) {
++ vs->force_update_offset = vs->output.offset;
++ }
++ vs->job_update = VNC_STATE_UPDATE_NONE;
+ }
+ flush = vs->ioc != NULL && vs->abort != true;
+ vnc_unlock_output(vs);
+diff --git a/ui/vnc.c b/ui/vnc.c
+index 9e03cc7c01..4805ac41d0 100644
+--- a/ui/vnc.c
++++ b/ui/vnc.c
+@@ -1021,14 +1021,28 @@ static bool vnc_should_update(VncState *vs)
+ break;
+ case VNC_STATE_UPDATE_INCREMENTAL:
+ /* Only allow incremental updates if the pending send queue
+- * is less than the permitted threshold
++ * is less than the permitted threshold, and the job worker
++ * is completely idle.
+ */
+- if (vs->output.offset < vs->throttle_output_offset) {
++ if (vs->output.offset < vs->throttle_output_offset &&
++ vs->job_update == VNC_STATE_UPDATE_NONE) {
+ return true;
+ }
+ break;
+ case VNC_STATE_UPDATE_FORCE:
+- return true;
++ /* Only allow forced updates if the pending send queue
++ * does not contain a previous forced update, and the
++ * job worker is completely idle.
++ *
++ * Note this means we'll queue a forced update, even if
++ * the output buffer size is otherwise over the throttle
++ * output limit.
++ */
++ if (vs->force_update_offset == 0 &&
++ vs->job_update == VNC_STATE_UPDATE_NONE) {
++ return true;
++ }
++ break;
+ }
+ return false;
+ }
+@@ -1096,8 +1110,9 @@ static int vnc_update_client(VncState *vs, int has_dirty)
+ }
+ }
+
+- vnc_job_push(job);
++ vs->job_update = vs->update;
+ vs->update = VNC_STATE_UPDATE_NONE;
++ vnc_job_push(job);
+ vs->has_dirty = 0;
+ return n;
+ }
+@@ -1332,6 +1347,11 @@ static ssize_t vnc_client_write_plain(VncState *vs)
+ if (!ret)
+ return 0;
+
++ if (ret >= vs->force_update_offset) {
++ vs->force_update_offset = 0;
++ } else {
++ vs->force_update_offset -= ret;
++ }
+ buffer_advance(&vs->output, ret);
+
+ if (vs->output.offset == 0) {
+diff --git a/ui/vnc.h b/ui/vnc.h
+index 8fe69595c6..3f4cd4d93d 100644
+--- a/ui/vnc.h
++++ b/ui/vnc.h
+@@ -271,6 +271,7 @@ struct VncState
+
+ VncDisplay *vd;
+ VncStateUpdate update; /* Most recent pending request from client */
++ VncStateUpdate job_update; /* Currently processed by job thread */
+ int has_dirty;
+ uint32_t features;
+ int absolute;
+@@ -298,6 +299,12 @@ struct VncState
+
+ VncClientInfo *info;
+
++ /* Job thread bottom half has put data for a forced update
++ * into the output buffer. This offset points to the end of
++ * the update data in the output buffer. This lets us determine
++ * when a force update is fully sent to the client, allowing
++ * us to process further forced updates. */
++ size_t force_update_offset;
+ /* We allow multiple incremental updates or audio capture
+ * samples to be queued in output buffer, provided the
+ * buffer size doesn't exceed this threshold. The value
+--
+2.11.0
+
+
+From f887cf165db20f405cb8805c716bd363aaadf815 Mon Sep 17 00:00:00 2001
+From: "Daniel P. Berrange" <berrange@redhat.com>
+Date: Mon, 18 Dec 2017 19:12:26 +0000
+Subject: [PATCH 12/14] ui: place a hard cap on VNC server output buffer size
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The previous patches fix problems with throttling of forced framebuffer updates
+and audio data capture that would cause the QEMU output buffer size to grow
+without bound. Those fixes are graceful in that once the client catches up with
+reading data from the server, everything continues operating normally.
+
+There is some data which the server sends to the client that is impractical to
+throttle. Specifically there are various pseudo framebuffer update encodings to
+inform the client of things like desktop resizes, pointer changes, audio
+playback start/stop, LED state and so on. These generally only involve sending
+a very small amount of data to the client, but a malicious guest might be able
+to do things that trigger these changes at a very high rate. Throttling them is
+not practical as missed or delayed events would cause broken behaviour for the
+client.
+
+This patch thus takes a more forceful approach of setting an absolute upper
+bound on the amount of data we permit to be present in the output buffer at
+any time. The previous patch set a threshold for throttling the output buffer
+by allowing an amount of data equivalent to one complete framebuffer update and
+one seconds worth of audio data. On top of this it allowed for one further
+forced framebuffer update to be queued.
+
+To be conservative, we thus take that throttling threshold and multiply it by
+5 to form an absolute upper bound. If this bound is hit during vnc_write() we
+forceably disconnect the client, refusing to queue further data. This limit is
+high enough that it should never be hit unless a malicious client is trying to
+exploit the sever, or the network is completely saturated preventing any sending
+of data on the socket.
+
+This completes the fix for CVE-2017-15124 started in the previous patches.
+
+Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
+Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
+Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
+Message-id: 20171218191228.31018-12-berrange@redhat.com
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ ui/vnc.c | 29 +++++++++++++++++++++++++++++
+ 1 file changed, 29 insertions(+)
+
+diff --git a/ui/vnc.c b/ui/vnc.c
+index 4805ac41d0..e53e84587a 100644
+--- a/ui/vnc.c
++++ b/ui/vnc.c
+@@ -1521,8 +1521,37 @@ gboolean vnc_client_io(QIOChannel *ioc G_GNUC_UNUSED,
+ }
+
+
++/*
++ * Scale factor to apply to vs->throttle_output_offset when checking for
++ * hard limit. Worst case normal usage could be x2, if we have a complete
++ * incremental update and complete forced update in the output buffer.
++ * So x3 should be good enough, but we pick x5 to be conservative and thus
++ * (hopefully) never trigger incorrectly.
++ */
++#define VNC_THROTTLE_OUTPUT_LIMIT_SCALE 5
++
+ void vnc_write(VncState *vs, const void *data, size_t len)
+ {
++ if (vs->disconnecting) {
++ return;
++ }
++ /* Protection against malicious client/guest to prevent our output
++ * buffer growing without bound if client stops reading data. This
++ * should rarely trigger, because we have earlier throttling code
++ * which stops issuing framebuffer updates and drops audio data
++ * if the throttle_output_offset value is exceeded. So we only reach
++ * this higher level if a huge number of pseudo-encodings get
++ * triggered while data can't be sent on the socket.
++ *
++ * NB throttle_output_offset can be zero during early protocol
++ * handshake, or from the job thread's VncState clone
++ */
++ if (vs->throttle_output_offset != 0 &&
++ vs->output.offset > (vs->throttle_output_offset *
++ VNC_THROTTLE_OUTPUT_LIMIT_SCALE)) {
++ vnc_disconnect_start(vs);
++ return;
++ }
+ buffer_reserve(&vs->output, len);
+
+ if (vs->ioc != NULL && buffer_empty(&vs->output)) {
+--
+2.11.0
+
+
+From 6aa22a29187e1908f5db738d27c64a9efc8d0bfa Mon Sep 17 00:00:00 2001
+From: "Daniel P. Berrange" <berrange@redhat.com>
+Date: Mon, 18 Dec 2017 19:12:27 +0000
+Subject: [PATCH 13/14] ui: add trace events related to VNC client throttling
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The VNC client throttling is quite subtle so will benefit from having trace
+points available for live debugging.
+
+Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
+Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
+Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
+Message-id: 20171218191228.31018-13-berrange@redhat.com
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ ui/trace-events | 7 +++++++
+ ui/vnc.c | 23 +++++++++++++++++++++++
+ 2 files changed, 30 insertions(+)
+
+diff --git a/ui/trace-events b/ui/trace-events
+index 1a9f126330..85f74f948b 100644
+--- a/ui/trace-events
++++ b/ui/trace-events
+@@ -35,6 +35,13 @@ vnc_client_connect(void *state, void *ioc) "VNC client connect state=%p ioc=%p"
+ vnc_client_disconnect_start(void *state, void *ioc) "VNC client disconnect start state=%p ioc=%p"
+ vnc_client_disconnect_finish(void *state, void *ioc) "VNC client disconnect finish state=%p ioc=%p"
+ vnc_client_io_wrap(void *state, void *ioc, const char *type) "VNC client I/O wrap state=%p ioc=%p type=%s"
++vnc_client_throttle_threshold(void *state, void *ioc, size_t oldoffset, size_t offset, int client_width, int client_height, int bytes_per_pixel, void *audio_cap) "VNC client throttle threshold state=%p ioc=%p oldoffset=%zu newoffset=%zu width=%d height=%d bpp=%d audio=%p"
++vnc_client_throttle_incremental(void *state, void *ioc, int job_update, size_t offset) "VNC client throttle incremental state=%p ioc=%p job-update=%d offset=%zu"
++vnc_client_throttle_forced(void *state, void *ioc, int job_update, size_t offset) "VNC client throttle forced state=%p ioc=%p job-update=%d offset=%zu"
++vnc_client_throttle_audio(void *state, void *ioc, size_t offset) "VNC client throttle audio state=%p ioc=%p offset=%zu"
++vnc_client_unthrottle_forced(void *state, void *ioc) "VNC client unthrottle forced offset state=%p ioc=%p"
++vnc_client_unthrottle_incremental(void *state, void *ioc, size_t offset) "VNC client unthrottle incremental state=%p ioc=%p offset=%zu"
++vnc_client_output_limit(void *state, void *ioc, size_t offset, size_t threshold) "VNC client output limit state=%p ioc=%p offset=%zu threshold=%zu"
+ vnc_auth_init(void *display, int websock, int auth, int subauth) "VNC auth init state=%p websock=%d auth=%d subauth=%d"
+ vnc_auth_start(void *state, int method) "VNC client auth start state=%p method=%d"
+ vnc_auth_pass(void *state, int method) "VNC client auth passed state=%p method=%d"
+diff --git a/ui/vnc.c b/ui/vnc.c
+index e53e84587a..0a5e629d5d 100644
+--- a/ui/vnc.c
++++ b/ui/vnc.c
+@@ -1011,6 +1011,12 @@ static void vnc_update_throttle_offset(VncState *vs)
+ */
+ offset = MAX(offset, 1024 * 1024);
+
++ if (vs->throttle_output_offset != offset) {
++ trace_vnc_client_throttle_threshold(
++ vs, vs->ioc, vs->throttle_output_offset, offset, vs->client_width,
++ vs->client_height, vs->client_pf.bytes_per_pixel, vs->audio_cap);
++ }
++
+ vs->throttle_output_offset = offset;
+ }
+
+@@ -1028,6 +1034,8 @@ static bool vnc_should_update(VncState *vs)
+ vs->job_update == VNC_STATE_UPDATE_NONE) {
+ return true;
+ }
++ trace_vnc_client_throttle_incremental(
++ vs, vs->ioc, vs->job_update, vs->output.offset);
+ break;
+ case VNC_STATE_UPDATE_FORCE:
+ /* Only allow forced updates if the pending send queue
+@@ -1042,6 +1050,8 @@ static bool vnc_should_update(VncState *vs)
+ vs->job_update == VNC_STATE_UPDATE_NONE) {
+ return true;
+ }
++ trace_vnc_client_throttle_forced(
++ vs, vs->ioc, vs->job_update, vs->force_update_offset);
+ break;
+ }
+ return false;
+@@ -1158,6 +1168,8 @@ static void audio_capture(void *opaque, void *buf, int size)
+ vnc_write_u16(vs, VNC_MSG_SERVER_QEMU_AUDIO_DATA);
+ vnc_write_u32(vs, size);
+ vnc_write(vs, buf, size);
++ } else {
++ trace_vnc_client_throttle_audio(vs, vs->ioc, vs->output.offset);
+ }
+ vnc_unlock_output(vs);
+ vnc_flush(vs);
+@@ -1328,6 +1340,7 @@ ssize_t vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen)
+ */
+ static ssize_t vnc_client_write_plain(VncState *vs)
+ {
++ size_t offset;
+ ssize_t ret;
+
+ #ifdef CONFIG_VNC_SASL
+@@ -1348,11 +1361,19 @@ static ssize_t vnc_client_write_plain(VncState *vs)
+ return 0;
+
+ if (ret >= vs->force_update_offset) {
++ if (vs->force_update_offset != 0) {
++ trace_vnc_client_unthrottle_forced(vs, vs->ioc);
++ }
+ vs->force_update_offset = 0;
+ } else {
+ vs->force_update_offset -= ret;
+ }
++ offset = vs->output.offset;
+ buffer_advance(&vs->output, ret);
++ if (offset >= vs->throttle_output_offset &&
++ vs->output.offset < vs->throttle_output_offset) {
++ trace_vnc_client_unthrottle_incremental(vs, vs->ioc, vs->output.offset);
++ }
+
+ if (vs->output.offset == 0) {
+ if (vs->ioc_tag) {
+@@ -1549,6 +1570,8 @@ void vnc_write(VncState *vs, const void *data, size_t len)
+ if (vs->throttle_output_offset != 0 &&
+ vs->output.offset > (vs->throttle_output_offset *
+ VNC_THROTTLE_OUTPUT_LIMIT_SCALE)) {
++ trace_vnc_client_output_limit(vs, vs->ioc, vs->output.offset,
++ vs->throttle_output_offset);
+ vnc_disconnect_start(vs);
+ return;
+ }
+--
+2.11.0
+
+
+From 30b80fd5269257f55203b7072c505b4ebaab5115 Mon Sep 17 00:00:00 2001
+From: "Daniel P. Berrange" <berrange@redhat.com>
+Date: Mon, 18 Dec 2017 19:12:28 +0000
+Subject: [PATCH 14/14] ui: mix misleading comments & return types of VNC I/O
+ helper methods
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+While the QIOChannel APIs for reading/writing data return ssize_t, with negative
+value indicating an error, the VNC code passes this return value through the
+vnc_client_io_error() method. This detects the error condition, disconnects the
+client and returns 0 to indicate error. Thus all the VNC helper methods should
+return size_t (unsigned), and misleading comments which refer to the possibility
+of negative return values need fixing.
+
+Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
+Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
+Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
+Message-id: 20171218191228.31018-14-berrange@redhat.com
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ ui/vnc-auth-sasl.c | 8 ++++----
+ ui/vnc-auth-sasl.h | 4 ++--
+ ui/vnc.c | 29 +++++++++++++++--------------
+ ui/vnc.h | 6 +++---
+ 4 files changed, 24 insertions(+), 23 deletions(-)
+
+diff --git a/ui/vnc-auth-sasl.c b/ui/vnc-auth-sasl.c
+index 8c1cdde3db..74a5f513f2 100644
+--- a/ui/vnc-auth-sasl.c
++++ b/ui/vnc-auth-sasl.c
+@@ -48,9 +48,9 @@ void vnc_sasl_client_cleanup(VncState *vs)
+ }
+
+
+-long vnc_client_write_sasl(VncState *vs)
++size_t vnc_client_write_sasl(VncState *vs)
+ {
+- long ret;
++ size_t ret;
+
+ VNC_DEBUG("Write SASL: Pending output %p size %zd offset %zd "
+ "Encoded: %p size %d offset %d\n",
+@@ -106,9 +106,9 @@ long vnc_client_write_sasl(VncState *vs)
+ }
+
+
+-long vnc_client_read_sasl(VncState *vs)
++size_t vnc_client_read_sasl(VncState *vs)
+ {
+- long ret;
++ size_t ret;
+ uint8_t encoded[4096];
+ const char *decoded;
+ unsigned int decodedLen;
+diff --git a/ui/vnc-auth-sasl.h b/ui/vnc-auth-sasl.h
+index b9d8de1c10..2ae224ee3a 100644
+--- a/ui/vnc-auth-sasl.h
++++ b/ui/vnc-auth-sasl.h
+@@ -65,8 +65,8 @@ struct VncDisplaySASL {
+
+ void vnc_sasl_client_cleanup(VncState *vs);
+
+-long vnc_client_read_sasl(VncState *vs);
+-long vnc_client_write_sasl(VncState *vs);
++size_t vnc_client_read_sasl(VncState *vs);
++size_t vnc_client_write_sasl(VncState *vs);
+
+ void start_auth_sasl(VncState *vs);
+
+diff --git a/ui/vnc.c b/ui/vnc.c
+index 0a5e629d5d..665a143578 100644
+--- a/ui/vnc.c
++++ b/ui/vnc.c
+@@ -1272,7 +1272,7 @@ void vnc_disconnect_finish(VncState *vs)
+ g_free(vs);
+ }
+
+-ssize_t vnc_client_io_error(VncState *vs, ssize_t ret, Error **errp)
++size_t vnc_client_io_error(VncState *vs, ssize_t ret, Error **errp)
+ {
+ if (ret <= 0) {
+ if (ret == 0) {
+@@ -1315,9 +1315,9 @@ void vnc_client_error(VncState *vs)
+ *
+ * Returns the number of bytes written, which may be less than
+ * the requested 'datalen' if the socket would block. Returns
+- * -1 on error, and disconnects the client socket.
++ * 0 on I/O error, and disconnects the client socket.
+ */
+-ssize_t vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen)
++size_t vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen)
+ {
+ Error *err = NULL;
+ ssize_t ret;
+@@ -1335,13 +1335,13 @@ ssize_t vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen)
+ * will switch the FD poll() handler back to read monitoring.
+ *
+ * Returns the number of bytes written, which may be less than
+- * the buffered output data if the socket would block. Returns
+- * -1 on error, and disconnects the client socket.
++ * the buffered output data if the socket would block. Returns
++ * 0 on I/O error, and disconnects the client socket.
+ */
+-static ssize_t vnc_client_write_plain(VncState *vs)
++static size_t vnc_client_write_plain(VncState *vs)
+ {
+ size_t offset;
+- ssize_t ret;
++ size_t ret;
+
+ #ifdef CONFIG_VNC_SASL
+ VNC_DEBUG("Write Plain: Pending output %p size %zd offset %zd. Wait SSF %d\n",
+@@ -1442,9 +1442,9 @@ void vnc_read_when(VncState *vs, VncReadEvent *func, size_t expecting)
+ *
+ * Returns the number of bytes read, which may be less than
+ * the requested 'datalen' if the socket would block. Returns
+- * -1 on error, and disconnects the client socket.
++ * 0 on I/O error or EOF, and disconnects the client socket.
+ */
+-ssize_t vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen)
++size_t vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen)
+ {
+ ssize_t ret;
+ Error *err = NULL;
+@@ -1460,12 +1460,13 @@ ssize_t vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen)
+ * when not using any SASL SSF encryption layers. Will read as much
+ * data as possible without blocking.
+ *
+- * Returns the number of bytes read. Returns -1 on error, and
+- * disconnects the client socket.
++ * Returns the number of bytes read, which may be less than
++ * the requested 'datalen' if the socket would block. Returns
++ * 0 on I/O error or EOF, and disconnects the client socket.
+ */
+-static ssize_t vnc_client_read_plain(VncState *vs)
++static size_t vnc_client_read_plain(VncState *vs)
+ {
+- ssize_t ret;
++ size_t ret;
+ VNC_DEBUG("Read plain %p size %zd offset %zd\n",
+ vs->input.buffer, vs->input.capacity, vs->input.offset);
+ buffer_reserve(&vs->input, 4096);
+@@ -1491,7 +1492,7 @@ static void vnc_jobs_bh(void *opaque)
+ */
+ static int vnc_client_read(VncState *vs)
+ {
+- ssize_t ret;
++ size_t ret;
+
+ #ifdef CONFIG_VNC_SASL
+ if (vs->sasl.conn && vs->sasl.runSSF)
+diff --git a/ui/vnc.h b/ui/vnc.h
+index 3f4cd4d93d..0c33a5f7fe 100644
+--- a/ui/vnc.h
++++ b/ui/vnc.h
+@@ -524,8 +524,8 @@ gboolean vnc_client_io(QIOChannel *ioc,
+ GIOCondition condition,
+ void *opaque);
+
+-ssize_t vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen);
+-ssize_t vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen);
++size_t vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen);
++size_t vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen);
+
+ /* Protocol I/O functions */
+ void vnc_write(VncState *vs, const void *data, size_t len);
+@@ -544,7 +544,7 @@ uint32_t read_u32(uint8_t *data, size_t offset);
+
+ /* Protocol stage functions */
+ void vnc_client_error(VncState *vs);
+-ssize_t vnc_client_io_error(VncState *vs, ssize_t ret, Error **errp);
++size_t vnc_client_io_error(VncState *vs, ssize_t ret, Error **errp);
+
+ void start_client_init(VncState *vs);
+ void start_auth_vnc(VncState *vs);
+--
+2.11.0
diff --git a/meta/recipes-devtools/qemu/qemu_2.11.0.bb b/meta/recipes-devtools/qemu/qemu_2.11.0.bb
index 8306db2ce5..10760dc2db 100644
--- a/meta/recipes-devtools/qemu/qemu_2.11.0.bb
+++ b/meta/recipes-devtools/qemu/qemu_2.11.0.bb
@@ -22,6 +22,7 @@ SRC_URI = "http://wiki.qemu-project.org/download/${BP}.tar.bz2 \
file://apic-fixup-fallthrough-to-PIC.patch \
file://linux-user-Fix-webkitgtk-hangs-on-32-bit-x86-target.patch \
file://memfd.patch \
+ file://CVE-2017-15124.patch \
"
UPSTREAM_CHECK_REGEX = "qemu-(?P<pver>\d+\..*)\.tar"