Blame SOURCES/kvm-ui-fix-VNC-client-throttling-when-audio-capture-is-a.patch

4a2fec
From e961103621520003d54a8c7eac562c2b9e8436db Mon Sep 17 00:00:00 2001
4a2fec
From: "Daniel P. Berrange" <berrange@redhat.com>
4a2fec
Date: Mon, 5 Feb 2018 11:10:06 +0100
4a2fec
Subject: [PATCH 12/20] ui: fix VNC client throttling when audio capture is
4a2fec
 active
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-12-berrange@redhat.com>
4a2fec
Patchwork-id: 78883
4a2fec
O-Subject: [RHV-7.5 qemu-kvm-rhev PATCH v2 11/17] ui: fix VNC client throttling when audio capture is active
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 must be disabled if audio capture is
4a2fec
enabled, because when streaming audio the output buffer offset will rarely be
4a2fec
zero due to queued audio data, and so this would starve framebuffer updates.
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 enable audio capture, and simply never
4a2fec
read data back from the server. This can easily make QEMU's VNC server send
4a2fec
buffer consume 100MB of RAM per second, until the OOM killer starts reaping
4a2fec
processes (hopefully the rogue QEMU process, but it might pick others...).
4a2fec
4a2fec
To address this we make the throttling more intelligent, so we can throttle
4a2fec
when audio capture is active too. To determine how to throttle incremental
4a2fec
updates or audio data, we calculate a size threshold. Normally the threshold is
4a2fec
the approximate number of bytes associated with a single complete framebuffer
4a2fec
update. ie width * height * bytes per pixel. We'll send incremental updates
4a2fec
until we hit this threshold, at which point we'll stop sending updates until
4a2fec
data has been written to the wire, causing the output buffer offset to fall
4a2fec
back below the threshold.
4a2fec
4a2fec
If audio capture is enabled, we increase the size of the threshold to also
4a2fec
allow for upto 1 seconds worth of audio data samples. ie nchannels * bytes
4a2fec
per sample * frequency. This allows the output buffer to have a mixture of
4a2fec
incremental framebuffer updates and audio data queued, but once the threshold
4a2fec
is exceeded, audio data will be dropped and incremental updates will be
4a2fec
throttled.
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-10-berrange@redhat.com
4a2fec
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
4a2fec
(cherry picked from commit e2b72cb6e0443d90d7ab037858cb6834b6cca852)
4a2fec
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
4a2fec
---
4a2fec
 ui/vnc.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
4a2fec
 ui/vnc.h |  6 ++++++
4a2fec
 2 files changed, 70 insertions(+), 8 deletions(-)
4a2fec
4a2fec
diff --git a/ui/vnc.c b/ui/vnc.c
4a2fec
index 2b04050..e132b74 100644
4a2fec
--- a/ui/vnc.c
4a2fec
+++ b/ui/vnc.c
4a2fec
@@ -60,6 +60,7 @@ static QTAILQ_HEAD(, VncDisplay) vnc_displays =
4a2fec
 
4a2fec
 static int vnc_cursor_define(VncState *vs);
4a2fec
 static void vnc_release_modifiers(VncState *vs);
4a2fec
+static void vnc_update_throttle_offset(VncState *vs);
4a2fec
 
4a2fec
 static void vnc_set_share_mode(VncState *vs, VncShareMode mode)
4a2fec
 {
4a2fec
@@ -766,6 +767,7 @@ static void vnc_dpy_switch(DisplayChangeListener *dcl,
4a2fec
         vnc_set_area_dirty(vs->dirty, vd, 0, 0,
4a2fec
                            vnc_width(vd),
4a2fec
                            vnc_height(vd));
4a2fec
+        vnc_update_throttle_offset(vs);
4a2fec
     }
4a2fec
 }
4a2fec
 
4a2fec
@@ -961,16 +963,67 @@ static int find_and_clear_dirty_height(VncState *vs,
4a2fec
     return h;
4a2fec
 }
4a2fec
 
4a2fec
+/*
4a2fec
+ * Figure out how much pending data we should allow in the output
4a2fec
+ * buffer before we throttle incremental display updates, and/or
4a2fec
+ * drop audio samples.
4a2fec
+ *
4a2fec
+ * We allow for equiv of 1 full display's worth of FB updates,
4a2fec
+ * and 1 second of audio samples. If audio backlog was larger
4a2fec
+ * than that the client would already suffering awful audio
4a2fec
+ * glitches, so dropping samples is no worse really).
4a2fec
+ */
4a2fec
+static void vnc_update_throttle_offset(VncState *vs)
4a2fec
+{
4a2fec
+    size_t offset =
4a2fec
+        vs->client_width * vs->client_height * vs->client_pf.bytes_per_pixel;
4a2fec
+
4a2fec
+    if (vs->audio_cap) {
4a2fec
+        int freq = vs->as.freq;
4a2fec
+        /* We don't limit freq when reading settings from client, so
4a2fec
+         * it could be upto MAX_INT in size. 48khz is a sensible
4a2fec
+         * upper bound for trustworthy clients */
4a2fec
+        int bps;
4a2fec
+        if (freq > 48000) {
4a2fec
+            freq = 48000;
4a2fec
+        }
4a2fec
+        switch (vs->as.fmt) {
4a2fec
+        default:
4a2fec
+        case  AUD_FMT_U8:
4a2fec
+        case  AUD_FMT_S8:
4a2fec
+            bps = 1;
4a2fec
+            break;
4a2fec
+        case  AUD_FMT_U16:
4a2fec
+        case  AUD_FMT_S16:
4a2fec
+            bps = 2;
4a2fec
+            break;
4a2fec
+        case  AUD_FMT_U32:
4a2fec
+        case  AUD_FMT_S32:
4a2fec
+            bps = 4;
4a2fec
+            break;
4a2fec
+        }
4a2fec
+        offset += freq * bps * vs->as.nchannels;
4a2fec
+    }
4a2fec
+
4a2fec
+    /* Put a floor of 1MB on offset, so that if we have a large pending
4a2fec
+     * buffer and the display is resized to a small size & back again
4a2fec
+     * we don't suddenly apply a tiny send limit
4a2fec
+     */
4a2fec
+    offset = MAX(offset, 1024 * 1024);
4a2fec
+
4a2fec
+    vs->throttle_output_offset = offset;
4a2fec
+}
4a2fec
+
4a2fec
 static bool vnc_should_update(VncState *vs)
4a2fec
 {
4a2fec
     switch (vs->update) {
4a2fec
     case VNC_STATE_UPDATE_NONE:
4a2fec
         break;
4a2fec
     case VNC_STATE_UPDATE_INCREMENTAL:
4a2fec
-        /* Only allow incremental updates if the output buffer
4a2fec
-         * is empty, or if audio capture is enabled.
4a2fec
+        /* Only allow incremental updates if the pending send queue
4a2fec
+         * is less than the permitted threshold
4a2fec
          */
4a2fec
-        if (!vs->output.offset || vs->audio_cap) {
4a2fec
+        if (vs->output.offset < vs->throttle_output_offset) {
4a2fec
             return true;
4a2fec
         }
4a2fec
         break;
4a2fec
@@ -1084,11 +1137,13 @@ static void audio_capture(void *opaque, void *buf, int size)
4a2fec
     VncState *vs = opaque;
4a2fec
 
4a2fec
     vnc_lock_output(vs);
4a2fec
-    vnc_write_u8(vs, VNC_MSG_SERVER_QEMU);
4a2fec
-    vnc_write_u8(vs, VNC_MSG_SERVER_QEMU_AUDIO);
4a2fec
-    vnc_write_u16(vs, VNC_MSG_SERVER_QEMU_AUDIO_DATA);
4a2fec
-    vnc_write_u32(vs, size);
4a2fec
-    vnc_write(vs, buf, size);
4a2fec
+    if (vs->output.offset < vs->throttle_output_offset) {
4a2fec
+        vnc_write_u8(vs, VNC_MSG_SERVER_QEMU);
4a2fec
+        vnc_write_u8(vs, VNC_MSG_SERVER_QEMU_AUDIO);
4a2fec
+        vnc_write_u16(vs, VNC_MSG_SERVER_QEMU_AUDIO_DATA);
4a2fec
+        vnc_write_u32(vs, size);
4a2fec
+        vnc_write(vs, buf, size);
4a2fec
+    }
4a2fec
     vnc_unlock_output(vs);
4a2fec
     vnc_flush(vs);
4a2fec
 }
4a2fec
@@ -2288,6 +2343,7 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len)
4a2fec
         break;
4a2fec
     }
4a2fec
 
4a2fec
+    vnc_update_throttle_offset(vs);
4a2fec
     vnc_read_when(vs, protocol_client_msg, 1);
4a2fec
     return 0;
4a2fec
 }
4a2fec
diff --git a/ui/vnc.h b/ui/vnc.h
4a2fec
index b9d310e..8fe6959 100644
4a2fec
--- a/ui/vnc.h
4a2fec
+++ b/ui/vnc.h
4a2fec
@@ -298,6 +298,12 @@ struct VncState
4a2fec
 
4a2fec
     VncClientInfo *info;
4a2fec
 
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
+     * is calculating dynamically based on framebuffer size
4a2fec
+     * and audio sample settings in vnc_update_throttle_offset() */
4a2fec
+    size_t throttle_output_offset;
4a2fec
     Buffer output;
4a2fec
     Buffer input;
4a2fec
     /* current output mode information */
4a2fec
-- 
4a2fec
1.8.3.1
4a2fec