From e7b6802367686f72cba7b1a43a07302add963ca6 Mon Sep 17 00:00:00 2001 From: Mark Dilger 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)