Skip to content

Commit a0a0950

Browse files
Kenostaticfloat
authored andcommitted
Fix race condition in sandbox
The current sandbox code has a race condition. If the child exits before the sigwait signal mask is setup, we will hang forever waiting for a signal. This has been observed in practice in Yggdrasil. I don't know that the sigwait here is supposed to do - plain waitpid should be fine, so try removing it to see if everything works. If the sigwait does turn out to be necessary, we should be able to fix this by moving the signal mask setup to above the fork instead.
1 parent 949bf17 commit a0a0950

File tree

1 file changed

+29
-26
lines changed

1 file changed

+29
-26
lines changed

deps/userns_sandbox.c

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -259,35 +259,38 @@ static int sandbox_main(const char * root_dir, const char * new_cd, int sandbox_
259259

260260
// Let's perform normal init functions, handling signals from orphaned
261261
// children, etc
262-
sigset_t waitset;
263-
sigemptyset(&waitset);
264-
sigaddset(&waitset, SIGCHLD);
265-
sigprocmask(SIG_BLOCK, &waitset, NULL);
266262
for (;;) {
267-
int sig;
268-
sigwait(&waitset, &sig);
269-
270-
pid_t reaped_pid;
271-
while ((reaped_pid = waitpid(-1, &status, 0)) != -1) {
272-
if (reaped_pid == child_pid) {
273-
// If it was the main pid that exited, we're going to exit too.
274-
// If we died of a signal, return that signal + 256, which we will
275-
// notice on the other end as a signal.
276-
if (WIFSIGNALED(status)) {
277-
uint32_t reported_exit_code = 256 + WTERMSIG(status);
278-
check(sizeof(uint32_t) == write(parent_pipe[1], &reported_exit_code, sizeof(uint32_t)));
279-
return 0;
280-
}
281-
if (WIFEXITED(status)) {
282-
// Normal exits get reported in a more straightforward fashion
283-
uint32_t reported_exit_code = WEXITSTATUS(status);
284-
check(sizeof(uint32_t) == write(parent_pipe[1], &reported_exit_code, sizeof(uint32_t)));
285-
return 0;
286-
}
287-
288-
// Unsure what's going on here, but it isn't good.
263+
pid_t reaped_pid = waitpid(-1, &status, 0);
264+
if (reaped_pid == -1) {
265+
if (errno == ECHILD) {
266+
// Somehow we missed the reaping of our child - this shouldn't happen,
267+
// but let's make sure we don't hang forever if it somehow does.
268+
check(-2);
269+
}
270+
if (errno != EINTR) {
271+
// Some other unexpected error
289272
check(-1);
290273
}
274+
continue;
275+
}
276+
if (reaped_pid == child_pid) {
277+
// If it was the main pid that exited, we're going to exit too.
278+
// If we died of a signal, return that signal + 256, which we will
279+
// notice on the other end as a signal.
280+
if (WIFSIGNALED(status)) {
281+
uint32_t reported_exit_code = 256 + WTERMSIG(status);
282+
check(sizeof(uint32_t) == write(parent_pipe[1], &reported_exit_code, sizeof(uint32_t)));
283+
return 0;
284+
}
285+
if (WIFEXITED(status)) {
286+
// Normal exits get reported in a more straightforward fashion
287+
uint32_t reported_exit_code = WEXITSTATUS(status);
288+
check(sizeof(uint32_t) == write(parent_pipe[1], &reported_exit_code, sizeof(uint32_t)));
289+
return 0;
290+
}
291+
292+
// Unsure what's going on here, but it isn't good.
293+
check(-1);
291294
}
292295
}
293296
}

0 commit comments

Comments
 (0)