[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-changelog] [qemu-xen master] ui: fix VNC client throttling when forced update is requested



commit f9e53c77ea3bfe92ddc8c755935e7521dd3ed08e
Author:     Daniel P. Berrange <berrange@xxxxxxxxxx>
AuthorDate: Mon Dec 18 19:12:25 2017 +0000
Commit:     Michael Roth <mdroth@xxxxxxxxxxxxxxxxxx>
CommitDate: Mon Feb 12 18:34:21 2018 -0600

    ui: fix VNC client throttling when forced update is requested
    
    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@xxxxxxxxxx>
      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@xxxxxxxxxx>
    Reviewed-by: Darren Kenny <darren.kenny@xxxxxxxxxx>
    Reviewed-by: Marc-André Lureau <marcandre.lureau@xxxxxxxxxx>
    Message-id: 20171218191228.31018-11-berrange@xxxxxxxxxx
    Signed-off-by: Gerd Hoffmann <kraxel@xxxxxxxxxx>
    (cherry picked from commit ada8d2e4369ea49677d8672ac81bce73eefd5b54)
    Signed-off-by: Michael Roth <mdroth@xxxxxxxxxxxxxxxxxx>
---
 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 761493b..8c1cdde 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 f786777..e326679 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 a2699f5..4021c011 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 8fe6959..3f4cd4d 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
--
generated by git-patchbot for /home/xen/git/qemu-xen.git#master

_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxxx
https://lists.xenproject.org/xen-changelog

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.