From 9bcedede452c1f37dd790f86bc587353cc455e3f Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Tue, 10 Aug 2021 22:05:00 +1200
Subject: [PATCH] Try to make EXEC_BACKEND more convenient on macOS.

Use posix_spawn() instead of fork() + execv(), with a special
undocumented flag that disables ASLR, if you build with -DEXEC_BACKEND
-DUSE_POSIX_SPAWN -DUSE_POSIX_SPAWN_DISABLE_ASLR.

XXX Experiment only...
XXX Still fails make check occasionally :-(
---
 src/backend/postmaster/postmaster.c | 33 +++++++++++++++++++
 src/bin/pg_ctl/pg_ctl.c             | 51 ++++++++++++++++++++++++++++-
 2 files changed, 83 insertions(+), 1 deletion(-)

diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index fc0bc8d99e..3a1e9ae8f8 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -93,6 +93,10 @@
 #include <pthread.h>
 #endif
 
+#ifdef USE_POSIX_SPAWN
+#include <spawn.h>
+#endif
+
 #include "access/transam.h"
 #include "access/xlog.h"
 #include "catalog/pg_control.h"
@@ -4585,6 +4589,10 @@ internal_forkexec(int argc, char *argv[], Port *port)
 	char		tmpfilename[MAXPGPATH];
 	BackendParameters param;
 	FILE	   *fp;
+#ifdef USE_POSIX_SPAWN
+	posix_spawn_file_actions_t spawn_file_actions;
+	posix_spawnattr_t spawnattrs;
+#endif
 
 	if (!save_backend_variables(&param, port))
 		return -1;				/* log made by save_backend_variables */
@@ -4642,6 +4650,30 @@ internal_forkexec(int argc, char *argv[], Port *port)
 	/* Insert temp file name after --fork argument */
 	argv[2] = tmpfilename;
 
+#ifdef USE_POSIX_SPAWN
+	posix_spawn_file_actions_init(&spawn_file_actions);
+	posix_spawnattr_init(&spawnattrs);
+#ifdef USE_POSIX_SPAWN_DISABLE_ASLR
+	/*
+	 * Undocumented magic.  See bsd/sys/spawn.h and bsd/kern/kern_exec.c in the
+	 * Darwin sources at https://github.com/apple/darwin-xnu.
+	 */
+	if ((errno = posix_spawnattr_setflags(&spawnattrs, 0x0100)) != 0)
+		elog(ERROR, "could not set ASLR disable flag when spawning backend: %m");
+#endif
+	errno = posix_spawn(&pid,
+						postgres_exec_path,
+						&spawn_file_actions,
+						&spawnattrs,
+						argv,
+						NULL);
+	if (errno != 0)
+	{
+			ereport(LOG,
+					(errmsg("could not spawn server process \"%s\": %m",
+							postgres_exec_path)));
+	}
+#else
 	/* Fire off execv in child */
 	if ((pid = fork_process()) == 0)
 	{
@@ -4654,6 +4686,7 @@ internal_forkexec(int argc, char *argv[], Port *port)
 			exit(1);
 		}
 	}
+#endif
 
 	return pid;					/* Parent returns pid, or -1 on fork failure */
 }
diff --git a/src/bin/pg_ctl/pg_ctl.c b/src/bin/pg_ctl/pg_ctl.c
index 7985da0a94..f105e483c4 100644
--- a/src/bin/pg_ctl/pg_ctl.c
+++ b/src/bin/pg_ctl/pg_ctl.c
@@ -18,6 +18,10 @@
 #include <sys/wait.h>
 #include <unistd.h>
 
+#ifdef USE_POSIX_SPAWN
+#include <spawn.h>
+#endif
+
 #ifdef HAVE_SYS_RESOURCE_H
 #include <sys/time.h>
 #include <sys/resource.h>
@@ -442,15 +446,59 @@ free_readfile(char **optlines)
 static pgpid_t
 start_postmaster(void)
 {
+#ifdef USE_POSIX_SPAWN
+	posix_spawn_file_actions_t spawn_file_actions;
+	posix_spawnattr_t spawnattrs;
+#else
 	char		cmd[MAXPGPATH];
+#endif
 
 #ifndef WIN32
-	pgpid_t		pm_pid;
+	pid_t		pm_pid;
 
 	/* Flush stdio channels just before fork, to avoid double-output problems */
 	fflush(stdout);
 	fflush(stderr);
 
+#ifdef USE_POSIX_SPAWN
+	posix_spawn_file_actions_init(&spawn_file_actions);
+	posix_spawnattr_init(&spawnattrs);
+#ifdef USE_POSIX_SPAWN_DISABLE_ASLR
+	/*
+	 * Undocumented magic.  See bsd/sys/spawn.h and bsd/kern/kern_exec.c in the
+	 * Darwin sources at https://github.com/apple/darwin-xnu.
+	 */
+	if ((errno = posix_spawnattr_setflags(&spawnattrs, 0x0100)) != 0)
+		write_stderr(_("could not set undocumented ASLR disable flag when spawning postmaster: %s"),
+					 strerror(errno));
+#endif
+	{
+		/* XXX:HACK this is incomplete, doesn't include the postopts... */
+		/* XXX Theory here was that shell exec style used by the exec code
+		 * might drop the flag, hence spawning executable directly, but doing
+		 * that properly would involve unpicking the options and their quotes
+		 * etc... */
+		char *args[] = {
+			exec_path,
+			"-D",
+			getenv("PGDATA"),
+			NULL
+		};
+		errno = posix_spawn(&pm_pid,
+							exec_path,
+							&spawn_file_actions,
+							&spawnattrs,
+							args,
+							NULL);
+	}
+	if (errno != 0)
+	{
+		write_stderr(_("%s: could not start server: %s\n"),
+					 progname, strerror(errno));
+		exit(1);
+	}
+	return pm_pid;
+#else
 	pm_pid = fork();
 	if (pm_pid < 0)
 	{
@@ -502,6 +550,7 @@ start_postmaster(void)
 	exit(1);
 
 	return 0;					/* keep dumb compilers quiet */
+#endif
 
 #else							/* WIN32 */
 
-- 
2.30.1 (Apple Git-130)

