From 6b14ef83a0c5778cacfda90d127c87f94c03cbff Mon Sep 17 00:00:00 2001
From: Ray Donnelly <mingw.android@gmail.com>
Date: Tue, 24 Dec 2019 18:37:17 +0100
Subject: [PATCH 13/25] Add CondaEcosystemModifyDllSearchPath()
There are 2 modes depending on CONDA_DLL_SEARCH_MODIFICATION env variable
- unset CONDA_DLL_SEARCH_MODIFICATION (Default)
In this mode, the python interpreter works as if the python interpreter
was called with the following conda directories.
os.add_dll_directory(join(sys.prefix, 'bin'))
os.add_dll_directory(join(sys.prefix, 'Scripts'))
os.add_dll_directory(join(sys.prefix, 'Library', 'bin'))
os.add_dll_directory(join(sys.prefix, 'Library', 'usr', 'bin'))
os.add_dll_directory(join(sys.prefix, 'Library', 'mingw-w64', 'bin'))
Search order
- The directory that contains the DLL (if looking for a dependency)
- Application (python.exe) directory
- Directories added with os.add_dll_directory
- The 5 conda directories
- C:\Windows\System32
Note that the default behaviour changed in conda python 3.10 to
make os.add_dll_directory work in user code.
- CONDA_DLL_SEARCH_MODIFICATION=1
Search order is roughly,
- The directory that contains the DLL (if looking for a dependency)
- Application (python.exe) directory
- C:\Windows
- Current working directory
- The 5 conda directories
- PATH
- Directories added with os.add_dll_directory
- Old PATH entries (Deficiency in current patch)
- Old working directories (Deficiency in current patch)
- C:\Windows\System32
This changes the DLL search order so that C:\Windows\System32 does not
get searched in before entries in PATH.
Reviewed by Kai Tietz 7.2.2019
Updated a bit to include other directories.
Made fwprintfs breakpointable
From Shaun Walbridge:
Fix CondaEcosystemModifyDllSearchPath for users of the Python DLL
Co-authored-by: Isuru Fernando <isuruf@gmail.com>
---
Modules/main.c | 370 +++++++++++++++++++++++++++++++++++++++++++
Python/dynload_win.c | 4 +
Python/pylifecycle.c | 5 +-
3 files changed, 378 insertions(+), 1 deletion(-)
diff --git a/Modules/main.c b/Modules/main.c
index 2684d23067..d04a4962b7 100644
--- a/Modules/main.c
+++ b/Modules/main.c
@@ -17,6 +17,10 @@
#endif
#ifdef MS_WINDOWS
# include <windows.h> // STATUS_CONTROL_C_EXIT
+# include <shlwapi.h>
+# include <string.h>
+# include <malloc.h>
+# include <libloaderapi.h>
#endif
/* End of includes for exit_sigint() */
@@ -680,10 +684,376 @@ Py_RunMain(void)
return exitcode;
}
+#ifdef MS_WINDOWS
+/* Please do not remove this function. It is needed for testing
+ CondaEcosystemModifyDllSearchPath(). */
+
+/*
+void LoadAndUnloadTestDLL(wchar_t* test_dll)
+{
+ wchar_t test_path[MAX_PATH + 1];
+ HMODULE hDLL = LoadLibraryExW(&test_dll[0], NULL, 0);
+ if (hDLL == NULL)
+ {
+ wchar_t err_msg[256];
+ DWORD err_code = GetLastError();
+ FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, err_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ err_msg, (sizeof(err_msg) / sizeof(wchar_t)), NULL);
+ fwprintf(stderr, L"LoadAndUnloadTestDLL() :: ERROR :: Failed to load %ls, error is: %ls\n", &test_dll[0], &err_msg[0]);
+ }
+ GetModuleFileNameW(hDLL, &test_path[0], MAX_PATH);
+ fwprintf(stderr, L"LoadAndUnloadTestDLL() :: %ls loaded from %ls\n", &test_dll[0], &test_path[0]);
+ if (FreeLibrary(hDLL) == 0)
+ {
+ wchar_t err_msg[256];
+ DWORD err_code = GetLastError();
+ FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, err_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ err_msg, (sizeof(err_msg) / sizeof(wchar_t)), NULL);
+ fwprintf(stderr, L"LoadAndUnloadTestDLL() :: ERROR :: Failed to free %ls, error is: %ls\n", &test_dll[0], &err_msg[0]);
+ }
+}
+*/
+
+/*
+ Provided CONDA_DLL_SEARCH_MODIFICATION_ENABLE is set (to anything at all!)
+ this function will modify the DLL search path so that C:\Windows\System32
+ does not appear before entries in PATH. If it does appear in PATH then it
+ gets added at the position it was in in PATH.
+
+ This is achieved via a call to SetDefaultDllDirectories() then calls to
+ AddDllDirectory() for each entry in PATH. We also take the opportunity to
+ clean-up these PATH entries such that any '/' are replaced with '\', no
+ double quotes occour and no PATH entry ends with '\'.
+
+ Caution: Microsoft's documentation says that the search order of entries
+ passed to AddDllDirectory is not respected and arbitrary. I do not think
+ this will be the case but it is worth bearing in mind.
+*/
+
+#if !defined(LOAD_LIBRARY_SEARCH_DEFAULT_DIRS)
+#define LOAD_LIBRARY_SEARCH_DEFAULT_DIRS 0x00001000
+#endif
+
+/* Caching of prior processed PATH environment */
+static wchar_t *sv_path_env = NULL;
+typedef void (WINAPI *SDDD)(DWORD DirectoryFlags);
+typedef void (WINAPI *SDD)(PCWSTR SetDir);
+typedef void (WINAPI *ADD)(PCWSTR NewDirectory);
+static SDDD pSetDefaultDllDirectories = NULL;
+static SDD pSetDllDirectory = NULL;
+static ADD pAddDllDirectory = NULL;
+static int sv_failed_to_find_dll_fns = 0;
+/* Have hidden this behind a define because it is clearly not code that
+ could be considered for upstreaming so clearly delimiting it makes it
+ easier to remove. */
+#define HARDCODE_CONDA_PATHS
+#if defined(HARDCODE_CONDA_PATHS)
+typedef struct
+{
+ wchar_t *p_relative;
+ wchar_t *p_name;
+} CONDA_PATH;
+
+#define NUM_CONDA_PATHS 5
+
+static CONDA_PATH condaPaths[NUM_CONDA_PATHS] =
+{
+ {L"Library\\mingw-w64\\bin", NULL},
+ {L"Library\\usr\\bin", NULL},
+ {L"Library\\bin", NULL},
+ {L"Scripts", NULL},
+ {L"bin", NULL}
+};
+#endif /* HARDCODE_CONDA_PATHS */
+static wchar_t sv_dll_dirname[1024];
+static wchar_t sv_windows_directory[1024];
+static wchar_t *sv_added_windows_directory = NULL;
+static wchar_t *sv_added_cwd = NULL;
+
+int CondaEcosystemModifyDllSearchPath_Init()
+{
+ int debug_it = _wgetenv(L"CONDA_DLL_SEARCH_MODIFICATION_DEBUG") ? 1 : 0;
+ wchar_t* enable = _wgetenv(L"CONDA_DLL_SEARCH_MODIFICATION_ENABLE");
+ int res = 0;
+#if defined(HARDCODE_CONDA_PATHS)
+ long long j;
+ CONDA_PATH *p_conda_path;
+#endif /* defined(HARDCODE_CONDA_PATHS) */
+ HMODULE dll_handle = NULL;
+
+ if (pSetDefaultDllDirectories == NULL)
+ {
+ wchar_t *conda_prefix = _wgetenv(L"CONDA_PREFIX");
+ wchar_t *build_prefix = _wgetenv(L"BUILD_PREFIX");
+ wchar_t *prefix = _wgetenv(L"PREFIX");
+ pSetDefaultDllDirectories = (SDDD)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "SetDefaultDllDirectories");
+ pSetDllDirectory = (SDD)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "SetDllDirectoryW");
+ pAddDllDirectory = (ADD)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "AddDllDirectory");
+
+ /* Determine sv_dll_dirname */
+ if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
+ (LPCSTR) &CondaEcosystemModifyDllSearchPath_Init, &dll_handle) == 0)
+ {
+ // Getting the pythonxx.dll path failed. Fall back to relative path of python.exe
+ // assuming that the executable that is running this code is python.exe
+ dll_handle = NULL;
+ }
+ GetModuleFileNameW(dll_handle, &sv_dll_dirname[0], sizeof(sv_dll_dirname)/sizeof(sv_dll_dirname[0])-1);
+ sv_dll_dirname[sizeof(sv_dll_dirname)/sizeof(sv_dll_dirname[0])-1] = L'\0';
+ if (wcsrchr(sv_dll_dirname, L'\\'))
+ *wcsrchr(sv_dll_dirname, L'\\') = L'\0';
+
+#if defined(HARDCODE_CONDA_PATHS)
+ for (p_conda_path = &condaPaths[0]; p_conda_path < &condaPaths[NUM_CONDA_PATHS]; ++p_conda_path)
+ {
+ size_t n_chars_dll_dirname = wcslen(sv_dll_dirname);
+ size_t n_chars_p_relative = wcslen(p_conda_path->p_relative);
+ p_conda_path->p_name = malloc(sizeof(wchar_t) * (n_chars_dll_dirname + n_chars_p_relative + 2));
+ wcsncpy(p_conda_path->p_name, sv_dll_dirname, n_chars_dll_dirname+1);
+ wcsncat(p_conda_path->p_name, L"\\", 2);
+ wcsncat(p_conda_path->p_name, p_conda_path->p_relative, n_chars_p_relative+1);
+ }
+#endif /* defined(HARDCODE_CONDA_PATHS) */
+
+ /* Determine sv_windows_directory */
+ {
+ char tmp_ascii[1024];
+ size_t convertedChars = 0;
+ GetWindowsDirectory(&tmp_ascii[0], sizeof(tmp_ascii) / sizeof(tmp_ascii[0]) - 1);
+ tmp_ascii[sizeof(tmp_ascii) / sizeof(tmp_ascii[0]) - 1] = L'\0';
+ mbstowcs_s(&convertedChars, sv_windows_directory, strlen(tmp_ascii)+1, tmp_ascii, _TRUNCATE);
+ sv_windows_directory[sizeof(sv_windows_directory) / sizeof(sv_windows_directory[0]) - 1] = L'\0';
+ }
+ }
+
+ if (pSetDefaultDllDirectories == NULL || pSetDllDirectory == NULL || pAddDllDirectory == NULL)
+ {
+ if (debug_it)
+ fwprintf(stderr, L"CondaEcosystemModifyDllSearchPath() :: WARNING :: Please install KB2533623 from http://go.microsoft.com/fwlink/p/?linkid=217865\n"\
+ L"CondaEcosystemModifyDllSearchPath() :: WARNING :: to improve conda ecosystem DLL isolation");
+ sv_failed_to_find_dll_fns = 1;
+ res = 2;
+ }
+#if defined(HARDCODE_CONDA_PATHS)
+ else if (enable == NULL || !wcscmp(enable, L"0")) {
+ for (j = NUM_CONDA_PATHS-1, p_conda_path = &condaPaths[NUM_CONDA_PATHS-1]; j > -1; --j, --p_conda_path)
+ {
+ if (debug_it)
+ fwprintf(stderr, L"CondaEcosystemModifyDllSearchPath() :: AddDllDirectory(%ls - ExePrefix)\n", p_conda_path->p_name);
+ pAddDllDirectory(p_conda_path->p_name);
+ }
+ }
+#endif /* defined(HARDCODE_CONDA_PATHS) */
+ return res;
+}
+
+int CondaEcosystemModifyDllSearchPath(int add_windows_directory, int add_cwd) {
+ int debug_it = _wgetenv(L"CONDA_DLL_SEARCH_MODIFICATION_DEBUG") ? 1 : 0;
+ const wchar_t *path_env = _wgetenv(L"PATH");
+ wchar_t current_working_directory[1024];
+ const wchar_t *p_cwd = NULL;
+ long long entry_num = 0;
+ long long i;
+ wchar_t **path_entries;
+ wchar_t *path_end;
+ long long num_entries = 1;
+#if defined(HARDCODE_CONDA_PATHS)
+ long long j;
+ CONDA_PATH *p_conda_path;
+ int foundCondaPath[NUM_CONDA_PATHS] = {0, 0, 0, 0, 0};
+#endif /* defined(HARDCODE_CONDA_PATHS) */
+ wchar_t *enable;
+
+ int SetDllDirectoryValue = LOAD_LIBRARY_SEARCH_DEFAULT_DIRS;
+ if (sv_failed_to_find_dll_fns)
+ return 1;
+
+ /* Fix for embedding the Python DLL. Courtesy of Shaun Walbridge
+ * if the CondaEcosystemModifyDllSearchPath_Init(argc, argv) code hasn't been run
+ * or failed to bind to the required functions in kernel32.dll, fail early to avoid
+ * an access violation. */
+ if (pSetDefaultDllDirectories == NULL || pSetDllDirectory == NULL || pAddDllDirectory == NULL)
+ return 1;
+
+ enable = _wgetenv(L"CONDA_DLL_SEARCH_MODIFICATION_ENABLE");
+ if (enable == NULL || !wcscmp(enable, L"0"))
+ return 0;
+ if (_wgetenv(L"CONDA_DLL_SEARCH_MODIFICATION_NEVER_ADD_WINDOWS_DIRECTORY"))
+ add_windows_directory = 0;
+ if (_wgetenv(L"CONDA_DLL_SEARCH_MODIFICATION_NEVER_ADD_CWD"))
+ add_cwd = 0;
+
+ if (add_cwd)
+ {
+ _wgetcwd(¤t_working_directory[0], (sizeof(current_working_directory)/sizeof(current_working_directory[0])) - 1);
+ current_working_directory[sizeof(current_working_directory)/sizeof(current_working_directory[0]) - 1] = L'\0';
+ p_cwd = ¤t_working_directory[0];
+ }
+
+ /* cache path to avoid multiple adds */
+ if (sv_path_env != NULL && path_env != NULL && !wcscmp(path_env, sv_path_env))
+ {
+ if ((add_windows_directory && sv_added_windows_directory != NULL) ||
+ (!add_windows_directory && sv_added_windows_directory == NULL) )
+ {
+ if ((p_cwd == NULL && sv_added_cwd == NULL) ||
+ p_cwd != NULL && sv_added_cwd != NULL && !wcscmp(p_cwd, sv_added_cwd))
+ {
+ if (_wgetenv(L"CONDA_DLL_SEARCH_MODIFICATION_NEVER_CACHE") == NULL)
+ {
+ if (debug_it)
+ fwprintf(stderr, L"CondaEcosystemModifyDllSearchPath() :: INFO :: Values unchanged\n");
+ return 0;
+ }
+ }
+ }
+ }
+ /* Something has changed.
+ Reset to default search order */
+ pSetDllDirectory(NULL);
+
+ if (sv_path_env != NULL)
+ {
+ free(sv_path_env);
+ }
+ sv_path_env = (path_env == NULL) ? NULL : _wcsdup(path_env);
+
+ if (path_env != NULL)
+ {
+ size_t len = wcslen(path_env);
+ wchar_t *path = (wchar_t *)alloca((len + 1) * sizeof(wchar_t));
+ if (debug_it)
+ fwprintf(stderr, L"CondaEcosystemModifyDllSearchPath() :: PATH=%ls\n\b", path_env);
+ memcpy(path, path_env, (len + 1) * sizeof(wchar_t));
+ /* Convert any / to \ */
+ /* Replace slash with backslash */
+ while ((path_end = wcschr(path, L'/')))
+ *path_end = L'\\';
+ /* Remove all double quotes */
+ while ((path_end = wcschr(path, L'"')))
+ memmove(path_end, path_end + 1, sizeof(wchar_t) * (len-- - (path_end - path)));
+ /* Remove all leading and double ';' */
+ while (*path == L';')
+ memmove(path, path + 1, sizeof(wchar_t) * len--);
+ while ((path_end = wcsstr(path, L";;")))
+ memmove(path_end, path_end + 1, sizeof(wchar_t) * (len-- - (path_end - path)));
+ /* Remove trailing ;'s */
+ while(path[len-1] == L';')
+ path[len-- - 1] = L'\0';
+
+ if (len == 0)
+ return 2;
+
+ /* Count the number of path entries */
+ path_end = path;
+ while ((path_end = wcschr(path_end, L';')))
+ {
+ ++num_entries;
+ ++path_end;
+ }
+
+ path_entries = (wchar_t **)alloca((num_entries) * sizeof(wchar_t *));
+ path_end = wcschr(path, L';');
+
+ if (getenv("CONDA_DLL_SET_DLL_DIRECTORY_VALUE") != NULL)
+ SetDllDirectoryValue = atoi(getenv("CONDA_DLL_SET_DLL_DIRECTORY_VALUE"));
+ pSetDefaultDllDirectories(SetDllDirectoryValue);
+ while (path != NULL)
+ {
+ if (path_end != NULL)
+ {
+ *path_end = L'\0';
+ /* Hygiene, no \ at the end */
+ while (path_end > path && path_end[-1] == L'\\')
+ {
+ --path_end;
+ *path_end = L'\0';
+ }
+ }
+ if (wcslen(path) != 0)
+ path_entries[entry_num++] = path;
+ path = path_end;
+ if (path != NULL)
+ {
+ while (*path == L'\0')
+ ++path;
+ path_end = wcschr(path, L';');
+ }
+ }
+ for (i = num_entries - 1; i > -1; --i)
+ {
+#if defined(HARDCODE_CONDA_PATHS)
+ for (j = 0, p_conda_path = &condaPaths[0]; p_conda_path < &condaPaths[NUM_CONDA_PATHS]; ++j, ++p_conda_path)
+ {
+ if (!foundCondaPath[j] && !wcscmp(path_entries[i], p_conda_path->p_name))
+ {
+ foundCondaPath[j] = 1;
+ break;
+ }
+ }
+#endif /* defined(HARDCODE_CONDA_PATHS) */
+ if (debug_it)
+ fwprintf(stderr, L"CondaEcosystemModifyDllSearchPath() :: AddDllDirectory(%ls)\n", path_entries[i]);
+ pAddDllDirectory(path_entries[i]);
+ }
+ }
+
+#if defined(HARDCODE_CONDA_PATHS)
+ if (_wgetenv(L"CONDA_DLL_SEARCH_MODIFICATION_DO_NOT_ADD_EXEPREFIX") == NULL)
+ {
+ for (j = NUM_CONDA_PATHS-1, p_conda_path = &condaPaths[NUM_CONDA_PATHS-1]; j > -1; --j, --p_conda_path)
+ {
+ if (debug_it)
+ fwprintf(stderr, L"CondaEcosystemModifyDllSearchPath() :: p_conda_path->p_name = %ls, foundCondaPath[%zd] = %d\n", p_conda_path->p_name, j, foundCondaPath[j]);
+ if (!foundCondaPath[j])
+ {
+ if (debug_it)
+ fwprintf(stderr, L"CondaEcosystemModifyDllSearchPath() :: AddDllDirectory(%ls - ExePrefix)\n", p_conda_path->p_name);
+ pAddDllDirectory(p_conda_path->p_name);
+ }
+ }
+ }
+#endif /* defined(HARDCODE_CONDA_PATHS) */
+
+ if (p_cwd)
+ {
+ if (sv_added_cwd != NULL && wcscmp(p_cwd, sv_added_cwd))
+ {
+ free(sv_added_cwd);
+ }
+ sv_added_cwd = _wcsdup(p_cwd);
+ if (debug_it)
+ fwprintf(stderr, L"CondaEcosystemModifyDllSearchPath() :: AddDllDirectory(%ls - CWD)\n", sv_added_cwd);
+ pAddDllDirectory(sv_added_cwd);
+ }
+
+ if (add_windows_directory)
+ {
+ sv_added_windows_directory = &sv_windows_directory[0];
+ if (debug_it)
+ fwprintf(stderr, L"CondaEcosystemModifyDllSearchPath() :: AddDllDirectory(%ls - WinDir)\n", sv_windows_directory);
+ pAddDllDirectory(sv_windows_directory);
+ }
+ else
+ {
+ sv_added_windows_directory = NULL;
+ }
+
+ return 0;
+}
+#endif
+
static int
pymain_main(_PyArgv *args)
{
+#ifdef MS_WINDOWS
+ /* LoadAndUnloadTestDLL(L"libiomp5md.dll"); */
+ CondaEcosystemModifyDllSearchPath_Init(args->argc, args->wchar_argv);
+ /* LoadAndUnloadTestDLL(L"libiomp5md.dll"); */
+#endif
PyStatus status = pymain_init(args);
if (_PyStatus_IS_EXIT(status)) {
pymain_free();
diff --git a/Python/dynload_win.c b/Python/dynload_win.c
index 5702ab2cd7..e2efd4c8ea 100644
--- a/Python/dynload_win.c
+++ b/Python/dynload_win.c
@@ -190,6 +190,10 @@ dl_funcptr _PyImport_FindSharedFuncptrWindows(const char *prefix,
to avoid DLL preloading attacks and enable use of the
AddDllDirectory function. We add SEARCH_DLL_LOAD_DIR to
ensure DLLs adjacent to the PYD are preferred. */
+ /* This resyncs values in PATH to AddDllDirectory() */
+ extern int CondaEcosystemModifyDllSearchPath(int, int);
+ CondaEcosystemModifyDllSearchPath(1, 1);
+
Py_BEGIN_ALLOW_THREADS
hDLL = LoadLibraryExW(wpathname, NULL,
LOAD_LIBRARY_SEARCH_DEFAULT_DIRS |
diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c
index eeaf20b461..a32aa666e4 100644
--- a/Python/pylifecycle.c
+++ b/Python/pylifecycle.c
@@ -101,7 +101,10 @@ _PyRuntime_Initialize(void)
return _PyStatus_OK();
}
runtime_initialized = 1;
-
+#ifdef MS_WINDOWS
+ extern int CondaEcosystemModifyDllSearchPath_Init();
+ CondaEcosystemModifyDllSearchPath_Init();
+#endif
return _PyRuntimeState_Init(&_PyRuntime);
}
--
2.32.1 (Apple Git-133)