From patchwork Mon Dec 18 19:12:26 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?q?Daniel_P=2E_Berrang=C3=A9?= X-Patchwork-Id: 850372 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=nongnu.org (client-ip=2001:4830:134:3::11; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=) Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3z0rdb2QNgz9sR8 for ; Tue, 19 Dec 2017 06:27:15 +1100 (AEDT) Received: from localhost ([::1]:33192 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1eR14D-0001wC-A7 for incoming@patchwork.ozlabs.org; Mon, 18 Dec 2017 14:27:13 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:50529) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1eR0qb-0006Wq-7U for qemu-devel@nongnu.org; Mon, 18 Dec 2017 14:13:11 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1eR0qZ-0005Tz-0y for qemu-devel@nongnu.org; Mon, 18 Dec 2017 14:13:09 -0500 Received: from mx1.redhat.com ([209.132.183.28]:50668) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1eR0qY-0005Sk-Mw for qemu-devel@nongnu.org; Mon, 18 Dec 2017 14:13:06 -0500 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.13]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id DC0E32D6A28 for ; Mon, 18 Dec 2017 19:13:05 +0000 (UTC) Received: from t460.redhat.com (unknown [10.33.36.45]) by smtp.corp.redhat.com (Postfix) with ESMTP id BA49E78401; Mon, 18 Dec 2017 19:13:04 +0000 (UTC) From: "Daniel P. Berrange" To: qemu-devel@nongnu.org Date: Mon, 18 Dec 2017 19:12:26 +0000 Message-Id: <20171218191228.31018-12-berrange@redhat.com> In-Reply-To: <20171218191228.31018-1-berrange@redhat.com> References: <20171218191228.31018-1-berrange@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.13 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.29]); Mon, 18 Dec 2017 19:13:05 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH v1 11/13] ui: place a hard cap on VNC server output buffer size X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: =?utf-8?q?Marc-Andr=C3=A9_Lureau?= , Gerd Hoffmann , P J P Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: "Qemu-devel" The previous patches fix problems with throttling of forced framebuffer updates and audio data capture that would cause the QEMU output buffer size to grow without bound. Those fixes are graceful in that once the client catches up with reading data from the server, everything continues operating normally. There is some data which the server sends to the client that is impractical to throttle. Specifically there are various pseudo framebuffer update encodings to inform the client of things like desktop resizes, pointer changes, audio playback start/stop, LED state and so on. These generally only involve sending a very small amount of data to the client, but a malicious guest might be able to do things that trigger these changes at a very high rate. Throttling them is not practical as missed or delayed events would cause broken behaviour for the client. This patch thus takes a more forceful approach of setting an absolute upper bound on the amount of data we permit to be present in the output buffer at any time. The previous patch set a threshold for throttling the output buffer by allowing an amount of data equivalent to one complete framebuffer update and one seconds worth of audio data. On top of this it allowed for one further forced framebuffer update to be queued. To be conservative, we thus take that throttling threshold and multiply it by 5 to form an absolute upper bound. If this bound is hit during vnc_write() we forceably disconnect the client, refusing to queue further data. This limit is high enough that it should never be hit unless a malicious client is trying to exploit the sever, or the network is completely saturated preventing any sending of data on the socket. This completes the fix for CVE-2017-15124 started in the previous patches. Signed-off-by: Daniel P. Berrange --- ui/vnc.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/ui/vnc.c b/ui/vnc.c index 4021c0118c..a4f0279cdc 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -1521,8 +1521,37 @@ gboolean vnc_client_io(QIOChannel *ioc G_GNUC_UNUSED, } +/* + * Scale factor to apply to vs->throttle_output_offset when checking for + * hard limit. Worst case normal usage could be x2, if we have a complete + * incremental update and complete forced update in the output buffer. + * So x3 should be good enough, but we pick x5 to be conservative and thus + * (hopefully) never trigger incorrectly. + */ +#define VNC_THROTTLE_OUTPUT_LIMIT_SCALE 5 + void vnc_write(VncState *vs, const void *data, size_t len) { + if (vs->disconnecting) { + return; + } + /* Protection against malicious client/guest to prevent our output + * buffer growing without bound if client stops reading data. This + * should rarely trigger, because we have earlier throttling code + * which stops issuing framebuffer updates and drops audio data + * if the throttle_output_offset value is exceeded. So we only reach + * this higher level if a huge number of pseudo-encodings get + * triggered while data can't be sent on the socket. + * + * NB throttle_output_offset can be zero during early protocol + * handshake, or from the job thread's VncState clone + */ + if (vs->throttle_output_offset != 0 && + vs->output.offset > (vs->throttle_output_offset * + VNC_THROTTLE_OUTPUT_LIMIT_SCALE)) { + vnc_disconnect_start(vs); + return; + } buffer_reserve(&vs->output, len); if (vs->ioc != NULL && buffer_empty(&vs->output)) {