From e7b6802367686f72cba7b1a43a07302add963ca6 Mon Sep 17 00:00:00 2001
From: Mark Dilger <mark.dilger@enterprisedb.com>
Date: Thu, 26 Feb 2026 11:08:17 -0800
Subject: [PATCH v1] Harden internal_load_library() against transient errors

MacOS can fail to find a shared library during rapid crash/restart
cycles, owing to well known filesystem flakiness.  When the library
is loaded in response to the postgresql.conf entry

  shared_preload_library = 'mylib'

the library name gets expanded to the full path /path/to/mylib.dylib
and that gets stored.  Later, the same library can by called using
"$libdir/mylib".  The $libdir/ prefix gets stripped in
load_external_function(), resulting in a search for just "mylib".
The call to expand_dynamic_library_name("mylib") tries to find it,
but pg_file_exists() can get a spurious failure from macOS despite
the file existing.  In that case, pg_file_exists() fails and
internal_load_libary("mylib") gets called without the full path,
leading to a strcmp for "mylib" to fail, because it doesn't match
"/path/to/mylib.dylib".  Then stat("mylib") gets called, which fails
with ENOENT.

Modify internal_load_library() in dfmgr.c to have a fallback when
the matching fails to check whether the library was already loaded
during server startup.
---
 src/backend/utils/fmgr/dfmgr.c | 71 +++++++++++++++++++++++++++++-----
 1 file changed, 61 insertions(+), 10 deletions(-)

diff --git a/src/backend/utils/fmgr/dfmgr.c b/src/backend/utils/fmgr/dfmgr.c
index 1366521f471..ef4a27ac838 100644
--- a/src/backend/utils/fmgr/dfmgr.c
+++ b/src/backend/utils/fmgr/dfmgr.c
@@ -193,6 +193,7 @@ internal_load_library(const char *libname)
 	char	   *load_error;
 	struct stat stat_buf;
 	PG_init_t	PG_init;
+	int			stat_errno = 0;
 
 	/*
 	 * Scan the list of loaded FILES to see if the file has been loaded.
@@ -209,16 +210,66 @@ internal_load_library(const char *libname)
 		 * Check for same files - different paths (ie, symlink or link)
 		 */
 		if (stat(libname, &stat_buf) == -1)
-			ereport(ERROR,
-					(errcode_for_file_access(),
-					 errmsg("could not access file \"%s\": %m",
-							libname)));
-
-		for (file_scanner = file_list;
-			 file_scanner != NULL &&
-			 !SAME_INODE(stat_buf, *file_scanner);
-			 file_scanner = file_scanner->next)
-			;
+		{
+			/*
+			 * Save errno for later, but don't error out yet. We might still
+			 * find this library already loaded under a different path. This
+			 * can happen if the library was preloaded with its full path but
+			 * we're now trying to load it with a bare name.
+			 */
+			stat_errno = errno;
+		}
+
+		if (stat_errno == 0)
+		{
+			for (file_scanner = file_list;
+				 file_scanner != NULL &&
+				 !SAME_INODE(stat_buf, *file_scanner);
+				 file_scanner = file_scanner->next)
+				;
+		}
+
+		/*
+		 * If we couldn't stat() the file, check if any loaded library's
+		 * filename ends with the basename we're looking for. This handles
+		 * the case where a library was preloaded with its full path but
+		 * we're trying to load it again with just the basename.
+		 */
+		if (stat_errno != 0 && file_scanner == NULL)
+		{
+			size_t		libname_len = strlen(libname);
+
+			for (file_scanner = file_list;
+				 file_scanner != NULL;
+				 file_scanner = file_scanner->next)
+			{
+				size_t		scanner_len = strlen(file_scanner->filename);
+				const char *scanner_base;
+
+				/* Check if the loaded filename ends with our libname */
+				if (scanner_len >= libname_len)
+				{
+					scanner_base = file_scanner->filename + scanner_len - libname_len;
+					if ((scanner_base == file_scanner->filename ||
+						 IS_DIR_SEP(scanner_base[-1])) &&
+						strcmp(scanner_base, libname) == 0)
+					{
+						/* Found a match by basename */
+						break;
+					}
+				}
+			}
+
+			/* If still not found, now we can error out */
+			if (file_scanner == NULL)
+			{
+				errno = stat_errno;
+				ereport(ERROR,
+						(errcode_for_file_access(),
+						 errmsg("could not access file \"%s\": %m",
+								libname)));
+			}
+		}
 	}
 
 	if (file_scanner == NULL)
-- 
2.39.5 (Apple Git-154)

