9ae3a8
From 52fe55e2bf9df408ebe127a670ee698642d3fcb4 Mon Sep 17 00:00:00 2001
9ae3a8
From: "Daniel P. Berrange" <berrange@redhat.com>
9ae3a8
Date: Thu, 8 Feb 2018 17:50:38 +0100
9ae3a8
Subject: [PATCH 24/27] ui: place a hard cap on VNC server output buffer size
9ae3a8
MIME-Version: 1.0
9ae3a8
Content-Type: text/plain; charset=UTF-8
9ae3a8
Content-Transfer-Encoding: 8bit
9ae3a8
9ae3a8
RH-Author: Daniel P. Berrange <berrange@redhat.com>
9ae3a8
Message-id: <20180208175041.5634-25-berrange@redhat.com>
9ae3a8
Patchwork-id: 78957
9ae3a8
O-Subject: [RHEL-7.5 qemu-kvm PATCH v1 24/27] ui: place a hard cap on VNC server output buffer size
9ae3a8
Bugzilla: 1527405
9ae3a8
RH-Acked-by: Laszlo Ersek <lersek@redhat.com>
9ae3a8
RH-Acked-by: Gerd Hoffmann <kraxel@redhat.com>
9ae3a8
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
9ae3a8
9ae3a8
From: "Daniel P. Berrange" <berrange@redhat.com>
9ae3a8
9ae3a8
The previous patches fix problems with throttling of forced framebuffer updates
9ae3a8
and audio data capture that would cause the QEMU output buffer size to grow
9ae3a8
without bound. Those fixes are graceful in that once the client catches up with
9ae3a8
reading data from the server, everything continues operating normally.
9ae3a8
9ae3a8
There is some data which the server sends to the client that is impractical to
9ae3a8
throttle. Specifically there are various pseudo framebuffer update encodings to
9ae3a8
inform the client of things like desktop resizes, pointer changes, audio
9ae3a8
playback start/stop, LED state and so on. These generally only involve sending
9ae3a8
a very small amount of data to the client, but a malicious guest might be able
9ae3a8
to do things that trigger these changes at a very high rate. Throttling them is
9ae3a8
not practical as missed or delayed events would cause broken behaviour for the
9ae3a8
client.
9ae3a8
9ae3a8
This patch thus takes a more forceful approach of setting an absolute upper
9ae3a8
bound on the amount of data we permit to be present in the output buffer at
9ae3a8
any time. The previous patch set a threshold for throttling the output buffer
9ae3a8
by allowing an amount of data equivalent to one complete framebuffer update and
9ae3a8
one seconds worth of audio data. On top of this it allowed for one further
9ae3a8
forced framebuffer update to be queued.
9ae3a8
9ae3a8
To be conservative, we thus take that throttling threshold and multiply it by
9ae3a8
5 to form an absolute upper bound. If this bound is hit during vnc_write() we
9ae3a8
forceably disconnect the client, refusing to queue further data. This limit is
9ae3a8
high enough that it should never be hit unless a malicious client is trying to
9ae3a8
exploit the sever, or the network is completely saturated preventing any sending
9ae3a8
of data on the socket.
9ae3a8
9ae3a8
This completes the fix for CVE-2017-15124 started in the previous patches.
9ae3a8
9ae3a8
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
9ae3a8
Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
9ae3a8
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
9ae3a8
Message-id: 20171218191228.31018-12-berrange@redhat.com
9ae3a8
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
9ae3a8
(cherry picked from commit f887cf165db20f405cb8805c716bd363aaadf815)
9ae3a8
9ae3a8
 Conflicts:
9ae3a8
	ui/vnc.c - context differences and no 'vs->disconnecting' flag.
9ae3a8
	Using share_mode as a better check for the disconnecting state
9ae3a8
	than csock == -1, because the worker thread calls vnc_write()
9ae3a8
	with a fake VncState that has csock == -1.
9ae3a8
9ae3a8
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
9ae3a8
---
9ae3a8
 ui/vnc.c | 29 +++++++++++++++++++++++++++++
9ae3a8
 1 file changed, 29 insertions(+)
9ae3a8
9ae3a8
diff --git a/ui/vnc.c b/ui/vnc.c
9ae3a8
index 96b6caf..61fbec2 100644
9ae3a8
--- a/ui/vnc.c
9ae3a8
+++ b/ui/vnc.c
9ae3a8
@@ -1460,8 +1460,37 @@ void vnc_client_read(void *opaque)
9ae3a8
     }
9ae3a8
 }
9ae3a8
 
9ae3a8
+/*
9ae3a8
+ * Scale factor to apply to vs->throttle_output_offset when checking for
9ae3a8
+ * hard limit. Worst case normal usage could be x2, if we have a complete
9ae3a8
+ * incremental update and complete forced update in the output buffer.
9ae3a8
+ * So x3 should be good enough, but we pick x5 to be conservative and thus
9ae3a8
+ * (hopefully) never trigger incorrectly.
9ae3a8
+ */
9ae3a8
+#define VNC_THROTTLE_OUTPUT_LIMIT_SCALE 5
9ae3a8
+
9ae3a8
 void vnc_write(VncState *vs, const void *data, size_t len)
9ae3a8
 {
9ae3a8
+    if (vs->share_mode == VNC_SHARE_MODE_DISCONNECTED) {
9ae3a8
+        return;
9ae3a8
+    }
9ae3a8
+    /* Protection against malicious client/guest to prevent our output
9ae3a8
+     * buffer growing without bound if client stops reading data. This
9ae3a8
+     * should rarely trigger, because we have earlier throttling code
9ae3a8
+     * which stops issuing framebuffer updates and drops audio data
9ae3a8
+     * if the throttle_output_offset value is exceeded. So we only reach
9ae3a8
+     * this higher level if a huge number of pseudo-encodings get
9ae3a8
+     * triggered while data can't be sent on the socket.
9ae3a8
+     *
9ae3a8
+     * NB throttle_output_offset can be zero during early protocol
9ae3a8
+     * handshake, or from the job thread's VncState clone
9ae3a8
+     */
9ae3a8
+    if (vs->throttle_output_offset != 0 &&
9ae3a8
+        vs->output.offset > (vs->throttle_output_offset *
9ae3a8
+                             VNC_THROTTLE_OUTPUT_LIMIT_SCALE)) {
9ae3a8
+        vnc_disconnect_start(vs);
9ae3a8
+        return;
9ae3a8
+    }
9ae3a8
     buffer_reserve(&vs->output, len);
9ae3a8
 
9ae3a8
     if (vs->csock != -1 && buffer_empty(&vs->output)) {
9ae3a8
-- 
9ae3a8
1.8.3.1
9ae3a8