[lxc-devel] [lxc/master] commands: fix race when open()/close() cmd socket

brauner on Github lxc-bot at linuxcontainers.org
Thu Dec 14 16:40:35 UTC 2017


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 674 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20171214/4f0cad4e/attachment.bin>
-------------- next part --------------
From 0f9a9692f39a7623bb26f4fca23848ec46ba09fa Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Thu, 14 Dec 2017 17:35:24 +0100
Subject: [PATCH] commands: fix race when open()/close() cmd socket

When we report STOPPED to a caller and then close the command socket it is
technically possible - and I've seen this happen on the test builders - that a
container start() right after a wait() will receive ECONNREFUSED because it
called open() before we close(). So let's lock setting the STOPPED state.

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 src/lxc/commands.c |  2 ++
 src/lxc/start.c    | 46 ++++++++++++++++++++++++++++++++++++----------
 2 files changed, 38 insertions(+), 10 deletions(-)

diff --git a/src/lxc/commands.c b/src/lxc/commands.c
index 4adede557..d1f726f60 100644
--- a/src/lxc/commands.c
+++ b/src/lxc/commands.c
@@ -1313,7 +1313,9 @@ int lxc_cmd_init(const char *name, const char *lxcpath, const char *suffix)
 		return -1;
 	TRACE("Creating abstract unix socket \"%s\"", offset);
 
+	process_lock();
 	fd = lxc_abstract_unix_open(path, SOCK_STREAM, 0);
+	process_unlock();
 	if (fd < 0) {
 		ERROR("%s - Failed to create command socket %s",
 		      strerror(errno), offset);
diff --git a/src/lxc/start.c b/src/lxc/start.c
index a144d6f8f..a4088922b 100644
--- a/src/lxc/start.c
+++ b/src/lxc/start.c
@@ -357,13 +357,11 @@ static int lxc_serve_state_clients(const char *name,
 	struct lxc_state_client *client;
 	struct lxc_msg msg = {.type = lxc_msg_state, .value = state};
 
-	process_lock();
 	handler->state = state;
 	TRACE("Set container state to %s", lxc_state2str(state));
 
 	if (lxc_list_empty(&handler->conf->state_clients)) {
 		TRACE("No state clients registered");
-		process_unlock();
 		lxc_monitor_send_state(name, state, handler->lxcpath);
 		return 0;
 	}
@@ -401,7 +399,6 @@ static int lxc_serve_state_clients(const char *name,
 		free(cur->elem);
 		free(cur);
 	}
-	process_unlock();
 
 	return 0;
 }
@@ -442,6 +439,39 @@ static int lxc_serve_state_socket_pair(const char *name,
 	return 0;
 }
 
+/* This locks the command socket so that when we inform a caller that the
+ * container is STOPPED the caller can actually immediately restart the
+ * container without racing against the close() call on the command socket
+ *fwithout racing against the close() call on the command socket fd.
+ */
+int lxc_set_state_stopped_close_cmd_socket(const char *name,
+					   struct lxc_handler *handler)
+{
+	int ret;
+
+	process_lock();
+	ret = lxc_serve_state_socket_pair(name, handler, STOPPED);
+	if (ret < 0) {
+		process_unlock();
+		ERROR("Failed to synchronize via anonymous pair of unix sockets");
+		return -1;
+	}
+
+	ret = lxc_serve_state_clients(name, handler, STOPPED);
+
+	if (handler->conf->reboot == 0) {
+		/* close command socket */
+		close(handler->conf->maincmd_fd);
+		handler->conf->maincmd_fd = -1;
+	}
+
+	process_unlock();
+	if (ret < 0)
+		return -1;
+
+	return 0;
+}
+
 int lxc_set_state(const char *name, struct lxc_handler *handler,
 		  lxc_state_t state)
 {
@@ -453,7 +483,9 @@ int lxc_set_state(const char *name, struct lxc_handler *handler,
 		return -1;
 	}
 
+	process_lock();
 	ret = lxc_serve_state_clients(name, handler, state);
+	process_unlock();
 	if (ret < 0)
 		return -1;
 
@@ -753,13 +785,7 @@ void lxc_fini(const char *name, struct lxc_handler *handler)
 
 	cgroup_destroy(handler);
 
-	lxc_set_state(name, handler, STOPPED);
-
-	if (handler->conf->reboot == 0) {
-		/* close command socket */
-		close(handler->conf->maincmd_fd);
-		handler->conf->maincmd_fd = -1;
-	}
+	lxc_set_state_stopped_close_cmd_socket(name, handler);
 
 	if (run_lxc_hooks(name, "post-stop", handler->conf, handler->lxcpath, NULL)) {
 		ERROR("Failed to run lxc.hook.post-stop for container \"%s\".", name);


More information about the lxc-devel mailing list