|
|
4a2fec |
From bf44407132cb0c39e59bb2f7f72bde9a7b085c70 Mon Sep 17 00:00:00 2001
|
|
|
4a2fec |
From: "Daniel P. Berrange" <berrange@redhat.com>
|
|
|
4a2fec |
Date: Mon, 5 Feb 2018 11:10:07 +0100
|
|
|
4a2fec |
Subject: [PATCH 13/20] ui: fix VNC client throttling when forced update is
|
|
|
4a2fec |
requested
|
|
|
4a2fec |
MIME-Version: 1.0
|
|
|
4a2fec |
Content-Type: text/plain; charset=UTF-8
|
|
|
4a2fec |
Content-Transfer-Encoding: 8bit
|
|
|
4a2fec |
|
|
|
4a2fec |
RH-Author: Daniel P. Berrange <berrange@redhat.com>
|
|
|
4a2fec |
Message-id: <20180205111012.6210-13-berrange@redhat.com>
|
|
|
4a2fec |
Patchwork-id: 78882
|
|
|
4a2fec |
O-Subject: [RHV-7.5 qemu-kvm-rhev PATCH v2 12/17] ui: fix VNC client throttling when forced update is requested
|
|
|
4a2fec |
Bugzilla: 1527404
|
|
|
4a2fec |
RH-Acked-by: Gerd Hoffmann <kraxel@redhat.com>
|
|
|
4a2fec |
RH-Acked-by: Laszlo Ersek <lersek@redhat.com>
|
|
|
4a2fec |
RH-Acked-by: Thomas Huth <thuth@redhat.com>
|
|
|
4a2fec |
|
|
|
4a2fec |
From: "Daniel P. Berrange" <berrange@redhat.com>
|
|
|
4a2fec |
|
|
|
4a2fec |
The VNC server must throttle data sent to the client to prevent the 'output'
|
|
|
4a2fec |
buffer size growing without bound, if the client stops reading data off the
|
|
|
4a2fec |
socket (either maliciously or due to stalled/slow network connection).
|
|
|
4a2fec |
|
|
|
4a2fec |
The current throttling is very crude because it simply checks whether the
|
|
|
4a2fec |
output buffer offset is zero. This check is disabled if the client has requested
|
|
|
4a2fec |
a forced update, because we want to send these as soon as possible.
|
|
|
4a2fec |
|
|
|
4a2fec |
As a result, the VNC client can cause QEMU to allocate arbitrary amounts of RAM.
|
|
|
4a2fec |
They can first start something in the guest that triggers lots of framebuffer
|
|
|
4a2fec |
updates eg play a youtube video. Then repeatedly send full framebuffer update
|
|
|
4a2fec |
requests, but never read data back from the server. This can easily make QEMU's
|
|
|
4a2fec |
VNC server send buffer consume 100MB of RAM per second, until the OOM killer
|
|
|
4a2fec |
starts reaping processes (hopefully the rogue QEMU process, but it might pick
|
|
|
4a2fec |
others...).
|
|
|
4a2fec |
|
|
|
4a2fec |
To address this we make the throttling more intelligent, so we can throttle
|
|
|
4a2fec |
full updates. When we get a forced update request, we keep track of exactly how
|
|
|
4a2fec |
much data we put on the output buffer. We will not process a subsequent forced
|
|
|
4a2fec |
update request until this data has been fully sent on the wire. We always allow
|
|
|
4a2fec |
one forced update request to be in flight, regardless of what data is queued
|
|
|
4a2fec |
for incremental updates or audio data. The slight complication is that we do
|
|
|
4a2fec |
not initially know how much data an update will send, as this is done in the
|
|
|
4a2fec |
background by the VNC job thread. So we must track the fact that the job thread
|
|
|
4a2fec |
has an update pending, and not process any further updates until this job is
|
|
|
4a2fec |
has been completed & put data on the output buffer.
|
|
|
4a2fec |
|
|
|
4a2fec |
This unbounded memory growth affects all VNC server configurations supported by
|
|
|
4a2fec |
QEMU, with no workaround possible. The mitigating factor is that it can only be
|
|
|
4a2fec |
triggered by a client that has authenticated with the VNC server, and who is
|
|
|
4a2fec |
able to trigger a large quantity of framebuffer updates or audio samples from
|
|
|
4a2fec |
the guest OS. Mostly they'll just succeed in getting the OOM killer to kill
|
|
|
4a2fec |
their own QEMU process, but its possible other processes can get taken out as
|
|
|
4a2fec |
collateral damage.
|
|
|
4a2fec |
|
|
|
4a2fec |
This is a more general variant of the similar unbounded memory usage flaw in
|
|
|
4a2fec |
the websockets server, that was previously assigned CVE-2017-15268, and fixed
|
|
|
4a2fec |
in 2.11 by:
|
|
|
4a2fec |
|
|
|
4a2fec |
commit a7b20a8efa28e5f22c26c06cd06c2f12bc863493
|
|
|
4a2fec |
Author: Daniel P. Berrange <berrange@redhat.com>
|
|
|
4a2fec |
Date: Mon Oct 9 14:43:42 2017 +0100
|
|
|
4a2fec |
|
|
|
4a2fec |
io: monitor encoutput buffer size from websocket GSource
|
|
|
4a2fec |
|
|
|
4a2fec |
This new general memory usage flaw has been assigned CVE-2017-15124, and is
|
|
|
4a2fec |
partially fixed by this patch.
|
|
|
4a2fec |
|
|
|
4a2fec |
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
|
|
|
4a2fec |
Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
|
|
|
4a2fec |
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
|
|
|
4a2fec |
Message-id: 20171218191228.31018-11-berrange@redhat.com
|
|
|
4a2fec |
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
|
|
|
4a2fec |
(cherry picked from commit ada8d2e4369ea49677d8672ac81bce73eefd5b54)
|
|
|
4a2fec |
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
|
|
|
4a2fec |
---
|
|
|
4a2fec |
ui/vnc-auth-sasl.c | 5 +++++
|
|
|
4a2fec |
ui/vnc-jobs.c | 5 +++++
|
|
|
4a2fec |
ui/vnc.c | 28 ++++++++++++++++++++++++----
|
|
|
4a2fec |
ui/vnc.h | 7 +++++++
|
|
|
4a2fec |
4 files changed, 41 insertions(+), 4 deletions(-)
|
|
|
4a2fec |
|
|
|
4a2fec |
diff --git a/ui/vnc-auth-sasl.c b/ui/vnc-auth-sasl.c
|
|
|
4a2fec |
index 761493b..8c1cdde 100644
|
|
|
4a2fec |
--- a/ui/vnc-auth-sasl.c
|
|
|
4a2fec |
+++ b/ui/vnc-auth-sasl.c
|
|
|
4a2fec |
@@ -79,6 +79,11 @@ long vnc_client_write_sasl(VncState *vs)
|
|
|
4a2fec |
|
|
|
4a2fec |
vs->sasl.encodedOffset += ret;
|
|
|
4a2fec |
if (vs->sasl.encodedOffset == vs->sasl.encodedLength) {
|
|
|
4a2fec |
+ if (vs->sasl.encodedRawLength >= vs->force_update_offset) {
|
|
|
4a2fec |
+ vs->force_update_offset = 0;
|
|
|
4a2fec |
+ } else {
|
|
|
4a2fec |
+ vs->force_update_offset -= vs->sasl.encodedRawLength;
|
|
|
4a2fec |
+ }
|
|
|
4a2fec |
vs->output.offset -= vs->sasl.encodedRawLength;
|
|
|
4a2fec |
vs->sasl.encoded = NULL;
|
|
|
4a2fec |
vs->sasl.encodedOffset = vs->sasl.encodedLength = 0;
|
|
|
4a2fec |
diff --git a/ui/vnc-jobs.c b/ui/vnc-jobs.c
|
|
|
4a2fec |
index f786777..e326679 100644
|
|
|
4a2fec |
--- a/ui/vnc-jobs.c
|
|
|
4a2fec |
+++ b/ui/vnc-jobs.c
|
|
|
4a2fec |
@@ -152,6 +152,11 @@ void vnc_jobs_consume_buffer(VncState *vs)
|
|
|
4a2fec |
vs->ioc, G_IO_IN | G_IO_OUT, vnc_client_io, vs, NULL);
|
|
|
4a2fec |
}
|
|
|
4a2fec |
buffer_move(&vs->output, &vs->jobs_buffer);
|
|
|
4a2fec |
+
|
|
|
4a2fec |
+ if (vs->job_update == VNC_STATE_UPDATE_FORCE) {
|
|
|
4a2fec |
+ vs->force_update_offset = vs->output.offset;
|
|
|
4a2fec |
+ }
|
|
|
4a2fec |
+ vs->job_update = VNC_STATE_UPDATE_NONE;
|
|
|
4a2fec |
}
|
|
|
4a2fec |
flush = vs->ioc != NULL && vs->abort != true;
|
|
|
4a2fec |
vnc_unlock_output(vs);
|
|
|
4a2fec |
diff --git a/ui/vnc.c b/ui/vnc.c
|
|
|
4a2fec |
index e132b74..7f2210f 100644
|
|
|
4a2fec |
--- a/ui/vnc.c
|
|
|
4a2fec |
+++ b/ui/vnc.c
|
|
|
4a2fec |
@@ -1021,14 +1021,28 @@ static bool vnc_should_update(VncState *vs)
|
|
|
4a2fec |
break;
|
|
|
4a2fec |
case VNC_STATE_UPDATE_INCREMENTAL:
|
|
|
4a2fec |
/* Only allow incremental updates if the pending send queue
|
|
|
4a2fec |
- * is less than the permitted threshold
|
|
|
4a2fec |
+ * is less than the permitted threshold, and the job worker
|
|
|
4a2fec |
+ * is completely idle.
|
|
|
4a2fec |
*/
|
|
|
4a2fec |
- if (vs->output.offset < vs->throttle_output_offset) {
|
|
|
4a2fec |
+ if (vs->output.offset < vs->throttle_output_offset &&
|
|
|
4a2fec |
+ vs->job_update == VNC_STATE_UPDATE_NONE) {
|
|
|
4a2fec |
return true;
|
|
|
4a2fec |
}
|
|
|
4a2fec |
break;
|
|
|
4a2fec |
case VNC_STATE_UPDATE_FORCE:
|
|
|
4a2fec |
- return true;
|
|
|
4a2fec |
+ /* Only allow forced updates if the pending send queue
|
|
|
4a2fec |
+ * does not contain a previous forced update, and the
|
|
|
4a2fec |
+ * job worker is completely idle.
|
|
|
4a2fec |
+ *
|
|
|
4a2fec |
+ * Note this means we'll queue a forced update, even if
|
|
|
4a2fec |
+ * the output buffer size is otherwise over the throttle
|
|
|
4a2fec |
+ * output limit.
|
|
|
4a2fec |
+ */
|
|
|
4a2fec |
+ if (vs->force_update_offset == 0 &&
|
|
|
4a2fec |
+ vs->job_update == VNC_STATE_UPDATE_NONE) {
|
|
|
4a2fec |
+ return true;
|
|
|
4a2fec |
+ }
|
|
|
4a2fec |
+ break;
|
|
|
4a2fec |
}
|
|
|
4a2fec |
return false;
|
|
|
4a2fec |
}
|
|
|
4a2fec |
@@ -1096,8 +1110,9 @@ static int vnc_update_client(VncState *vs, int has_dirty)
|
|
|
4a2fec |
}
|
|
|
4a2fec |
}
|
|
|
4a2fec |
|
|
|
4a2fec |
- vnc_job_push(job);
|
|
|
4a2fec |
+ vs->job_update = vs->update;
|
|
|
4a2fec |
vs->update = VNC_STATE_UPDATE_NONE;
|
|
|
4a2fec |
+ vnc_job_push(job);
|
|
|
4a2fec |
vs->has_dirty = 0;
|
|
|
4a2fec |
return n;
|
|
|
4a2fec |
}
|
|
|
4a2fec |
@@ -1332,6 +1347,11 @@ static ssize_t vnc_client_write_plain(VncState *vs)
|
|
|
4a2fec |
if (!ret)
|
|
|
4a2fec |
return 0;
|
|
|
4a2fec |
|
|
|
4a2fec |
+ if (ret >= vs->force_update_offset) {
|
|
|
4a2fec |
+ vs->force_update_offset = 0;
|
|
|
4a2fec |
+ } else {
|
|
|
4a2fec |
+ vs->force_update_offset -= ret;
|
|
|
4a2fec |
+ }
|
|
|
4a2fec |
buffer_advance(&vs->output, ret);
|
|
|
4a2fec |
|
|
|
4a2fec |
if (vs->output.offset == 0) {
|
|
|
4a2fec |
diff --git a/ui/vnc.h b/ui/vnc.h
|
|
|
4a2fec |
index 8fe6959..3f4cd4d 100644
|
|
|
4a2fec |
--- a/ui/vnc.h
|
|
|
4a2fec |
+++ b/ui/vnc.h
|
|
|
4a2fec |
@@ -271,6 +271,7 @@ struct VncState
|
|
|
4a2fec |
|
|
|
4a2fec |
VncDisplay *vd;
|
|
|
4a2fec |
VncStateUpdate update; /* Most recent pending request from client */
|
|
|
4a2fec |
+ VncStateUpdate job_update; /* Currently processed by job thread */
|
|
|
4a2fec |
int has_dirty;
|
|
|
4a2fec |
uint32_t features;
|
|
|
4a2fec |
int absolute;
|
|
|
4a2fec |
@@ -298,6 +299,12 @@ struct VncState
|
|
|
4a2fec |
|
|
|
4a2fec |
VncClientInfo *info;
|
|
|
4a2fec |
|
|
|
4a2fec |
+ /* Job thread bottom half has put data for a forced update
|
|
|
4a2fec |
+ * into the output buffer. This offset points to the end of
|
|
|
4a2fec |
+ * the update data in the output buffer. This lets us determine
|
|
|
4a2fec |
+ * when a force update is fully sent to the client, allowing
|
|
|
4a2fec |
+ * us to process further forced updates. */
|
|
|
4a2fec |
+ size_t force_update_offset;
|
|
|
4a2fec |
/* We allow multiple incremental updates or audio capture
|
|
|
4a2fec |
* samples to be queued in output buffer, provided the
|
|
|
4a2fec |
* buffer size doesn't exceed this threshold. The value
|
|
|
4a2fec |
--
|
|
|
4a2fec |
1.8.3.1
|
|
|
4a2fec |
|