summary refs log tree commit diff
path: root/gnu/packages/patches/qemu-CVE-2017-9524.patch
diff options
context:
space:
mode:
authorLeo Famulari <leo@famulari.name>2017-06-23 14:22:36 -0400
committerLeo Famulari <leo@famulari.name>2017-06-23 16:54:36 -0400
commitc57b56722f6c167c5a285f47802047de85a356ae (patch)
tree553746ecc35c7ee368568a5e59b66f94322ecfd1 /gnu/packages/patches/qemu-CVE-2017-9524.patch
parentab126c170a42c1a6727e16d7d4ed6be1d34a89f3 (diff)
downloadguix-c57b56722f6c167c5a285f47802047de85a356ae.tar.gz
gnu: qemu: Fix CVE-2017-9524.
* gnu/packages/patches/qemu-CVE-2017-9524.patch: New file.
* gnu/local.mk (dist_patch_DATA): Add it.
* gnu/packages/qemu.scm (qemu)[source]: Use it.
Diffstat (limited to 'gnu/packages/patches/qemu-CVE-2017-9524.patch')
-rw-r--r--gnu/packages/patches/qemu-CVE-2017-9524.patch287
1 files changed, 287 insertions, 0 deletions
diff --git a/gnu/packages/patches/qemu-CVE-2017-9524.patch b/gnu/packages/patches/qemu-CVE-2017-9524.patch
new file mode 100644
index 0000000000..57160055e3
--- /dev/null
+++ b/gnu/packages/patches/qemu-CVE-2017-9524.patch
@@ -0,0 +1,287 @@
+Fix CVE-2017-9524:
+
+https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-9524
+http://seclists.org/oss-sec/2017/q2/454
+
+Patches copied from upstream source repository:
+
+http://git.qemu.org/?p=qemu.git;a=commitdiff;h=df8ad9f128c15aa0a0ebc7b24e9a22c9775b67af
+http://git.qemu.org/?p=qemu.git;a=commitdiff;h=0c9390d978cbf61e8f16c9f580fa96b305c43568
+
+From df8ad9f128c15aa0a0ebc7b24e9a22c9775b67af Mon Sep 17 00:00:00 2001
+From: Eric Blake <eblake@redhat.com>
+Date: Fri, 26 May 2017 22:04:21 -0500
+Subject: [PATCH] nbd: Fully initialize client in case of failed negotiation
+
+If a non-NBD client connects to qemu-nbd, we would end up with
+a SIGSEGV in nbd_client_put() because we were trying to
+unregister the client's association to the export, even though
+we skipped inserting the client into that list.  Easy trigger
+in two terminals:
+
+$ qemu-nbd -p 30001 --format=raw file
+$ nmap 127.0.0.1 -p 30001
+
+nmap claims that it thinks it connected to a pago-services1
+server (which probably means nmap could be updated to learn the
+NBD protocol and give a more accurate diagnosis of the open
+port - but that's not our problem), then terminates immediately,
+so our call to nbd_negotiate() fails.  The fix is to reorder
+nbd_co_client_start() to ensure that all initialization occurs
+before we ever try talking to a client in nbd_negotiate(), so
+that the teardown sequence on negotiation failure doesn't fault
+while dereferencing a half-initialized object.
+
+While debugging this, I also noticed that nbd_update_server_watch()
+called by nbd_client_closed() was still adding a channel to accept
+the next client, even when the state was no longer RUNNING.  That
+is fixed by making nbd_can_accept() pay attention to the current
+state.
+
+Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1451614
+
+Signed-off-by: Eric Blake <eblake@redhat.com>
+Message-Id: <20170527030421.28366-1-eblake@redhat.com>
+Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
+---
+ nbd/server.c | 8 +++-----
+ qemu-nbd.c   | 2 +-
+ 2 files changed, 4 insertions(+), 6 deletions(-)
+
+diff --git a/nbd/server.c b/nbd/server.c
+index ee59e5d234..49b55f6ede 100644
+--- a/nbd/server.c
++++ b/nbd/server.c
+@@ -1358,16 +1358,14 @@ static coroutine_fn void nbd_co_client_start(void *opaque)
+ 
+     if (exp) {
+         nbd_export_get(exp);
++        QTAILQ_INSERT_TAIL(&exp->clients, client, next);
+     }
++    qemu_co_mutex_init(&client->send_lock);
++
+     if (nbd_negotiate(data)) {
+         client_close(client);
+         goto out;
+     }
+-    qemu_co_mutex_init(&client->send_lock);
+-
+-    if (exp) {
+-        QTAILQ_INSERT_TAIL(&exp->clients, client, next);
+-    }
+ 
+     nbd_client_receive_next_request(client);
+ 
+diff --git a/qemu-nbd.c b/qemu-nbd.c
+index f60842fd86..651f85ecc1 100644
+--- a/qemu-nbd.c
++++ b/qemu-nbd.c
+@@ -325,7 +325,7 @@ out:
+ 
+ static int nbd_can_accept(void)
+ {
+-    return nb_fds < shared;
++    return state == RUNNING && nb_fds < shared;
+ }
+ 
+ static void nbd_export_closed(NBDExport *exp)
+-- 
+2.13.1
+
+From 0c9390d978cbf61e8f16c9f580fa96b305c43568 Mon Sep 17 00:00:00 2001
+From: Eric Blake <eblake@redhat.com>
+Date: Thu, 8 Jun 2017 17:26:17 -0500
+Subject: [PATCH] nbd: Fix regression on resiliency to port scan
+
+Back in qemu 2.5, qemu-nbd was immune to port probes (a transient
+server would not quit, regardless of how many probe connections
+came and went, until a connection actually negotiated).  But we
+broke that in commit ee7d7aa when removing the return value to
+nbd_client_new(), although that patch also introduced a bug causing
+an assertion failure on a client that fails negotiation.  We then
+made it worse during refactoring in commit 1a6245a (a segfault
+before we could even assert); the (masked) assertion was cleaned
+up in d3780c2 (still in 2.6), and just recently we finally fixed
+the segfault ("nbd: Fully intialize client in case of failed
+negotiation").  But that still means that ever since we added
+TLS support to qemu-nbd, we have been vulnerable to an ill-timed
+port-scan being able to cause a denial of service by taking down
+qemu-nbd before a real client has a chance to connect.
+
+Since negotiation is now handled asynchronously via coroutines,
+we no longer have a synchronous point of return by re-adding a
+return value to nbd_client_new().  So this patch instead wires
+things up to pass the negotiation status through the close_fn
+callback function.
+
+Simple test across two terminals:
+$ qemu-nbd -f raw -p 30001 file
+$ nmap 127.0.0.1 -p 30001 && \
+  qemu-io -c 'r 0 512' -f raw nbd://localhost:30001
+
+Note that this patch does not change what constitutes successful
+negotiation (thus, a client must enter transmission phase before
+that client can be considered as a reason to terminate the server
+when the connection ends).  Perhaps we may want to tweak things
+in a later patch to also treat a client that uses NBD_OPT_ABORT
+as being a 'successful' negotiation (the client correctly talked
+the NBD protocol, and informed us it was not going to use our
+export after all), but that's a discussion for another day.
+
+Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1451614
+
+Signed-off-by: Eric Blake <eblake@redhat.com>
+Message-Id: <20170608222617.20376-1-eblake@redhat.com>
+Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
+---
+ blockdev-nbd.c      |  6 +++++-
+ include/block/nbd.h |  2 +-
+ nbd/server.c        | 24 +++++++++++++++---------
+ qemu-nbd.c          |  4 ++--
+ 4 files changed, 23 insertions(+), 13 deletions(-)
+
+diff --git a/blockdev-nbd.c b/blockdev-nbd.c
+index dd0860f4a6..28f551a7b0 100644
+--- a/blockdev-nbd.c
++++ b/blockdev-nbd.c
+@@ -27,6 +27,10 @@ typedef struct NBDServerData {
+ 
+ static NBDServerData *nbd_server;
+ 
++static void nbd_blockdev_client_closed(NBDClient *client, bool ignored)
++{
++    nbd_client_put(client);
++}
+ 
+ static gboolean nbd_accept(QIOChannel *ioc, GIOCondition condition,
+                            gpointer opaque)
+@@ -46,7 +50,7 @@ static gboolean nbd_accept(QIOChannel *ioc, GIOCondition condition,
+     qio_channel_set_name(QIO_CHANNEL(cioc), "nbd-server");
+     nbd_client_new(NULL, cioc,
+                    nbd_server->tlscreds, NULL,
+-                   nbd_client_put);
++                   nbd_blockdev_client_closed);
+     object_unref(OBJECT(cioc));
+     return TRUE;
+ }
+diff --git a/include/block/nbd.h b/include/block/nbd.h
+index 416257abca..8fa5ce51f3 100644
+--- a/include/block/nbd.h
++++ b/include/block/nbd.h
+@@ -162,7 +162,7 @@ void nbd_client_new(NBDExport *exp,
+                     QIOChannelSocket *sioc,
+                     QCryptoTLSCreds *tlscreds,
+                     const char *tlsaclname,
+-                    void (*close)(NBDClient *));
++                    void (*close_fn)(NBDClient *, bool));
+ void nbd_client_get(NBDClient *client);
+ void nbd_client_put(NBDClient *client);
+ 
+diff --git a/nbd/server.c b/nbd/server.c
+index 49b55f6ede..f2b1aa47ce 100644
+--- a/nbd/server.c
++++ b/nbd/server.c
+@@ -81,7 +81,7 @@ static QTAILQ_HEAD(, NBDExport) exports = QTAILQ_HEAD_INITIALIZER(exports);
+ 
+ struct NBDClient {
+     int refcount;
+-    void (*close)(NBDClient *client);
++    void (*close_fn)(NBDClient *client, bool negotiated);
+ 
+     bool no_zeroes;
+     NBDExport *exp;
+@@ -778,7 +778,7 @@ void nbd_client_put(NBDClient *client)
+     }
+ }
+ 
+-static void client_close(NBDClient *client)
++static void client_close(NBDClient *client, bool negotiated)
+ {
+     if (client->closing) {
+         return;
+@@ -793,8 +793,8 @@ static void client_close(NBDClient *client)
+                          NULL);
+ 
+     /* Also tell the client, so that they release their reference.  */
+-    if (client->close) {
+-        client->close(client);
++    if (client->close_fn) {
++        client->close_fn(client, negotiated);
+     }
+ }
+ 
+@@ -975,7 +975,7 @@ void nbd_export_close(NBDExport *exp)
+ 
+     nbd_export_get(exp);
+     QTAILQ_FOREACH_SAFE(client, &exp->clients, next, next) {
+-        client_close(client);
++        client_close(client, true);
+     }
+     nbd_export_set_name(exp, NULL);
+     nbd_export_set_description(exp, NULL);
+@@ -1337,7 +1337,7 @@ done:
+ 
+ out:
+     nbd_request_put(req);
+-    client_close(client);
++    client_close(client, true);
+     nbd_client_put(client);
+ }
+ 
+@@ -1363,7 +1363,7 @@ static coroutine_fn void nbd_co_client_start(void *opaque)
+     qemu_co_mutex_init(&client->send_lock);
+ 
+     if (nbd_negotiate(data)) {
+-        client_close(client);
++        client_close(client, false);
+         goto out;
+     }
+ 
+@@ -1373,11 +1373,17 @@ out:
+     g_free(data);
+ }
+ 
++/*
++ * Create a new client listener on the given export @exp, using the
++ * given channel @sioc.  Begin servicing it in a coroutine.  When the
++ * connection closes, call @close_fn with an indication of whether the
++ * client completed negotiation.
++ */
+ void nbd_client_new(NBDExport *exp,
+                     QIOChannelSocket *sioc,
+                     QCryptoTLSCreds *tlscreds,
+                     const char *tlsaclname,
+-                    void (*close_fn)(NBDClient *))
++                    void (*close_fn)(NBDClient *, bool))
+ {
+     NBDClient *client;
+     NBDClientNewData *data = g_new(NBDClientNewData, 1);
+@@ -1394,7 +1400,7 @@ void nbd_client_new(NBDExport *exp,
+     object_ref(OBJECT(client->sioc));
+     client->ioc = QIO_CHANNEL(sioc);
+     object_ref(OBJECT(client->ioc));
+-    client->close = close_fn;
++    client->close_fn = close_fn;
+ 
+     data->client = client;
+     data->co = qemu_coroutine_create(nbd_co_client_start, data);
+diff --git a/qemu-nbd.c b/qemu-nbd.c
+index 651f85ecc1..9464a0461c 100644
+--- a/qemu-nbd.c
++++ b/qemu-nbd.c
+@@ -336,10 +336,10 @@ static void nbd_export_closed(NBDExport *exp)
+ 
+ static void nbd_update_server_watch(void);
+ 
+-static void nbd_client_closed(NBDClient *client)
++static void nbd_client_closed(NBDClient *client, bool negotiated)
+ {
+     nb_fds--;
+-    if (nb_fds == 0 && !persistent && state == RUNNING) {
++    if (negotiated && nb_fds == 0 && !persistent && state == RUNNING) {
+         state = TERMINATE;
+     }
+     nbd_update_server_watch();
+-- 
+2.13.1
+