diff options
-rw-r--r-- | src/nspawn/nspawn.c | 35 |
1 files changed, 26 insertions, 9 deletions
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 3a39b4a6f5..60e1b5dbf1 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -2928,6 +2928,7 @@ static int outer_child( assert(uuid_socket >= 0); assert(notify_socket >= 0); assert(kmsg_socket >= 0); + assert(cgroup_socket); if (prctl(PR_SET_PDEATHSIG, SIGKILL) < 0) return log_error_errno(errno, "PR_SET_PDEATHSIG failed: %m"); @@ -3170,20 +3171,18 @@ static int outer_child( * less privileged mountns (and using userns causes the mountns to be less privileged). */ if (!arg_use_cgns) { /* If !use_cgns, then cgroup_mount_mounts() needs to look at /proc/pid/cgroup; but because we've - * already chroot()ed, we don't have access to /proc. So the parent opens the file and sends it to - * us. */ + * already chroot()ed, we don't have access to /proc. So the parent opens the file for us and then + * sends it to us. */ int cgfd; _cleanup_fclose_ FILE *cgfile = NULL; - assert(cgroup_socket); - cgfd = receive_one_fd(cgroup_socket, 0); if (cgfd < 0) return log_error_errno(cgfd, "Failed to recv cgroup fd: %m"); cgfile = fdopen(cgfd, "re"); if (!cgfile) { - r = -errno; /* in case safe_close sets errno */ + r = -errno; /* in case safe_close() sets errno */ cgfd = safe_close(cgfd); return log_error_errno(r, "Failed to create a stream object for cgroup fd: %m"); } @@ -3194,6 +3193,15 @@ static int outer_child( arg_selinux_apifs_context); if (r < 0) return r; + } else { + /* We're not doing anything else, but wait for a bit of data anyway; as a synchronization point, to + * make sure that our SIGCHLD doesn't EINTR anything important. */ + char c; + l = recv(cgroup_socket, &c, sizeof(c), 0); + if (l < 0) + return log_error_errno(errno, "Failed to recv synchronization byte: %m"); + if (l != sizeof(c)) + return log_error_errno(errno, "Short read while receiving synchronization byte: %m"); } pid_socket = safe_close(pid_socket); @@ -3685,9 +3693,8 @@ static int run(int master, if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, uid_shift_socket_pair) < 0) return log_error_errno(errno, "Failed to create uid shift socket pair: %m"); - if (!arg_use_cgns) - if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, cgroup_socket_pair) < 0) - return log_error_errno(errno, "Failed to create cgroup socket pair: %m"); + if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, cgroup_socket_pair) < 0) + return log_error_errno(errno, "Failed to create cgroup socket pair: %m"); /* Child can be killed before execv(), so handle SIGCHLD in order to interrupt * parent's blocking calls and give it a chance to call wait() and terminate. */ @@ -3900,7 +3907,6 @@ static int run(int master, return r; if (!arg_use_cgns) { - /* helper_pid won't exit until this happens */ const char *fs; _cleanup_close_ int fd; @@ -3912,6 +3918,17 @@ static int run(int master, r = send_one_fd(cgroup_socket_pair[0], fd, 0); if (r < 0) return log_error_errno(r, "Failed to send cgroup fd: %m"); + } else { + /* we don't have any data to send, but sending something here is important because it acts as a + * synchronization point. Otherwise the outer child could exit earlier, and the resulting SIGCHLD + * could interrupt something like register_machine() above. We could use a barrier for this, but we + * have a perfectly good socket here already. */ + char c = '\0'; + l = send(cgroup_socket_pair[0], &c, sizeof(c), MSG_NOSIGNAL); + if (l < 0) + return log_error_errno(errno, "Failed to send synchronization byte: %m"); + if (l != sizeof(c)) + return log_error_errno(errno, "Short write while sending synchronization byte: %m"); } /* Wait for the outer child. */ |