From b6d49f538c752ec427b748db18eadfb8b4a8a317 Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Tue, 19 Mar 2024 00:25:34 +1300
Subject: [PATCH v3] Don't clobber LD_LIBRARY_PATH or LD_PRELOAD.

Our PS_USE_CLOBBER_ARGV code relocates the environment, which itself is
allowed, in order to steal the old space to make a bigger argv[0] for
ps/top to show, which is undefined behavior, but a widely used technique
going back to sendmail.

Unfortunately that corrupts musl's copy of LD_LIBRARY_PATH if set,
because it stashes a pointer to the initial value before main() begins.
It probably doesn't matter for installed servers but breaks the
regression tests and seems generally bad.

Stop clobbering memory at or beyond those those variable names.  Though
LD_LIBRARY_PATH is probably rare on production systems, anyone who is
defining it should hopefully be unlikely to see any new truncation in
practice, because pg_ctl defines some environment variables that appear
at the start of environ[] in the postmaster.  If that's still not
enough, affected users may be able to define extra dummy variables.

Though most Linux users use glibc, it doesn't seem that easy or nice to
to try to distinguish glibc from musl, so apply this logic to all
__linux__ systems.

Reported-by: Wolfgang Walther <walther@technowledgy.de>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: Bruce Momjian <bruce@momjian.us>
Discussion: https://postgr.es/m/fddd1cd6-dc16-40a2-9eb5-d7fef2101488%40technowledgy.de
---
 src/backend/utils/misc/ps_status.c | 26 +++++++++++++++++++++++++-
 1 file changed, 25 insertions(+), 1 deletion(-)

diff --git a/src/backend/utils/misc/ps_status.c b/src/backend/utils/misc/ps_status.c
index 5d829e6e483..d989e29364a 100644
--- a/src/backend/utils/misc/ps_status.c
+++ b/src/backend/utils/misc/ps_status.c
@@ -151,7 +151,31 @@ save_ps_display_args(int argc, char **argv)
 		for (i = 0; environ[i] != NULL; i++)
 		{
 			if (end_of_area + 1 == environ[i])
-				end_of_area = environ[i] + strlen(environ[i]);
+			{
+
+#if defined(__linux__)
+				/*
+				 * If we see variables that the musl runtime linker is known
+				 * to stash pointers to, give up so we don't break later calls
+				 * to dlopen().
+				 */
+				if (strstr(environ[i], "LD_LIBRARY_PATH=") == environ[i] ||
+					strstr(environ[i], "LD_PRELOAD=") == environ[i])
+				{
+					/*
+					 * We can overwrite the name, but stop at the equals sign.
+					 * Future loops will not find contiguous space, but we
+					 * don't break early because we want to count the total
+					 * number.
+					 */
+					end_of_area = strchr(environ[i], '=');
+				}
+				else
+#endif
+				{
+					end_of_area = environ[i] + strlen(environ[i]);
+				}
+			}
 		}
 
 		ps_buffer = argv[0];
-- 
2.39.2

