4645beb
From df8ad9f128c15aa0a0ebc7b24e9a22c9775b67af Mon Sep 17 00:00:00 2001
4645beb
From: Eric Blake <eblake@redhat.com>
4645beb
Date: Fri, 26 May 2017 22:04:21 -0500
4645beb
Subject: [PATCH] nbd: Fully initialize client in case of failed negotiation
4645beb
4645beb
If a non-NBD client connects to qemu-nbd, we would end up with
4645beb
a SIGSEGV in nbd_client_put() because we were trying to
4645beb
unregister the client's association to the export, even though
4645beb
we skipped inserting the client into that list.  Easy trigger
4645beb
in two terminals:
4645beb
4645beb
$ qemu-nbd -p 30001 --format=raw file
4645beb
$ nmap 127.0.0.1 -p 30001
4645beb
4645beb
nmap claims that it thinks it connected to a pago-services1
4645beb
server (which probably means nmap could be updated to learn the
4645beb
NBD protocol and give a more accurate diagnosis of the open
4645beb
port - but that's not our problem), then terminates immediately,
4645beb
so our call to nbd_negotiate() fails.  The fix is to reorder
4645beb
nbd_co_client_start() to ensure that all initialization occurs
4645beb
before we ever try talking to a client in nbd_negotiate(), so
4645beb
that the teardown sequence on negotiation failure doesn't fault
4645beb
while dereferencing a half-initialized object.
4645beb
4645beb
While debugging this, I also noticed that nbd_update_server_watch()
4645beb
called by nbd_client_closed() was still adding a channel to accept
4645beb
the next client, even when the state was no longer RUNNING.  That
4645beb
is fixed by making nbd_can_accept() pay attention to the current
4645beb
state.
4645beb
4645beb
Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1451614
4645beb
4645beb
Signed-off-by: Eric Blake <eblake@redhat.com>
4645beb
Message-Id: <20170527030421.28366-1-eblake@redhat.com>
4645beb
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
4645beb
---
4645beb
 nbd/server.c | 8 +++-----
4645beb
 qemu-nbd.c   | 2 +-
4645beb
 2 files changed, 4 insertions(+), 6 deletions(-)
4645beb
4645beb
diff --git a/nbd/server.c b/nbd/server.c
4645beb
index ee59e5d..49b55f6 100644
4645beb
--- a/nbd/server.c
4645beb
+++ b/nbd/server.c
4645beb
@@ -1358,17 +1358,15 @@ static coroutine_fn void nbd_co_client_start(void *opaque)
4645beb
 
4645beb
     if (exp) {
4645beb
         nbd_export_get(exp);
4645beb
+        QTAILQ_INSERT_TAIL(&exp->clients, client, next);
4645beb
     }
4645beb
+    qemu_co_mutex_init(&client->send_lock);
4645beb
+
4645beb
     if (nbd_negotiate(data)) {
4645beb
         client_close(client);
4645beb
         goto out;
4645beb
     }
4645beb
-    qemu_co_mutex_init(&client->send_lock);
4645beb
     nbd_set_handlers(client);
4645beb
-
4645beb
-    if (exp) {
4645beb
-        QTAILQ_INSERT_TAIL(&exp->clients, client, next);
4645beb
-    }
4645beb
 out:
4645beb
     g_free(data);
4645beb
 }
4645beb
diff --git a/qemu-nbd.c b/qemu-nbd.c
4645beb
index f60842f..651f85e 100644
4645beb
--- a/qemu-nbd.c
4645beb
+++ b/qemu-nbd.c
4645beb
@@ -325,7 +325,7 @@ out:
4645beb
 
4645beb
 static int nbd_can_accept(void)
4645beb
 {
4645beb
-    return nb_fds < shared;
4645beb
+    return state == RUNNING && nb_fds < shared;
4645beb
 }
4645beb
 
4645beb
 static void nbd_export_closed(NBDExport *exp)
4645beb
-- 
4645beb
1.8.3.1
4645beb