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

9bac43
From e961103621520003d54a8c7eac562c2b9e8436db Mon Sep 17 00:00:00 2001
9bac43
From: "Daniel P. Berrange" <berrange@redhat.com>
9bac43
Date: Mon, 5 Feb 2018 11:10:06 +0100
9bac43
Subject: [PATCH 12/20] ui: fix VNC client throttling when audio capture is
9bac43
 active
9bac43
MIME-Version: 1.0
9bac43
Content-Type: text/plain; charset=UTF-8
9bac43
Content-Transfer-Encoding: 8bit
9bac43
9bac43
RH-Author: Daniel P. Berrange <berrange@redhat.com>
9bac43
Message-id: <20180205111012.6210-12-berrange@redhat.com>
9bac43
Patchwork-id: 78883
9bac43
O-Subject: [RHV-7.5 qemu-kvm-rhev PATCH v2 11/17] ui: fix VNC client throttling when audio capture is active
9bac43
Bugzilla: 1527404
9bac43
RH-Acked-by: Gerd Hoffmann <kraxel@redhat.com>
9bac43
RH-Acked-by: Laszlo Ersek <lersek@redhat.com>
9bac43
RH-Acked-by: Thomas Huth <thuth@redhat.com>
9bac43
9bac43
From: "Daniel P. Berrange" <berrange@redhat.com>
9bac43
9bac43
The VNC server must throttle data sent to the client to prevent the 'output'
9bac43
buffer size growing without bound, if the client stops reading data off the
9bac43
socket (either maliciously or due to stalled/slow network connection).
9bac43
9bac43
The current throttling is very crude because it simply checks whether the
9bac43
output buffer offset is zero. This check must be disabled if audio capture is
9bac43
enabled, because when streaming audio the output buffer offset will rarely be
9bac43
zero due to queued audio data, and so this would starve framebuffer updates.
9bac43
9bac43
As a result, the VNC client can cause QEMU to allocate arbitrary amounts of RAM.
9bac43
They can first start something in the guest that triggers lots of framebuffer
9bac43
updates eg play a youtube video. Then enable audio capture, and simply never
9bac43
read data back from the server. This can easily make QEMU's VNC server send
9bac43
buffer consume 100MB of RAM per second, until the OOM killer starts reaping
9bac43
processes (hopefully the rogue QEMU process, but it might pick others...).
9bac43
9bac43
To address this we make the throttling more intelligent, so we can throttle
9bac43
when audio capture is active too. To determine how to throttle incremental
9bac43
updates or audio data, we calculate a size threshold. Normally the threshold is
9bac43
the approximate number of bytes associated with a single complete framebuffer
9bac43
update. ie width * height * bytes per pixel. We'll send incremental updates
9bac43
until we hit this threshold, at which point we'll stop sending updates until
9bac43
data has been written to the wire, causing the output buffer offset to fall
9bac43
back below the threshold.
9bac43
9bac43
If audio capture is enabled, we increase the size of the threshold to also
9bac43
allow for upto 1 seconds worth of audio data samples. ie nchannels * bytes
9bac43
per sample * frequency. This allows the output buffer to have a mixture of
9bac43
incremental framebuffer updates and audio data queued, but once the threshold
9bac43
is exceeded, audio data will be dropped and incremental updates will be
9bac43
throttled.
9bac43
9bac43
This unbounded memory growth affects all VNC server configurations supported by
9bac43
QEMU, with no workaround possible. The mitigating factor is that it can only be
9bac43
triggered by a client that has authenticated with the VNC server, and who is
9bac43
able to trigger a large quantity of framebuffer updates or audio samples from
9bac43
the guest OS. Mostly they'll just succeed in getting the OOM killer to kill
9bac43
their own QEMU process, but its possible other processes can get taken out as
9bac43
collateral damage.
9bac43
9bac43
This is a more general variant of the similar unbounded memory usage flaw in
9bac43
the websockets server, that was previously assigned CVE-2017-15268, and fixed
9bac43
in 2.11 by:
9bac43
9bac43
  commit a7b20a8efa28e5f22c26c06cd06c2f12bc863493
9bac43
  Author: Daniel P. Berrange <berrange@redhat.com>
9bac43
  Date:   Mon Oct 9 14:43:42 2017 +0100
9bac43
9bac43
    io: monitor encoutput buffer size from websocket GSource
9bac43
9bac43
This new general memory usage flaw has been assigned CVE-2017-15124, and is
9bac43
partially fixed by this patch.
9bac43
9bac43
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
9bac43
Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
9bac43
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
9bac43
Message-id: 20171218191228.31018-10-berrange@redhat.com
9bac43
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
9bac43
(cherry picked from commit e2b72cb6e0443d90d7ab037858cb6834b6cca852)
9bac43
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
9bac43
---
9bac43
 ui/vnc.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
9bac43
 ui/vnc.h |  6 ++++++
9bac43
 2 files changed, 70 insertions(+), 8 deletions(-)
9bac43
9bac43
diff --git a/ui/vnc.c b/ui/vnc.c
9bac43
index 2b04050..e132b74 100644
9bac43
--- a/ui/vnc.c
9bac43
+++ b/ui/vnc.c
9bac43
@@ -60,6 +60,7 @@ static QTAILQ_HEAD(, VncDisplay) vnc_displays =
9bac43
 
9bac43
 static int vnc_cursor_define(VncState *vs);
9bac43
 static void vnc_release_modifiers(VncState *vs);
9bac43
+static void vnc_update_throttle_offset(VncState *vs);
9bac43
 
9bac43
 static void vnc_set_share_mode(VncState *vs, VncShareMode mode)
9bac43
 {
9bac43
@@ -766,6 +767,7 @@ static void vnc_dpy_switch(DisplayChangeListener *dcl,
9bac43
         vnc_set_area_dirty(vs->dirty, vd, 0, 0,
9bac43
                            vnc_width(vd),
9bac43
                            vnc_height(vd));
9bac43
+        vnc_update_throttle_offset(vs);
9bac43
     }
9bac43
 }
9bac43
 
9bac43
@@ -961,16 +963,67 @@ static int find_and_clear_dirty_height(VncState *vs,
9bac43
     return h;
9bac43
 }
9bac43
 
9bac43
+/*
9bac43
+ * Figure out how much pending data we should allow in the output
9bac43
+ * buffer before we throttle incremental display updates, and/or
9bac43
+ * drop audio samples.
9bac43
+ *
9bac43
+ * We allow for equiv of 1 full display's worth of FB updates,
9bac43
+ * and 1 second of audio samples. If audio backlog was larger
9bac43
+ * than that the client would already suffering awful audio
9bac43
+ * glitches, so dropping samples is no worse really).
9bac43
+ */
9bac43
+static void vnc_update_throttle_offset(VncState *vs)
9bac43
+{
9bac43
+    size_t offset =
9bac43
+        vs->client_width * vs->client_height * vs->client_pf.bytes_per_pixel;
9bac43
+
9bac43
+    if (vs->audio_cap) {
9bac43
+        int freq = vs->as.freq;
9bac43
+        /* We don't limit freq when reading settings from client, so
9bac43
+         * it could be upto MAX_INT in size. 48khz is a sensible
9bac43
+         * upper bound for trustworthy clients */
9bac43
+        int bps;
9bac43
+        if (freq > 48000) {
9bac43
+            freq = 48000;
9bac43
+        }
9bac43
+        switch (vs->as.fmt) {
9bac43
+        default:
9bac43
+        case  AUD_FMT_U8:
9bac43
+        case  AUD_FMT_S8:
9bac43
+            bps = 1;
9bac43
+            break;
9bac43
+        case  AUD_FMT_U16:
9bac43
+        case  AUD_FMT_S16:
9bac43
+            bps = 2;
9bac43
+            break;
9bac43
+        case  AUD_FMT_U32:
9bac43
+        case  AUD_FMT_S32:
9bac43
+            bps = 4;
9bac43
+            break;
9bac43
+        }
9bac43
+        offset += freq * bps * vs->as.nchannels;
9bac43
+    }
9bac43
+
9bac43
+    /* Put a floor of 1MB on offset, so that if we have a large pending
9bac43
+     * buffer and the display is resized to a small size & back again
9bac43
+     * we don't suddenly apply a tiny send limit
9bac43
+     */
9bac43
+    offset = MAX(offset, 1024 * 1024);
9bac43
+
9bac43
+    vs->throttle_output_offset = offset;
9bac43
+}
9bac43
+
9bac43
 static bool vnc_should_update(VncState *vs)
9bac43
 {
9bac43
     switch (vs->update) {
9bac43
     case VNC_STATE_UPDATE_NONE:
9bac43
         break;
9bac43
     case VNC_STATE_UPDATE_INCREMENTAL:
9bac43
-        /* Only allow incremental updates if the output buffer
9bac43
-         * is empty, or if audio capture is enabled.
9bac43
+        /* Only allow incremental updates if the pending send queue
9bac43
+         * is less than the permitted threshold
9bac43
          */
9bac43
-        if (!vs->output.offset || vs->audio_cap) {
9bac43
+        if (vs->output.offset < vs->throttle_output_offset) {
9bac43
             return true;
9bac43
         }
9bac43
         break;
9bac43
@@ -1084,11 +1137,13 @@ static void audio_capture(void *opaque, void *buf, int size)
9bac43
     VncState *vs = opaque;
9bac43
 
9bac43
     vnc_lock_output(vs);
9bac43
-    vnc_write_u8(vs, VNC_MSG_SERVER_QEMU);
9bac43
-    vnc_write_u8(vs, VNC_MSG_SERVER_QEMU_AUDIO);
9bac43
-    vnc_write_u16(vs, VNC_MSG_SERVER_QEMU_AUDIO_DATA);
9bac43
-    vnc_write_u32(vs, size);
9bac43
-    vnc_write(vs, buf, size);
9bac43
+    if (vs->output.offset < vs->throttle_output_offset) {
9bac43
+        vnc_write_u8(vs, VNC_MSG_SERVER_QEMU);
9bac43
+        vnc_write_u8(vs, VNC_MSG_SERVER_QEMU_AUDIO);
9bac43
+        vnc_write_u16(vs, VNC_MSG_SERVER_QEMU_AUDIO_DATA);
9bac43
+        vnc_write_u32(vs, size);
9bac43
+        vnc_write(vs, buf, size);
9bac43
+    }
9bac43
     vnc_unlock_output(vs);
9bac43
     vnc_flush(vs);
9bac43
 }
9bac43
@@ -2288,6 +2343,7 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len)
9bac43
         break;
9bac43
     }
9bac43
 
9bac43
+    vnc_update_throttle_offset(vs);
9bac43
     vnc_read_when(vs, protocol_client_msg, 1);
9bac43
     return 0;
9bac43
 }
9bac43
diff --git a/ui/vnc.h b/ui/vnc.h
9bac43
index b9d310e..8fe6959 100644
9bac43
--- a/ui/vnc.h
9bac43
+++ b/ui/vnc.h
9bac43
@@ -298,6 +298,12 @@ struct VncState
9bac43
 
9bac43
     VncClientInfo *info;
9bac43
 
9bac43
+    /* We allow multiple incremental updates or audio capture
9bac43
+     * samples to be queued in output buffer, provided the
9bac43
+     * buffer size doesn't exceed this threshold. The value
9bac43
+     * is calculating dynamically based on framebuffer size
9bac43
+     * and audio sample settings in vnc_update_throttle_offset() */
9bac43
+    size_t throttle_output_offset;
9bac43
     Buffer output;
9bac43
     Buffer input;
9bac43
     /* current output mode information */
9bac43
-- 
9bac43
1.8.3.1
9bac43