From 423cfa40efe859448b69fbbbce62a5be750bc9fd Mon Sep 17 00:00:00 2001
From: Tavian Barnes <tavianator@tavianator.com>
Date: Mon, 13 May 2024 13:11:58 -0400
Subject: xspawn: Mask signals before fork()

This prevents signal handlers from running in the child before execve().
---
 src/xspawn.c | 37 +++++++++++++++++++++++++++++--------
 1 file changed, 29 insertions(+), 8 deletions(-)

diff --git a/src/xspawn.c b/src/xspawn.c
index 0b0cea4..5a8277d 100644
--- a/src/xspawn.c
+++ b/src/xspawn.c
@@ -5,9 +5,11 @@
 #include "xspawn.h"
 #include "alloc.h"
 #include "bfstd.h"
+#include "diag.h"
 #include "list.h"
 #include <errno.h>
 #include <fcntl.h>
+#include <signal.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/resource.h>
@@ -590,30 +592,49 @@ static pid_t bfs_fork_spawn(struct bfs_resolver *res, const struct bfs_spawn *ct
 		return -1;
 	}
 
+	// Block signals before fork() so handlers don't run in the child
+	sigset_t new_mask;
+	if (sigfillset(&new_mask) != 0) {
+		goto fail;
+	}
+	sigset_t old_mask;
+	errno = pthread_sigmask(SIG_BLOCK, &new_mask, &old_mask);
+	if (errno != 0) {
+		goto fail;
+	}
+
 	pid_t pid = fork();
-	if (pid < 0) {
-		close_quietly(pipefd[1]);
-		close_quietly(pipefd[0]);
-		return -1;
-	} else if (pid == 0) {
+	if (pid == 0) {
 		// Child
 		bfs_spawn_exec(res, ctx, argv, envp, pipefd);
 	}
 
-	// Parent
+	// Restore the original signal mask
+	int ret = pthread_sigmask(SIG_SETMASK, &old_mask, NULL);
+	bfs_verify(ret == 0, "pthread_sigmask(): %s", xstrerror(ret));
+
+	if (pid < 0) {
+		// fork() failed
+		goto fail;
+	}
+
 	xclose(pipefd[1]);
 
 	int error;
 	ssize_t nbytes = xread(pipefd[0], &error, sizeof(error));
 	xclose(pipefd[0]);
 	if (nbytes == sizeof(error)) {
-		int wstatus;
-		xwaitpid(pid, &wstatus, 0);
+		xwaitpid(pid, NULL, 0);
 		errno = error;
 		return -1;
 	}
 
 	return pid;
+
+fail:
+	close_quietly(pipefd[1]);
+	close_quietly(pipefd[0]);
+	return -1;
 }
 
 /** Call the right bfs_spawn() implementation. */
-- 
cgit v1.2.3