From 2e415410fc22b15d789b87b034c30bf8a998b5da Mon Sep 17 00:00:00 2001 From: Andre Noll Date: Tue, 23 Aug 2016 22:58:46 +0200 Subject: [PATCH] server: Fix race condition in afs startup. After server_init() returns, the server accepts connections on the TCP command socket. If an afs command arrives on the command socket, the server process forks and the resulting child process (the command handler) connects to the local afs socket. However, this socket is created by the afs process which was forked from the server process in server_init(). It is therefore possible that the command handler connects before the afs process started to listen on the local afs socket. In this case, the connection, hence the command fails. This commit fixes the race condition by letting the parent process block on read(2) on the afs socket. The afs process writes a byte to the other end of the socket after it has completed its setup, causing the parent process to resume. For this to work, we need a connection-mode byte stream for the communication between the server and the afs process, rather than the connectionless datagram socket we have now. There is no particular reason to prefer a datagram socket here, so let's switch to SOCK_STREAM. --- afs.c | 7 +++++++ server.c | 8 +++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/afs.c b/afs.c index 0accc451..071f657c 100644 --- a/afs.c +++ b/afs.c @@ -1024,6 +1024,13 @@ __noreturn void afs_init(uint32_t cookie, int socket_fd) register_command_task(cookie, &s); s.default_timeout.tv_sec = 0; s.default_timeout.tv_usec = 999 * 1000; + ret = write(socket_fd, "\0", 1); + if (ret != 1) { + if (ret == 0) + errno = EINVAL; + ret = -ERRNO_TO_PARA_ERROR(errno); + goto out_close; + } ret = schedule(&s); sched_shutdown(&s); out_close: diff --git a/server.c b/server.c index ae8848b6..ca71c4d8 100644 --- a/server.c +++ b/server.c @@ -411,8 +411,9 @@ static int init_afs(int argc, char **argv) { int ret, afs_server_socket[2]; pid_t afs_pid; + char c; - ret = socketpair(PF_UNIX, SOCK_DGRAM, 0, afs_server_socket); + ret = socketpair(PF_UNIX, SOCK_STREAM, 0, afs_server_socket); if (ret < 0) exit(EXIT_FAILURE); get_random_bytes_or_die((unsigned char *)&afs_socket_cookie, @@ -422,6 +423,7 @@ static int init_afs(int argc, char **argv) exit(EXIT_FAILURE); if (afs_pid == 0) { /* child (afs) */ int i; + for (i = argc - 1; i >= 0; i--) memset(argv[i], 0, strlen(argv[i])); sprintf(argv[0], "para_server (afs)"); @@ -430,6 +432,10 @@ static int init_afs(int argc, char **argv) } mmd->afs_pid = afs_pid; close(afs_server_socket[1]); + if (read(afs_server_socket[0], &c, 1) <= 0) { + PARA_EMERG_LOG("early afs exit\n"); + exit(EXIT_FAILURE); + } ret = mark_fd_nonblocking(afs_server_socket[0]); if (ret < 0) exit(EXIT_FAILURE); -- 2.39.2