Here's a patch for Linux and also FreeBSD.  The latter OS decided to
turn on ASLR by default recently, causing my workstation to fail like
this quite reliably, which reminded me to follow up with this.  It
disables ASLR in pg_ctl and pg_regress, which is enough for check and
check-world, but doesn't help you if you run the server directly
(unlike the different hack done for macOS).

For whatever random reason the failures are rarer on Linux (could be
my imagination, but I think they might be clustered, I didn't look
into the recipe for the randomness), but even without reproducing a
failure it's clear to see using pmap that this has the right effect.
I didn't bother with a check for the existence of ADDR_NO_RANDOMIZE
because it's since 2.6.12 which is definitely ancient enough.
From e615dc88c89142a1b7ecf7ba6f6f01ea59c61be5 Mon Sep 17 00:00:00 2001
From: Thomas Munro <tmu...@b1f14.hub>
Date: Wed, 17 Nov 2021 20:25:19 +1300
Subject: [PATCH] Make EXEC_BACKEND work reliably on Linux and FreeBSD.

Try to disable ASLR when building in EXEC_BACKEND mode (developer only),
to avoid random memory mapping failures.

Suggested-by: Andres Freund <and...@anarazel.de>
Discussion: https://postgr.es/m/20210806032944.m4tz7j2w47mant26%40alap3.anarazel.de
---
 configure                     |  2 +-
 configure.ac                  |  1 +
 src/bin/pg_ctl/pg_ctl.c       |  4 ++++
 src/common/exec.c             | 32 ++++++++++++++++++++++++++++++++
 src/include/pg_config.h.in    |  3 +++
 src/include/port.h            | 11 +++++++++++
 src/test/regress/pg_regress.c |  4 ++++
 7 files changed, 56 insertions(+), 1 deletion(-)

diff --git a/configure b/configure
index ded80be880..9b7033d7dd 100755
--- a/configure
+++ b/configure
@@ -13427,7 +13427,7 @@ $as_echo "#define HAVE_STDBOOL_H 1" >>confdefs.h
 fi
 
 
-for ac_header in atomic.h copyfile.h execinfo.h getopt.h ifaddrs.h langinfo.h mbarrier.h poll.h sys/epoll.h sys/event.h sys/ipc.h sys/prctl.h sys/procctl.h sys/pstat.h sys/resource.h sys/select.h sys/sem.h sys/shm.h sys/sockio.h sys/tas.h sys/uio.h sys/un.h termios.h ucred.h wctype.h
+for ac_header in atomic.h copyfile.h execinfo.h getopt.h ifaddrs.h langinfo.h mbarrier.h poll.h sys/epoll.h sys/event.h sys/ipc.h sys/personality.h sys/prctl.h sys/procctl.h sys/pstat.h sys/resource.h sys/select.h sys/sem.h sys/shm.h sys/sockio.h sys/tas.h sys/uio.h sys/un.h termios.h ucred.h wctype.h
 do :
   as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
 ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
diff --git a/configure.ac b/configure.ac
index 775e5c4436..9602b04e36 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1376,6 +1376,7 @@ AC_CHECK_HEADERS(m4_normalize([
 	sys/epoll.h
 	sys/event.h
 	sys/ipc.h
+	sys/personality.h
 	sys/prctl.h
 	sys/procctl.h
 	sys/pstat.h
diff --git a/src/bin/pg_ctl/pg_ctl.c b/src/bin/pg_ctl/pg_ctl.c
index 7fbbe7022e..eadb5c2297 100644
--- a/src/bin/pg_ctl/pg_ctl.c
+++ b/src/bin/pg_ctl/pg_ctl.c
@@ -451,6 +451,10 @@ start_postmaster(void)
 	fflush(stdout);
 	fflush(stderr);
 
+#ifdef EXEC_BACKEND
+	pg_disable_aslr();
+#endif
+
 	pm_pid = fork();
 	if (pm_pid < 0)
 	{
diff --git a/src/common/exec.c b/src/common/exec.c
index 81b810d4cf..e5d9174333 100644
--- a/src/common/exec.c
+++ b/src/common/exec.c
@@ -25,6 +25,14 @@
 #include <sys/wait.h>
 #include <unistd.h>
 
+#ifdef EXEC_BACKEND
+#if defined(HAVE_SYS_PERSONALITY_H)
+#include <sys/personality.h>
+#elif defined(HAVE_SYS_PROCCTL_H)
+#include <sys/procctl.h>
+#endif
+#endif
+
 /*
  * Hacky solution to allow expressing both frontend and backend error reports
  * in one macro call.  First argument of log_error is an errcode() call of
@@ -470,6 +478,30 @@ set_pglocale_pgservice(const char *argv0, const char *app)
 	}
 }
 
+#ifdef EXEC_BACKEND
+/*
+ * For the benefit of PostgreSQL developers testing EXEC_BACKEND code paths on
+ * Unix systems (ie paths normally exercised only on Windows), provide a way to
+ * disable ASLR so that we avoid memory map in developer-only builds, if we
+ * know how on this platform.  (See also the macOS-specific hack in
+ * sysv_shmem.c.)
+ */
+int
+pg_disable_aslr(void)
+{
+#if defined(HAVE_SYS_PERSONALITY_H)
+	return personality(ADDR_NO_RANDOMIZE);
+#elif defined(HAVE_SYS_PROCCTL_H) && defined(PROC_ASLR_FORCE_DISABLE)
+	int			data = PROC_ASLR_FORCE_DISABLE;
+
+	return procctl(P_PID, 0, PROC_ASLR_CTL, &data);
+#else
+	errno = ENOSYS;
+	return -1;
+#endif
+}
+#endif
+
 #ifdef WIN32
 
 /*
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index ca3592465e..a3d658644a 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -617,6 +617,9 @@
 /* Define to 1 if you have the <sys/ipc.h> header file. */
 #undef HAVE_SYS_IPC_H
 
+/* Define to 1 if you have the <sys/personality.h> header file. */
+#undef HAVE_SYS_PERSONALITY_H
+
 /* Define to 1 if you have the <sys/prctl.h> header file. */
 #undef HAVE_SYS_PRCTL_H
 
diff --git a/src/include/port.h b/src/include/port.h
index 49b4d38131..94b0f23837 100644
--- a/src/include/port.h
+++ b/src/include/port.h
@@ -42,6 +42,12 @@ typedef SOCKET pgsocket;
 typedef unsigned int socklen_t;
 #endif
 
+#ifdef EXEC_BACKEND
+#if defined(HAVE_SYS_PROCCTL_H)
+#include <sys/procctl.h>
+#endif
+#endif
+
 /* non-blocking */
 extern bool pg_set_noblock(pgsocket sock);
 extern bool pg_set_block(pgsocket sock);
@@ -140,6 +146,11 @@ extern char *pipe_read_line(char *cmd, char *line, int maxsize);
 /* Doesn't belong here, but this is used with find_other_exec(), so... */
 #define PG_BACKEND_VERSIONSTR "postgres (PostgreSQL) " PG_VERSION "\n"
 
+#ifdef EXEC_BACKEND
+/* Disable ASLR before exec, for developer builds only (in exec.c) */
+extern int pg_disable_aslr(void);
+#endif
+
 
 #if defined(WIN32) || defined(__CYGWIN__)
 #define EXE ".exe"
diff --git a/src/test/regress/pg_regress.c b/src/test/regress/pg_regress.c
index 2c8a600bad..67c78bccf2 100644
--- a/src/test/regress/pg_regress.c
+++ b/src/test/regress/pg_regress.c
@@ -1246,6 +1246,10 @@ spawn_process(const char *cmdline)
 	if (logfile)
 		fflush(logfile);
 
+#ifdef EXEC_BACKEND
+	pg_disable_aslr();
+#endif
+
 	pid = fork();
 	if (pid == -1)
 	{
-- 
2.33.1

Reply via email to