Commit ebd5ea8ce5 for openssl.org
commit ebd5ea8ce5788ab922884f42402a036b5b15322e
Author: Neil Horman <nhorman@openssl.org>
Date: Fri Apr 3 09:35:00 2026 -0400
Make win32_pathbyaddr more reliable
A user has reported that win32_pathbyaddr can be unreliable in
multithreaded environments. See:
https://learn.microsoft.com/en-us/windows/win32/api/tlhelp32/nf-tlhelp32-createtoolhelp32snapshot
Specifically they have observed the following behavior, as noted in the
above article:
When taking snapshots that include heaps and modules for a process other than
the current process, the CreateToolhelp32Snapshot function can fail or return
incorrect information for a variety of reasons. For example, if the loader data
table in the target process is corrupted or not initialized, or if the module
list changes during the function call as a result of DLLs being loaded or
unloaded, the function might fail with ERROR_BAD_LENGTH or other error code.
Ensure that the target process was not started in a suspended state, and try
calling the function again. If the function fails with ERROR_BAD_LENGTH when
called with TH32CS_SNAPMODULE or TH32CS_SNAPMODULE32, call the function again
until it succeeds.
This behavior necessitates calling DSO_pathbyaddr mutiple times to get a
succesful return code.
win32_pathbyaddr can be made more reliable, avoiding the need for multiple calls
by using alternate windows apis that are not/less succeptible to these transient
errors in multithreaded environments.
refactor win32_pathbyaddr here to implement that increased reliability.
Reviewed-by: Saša NedvÄ›dický <sashan@openssl.org>
Reviewed-by: Eugene Syromiatnikov <esyr@openssl.org>
MergeDate: Sun Apr 26 11:48:58 2026
(Merged from https://github.com/openssl/openssl/pull/30705)
diff --git a/crypto/dso/dso_win32.c b/crypto/dso/dso_win32.c
index 908a3bbdc7..01bb2e328e 100644
--- a/crypto/dso/dso_win32.c
+++ b/crypto/dso/dso_win32.c
@@ -485,106 +485,66 @@ typedef BOOL(WINAPI *MODULE32)(HANDLE, MODULEENTRY32 *);
static int win32_pathbyaddr(void *addr, char *path, int sz)
{
- HMODULE dll;
- HANDLE hModuleSnap = INVALID_HANDLE_VALUE;
- MODULEENTRY32 me32;
- CREATETOOLHELP32SNAPSHOT create_snap;
- CLOSETOOLHELP32SNAPSHOT close_snap;
- MODULE32 module_first, module_next;
-
- if (addr == NULL) {
- union {
- int (*f)(void *, char *, int);
- void *p;
- } t = {
- win32_pathbyaddr
- };
- addr = t.p;
+ HMODULE hModule = NULL;
+ const DWORD wpathSize = 32768; /* 32768 is the maximum possible path length on Windows */
+ WCHAR *wpath = NULL;
+ DWORD wlen, wsz;
+ int utf8len = -1;
+
+ if (addr == NULL)
+ addr = win32_pathbyaddr;
+
+ if (!GetModuleHandleExW(
+ GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
+ (LPCWSTR)addr, &hModule)) {
+ ERR_raise_data(ERR_LIB_DSO, DSO_R_SYM_FAILURE, "Unable to get module handle (%lu)\n",
+ GetLastError());
+ goto out;
+ }
+ wpath = (WCHAR *)OPENSSL_malloc(wpathSize * sizeof(WCHAR));
+ if (wpath == NULL) {
+ ERR_raise_data(ERR_LIB_DSO, DSO_R_NULL_HANDLE, "Path allocation failure (%lu)\n",
+ GetLastError());
+ goto out;
+ }
+ wlen = GetModuleFileNameW(hModule, wpath, wpathSize);
+ if (wlen == 0 || GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
+ ERR_raise_data(ERR_LIB_DSO, DSO_R_NAME_TRANSLATION_FAILED, "Module name fetch failed (%lu)\n",
+ GetLastError());
+ goto out;
}
- dll = LoadLibrary(TEXT(DLLNAME));
- if (dll == NULL) {
- ERR_raise(ERR_LIB_DSO, DSO_R_UNSUPPORTED);
- return -1;
- }
-
- create_snap = (CREATETOOLHELP32SNAPSHOT)
- GetProcAddress(dll, "CreateToolhelp32Snapshot");
- if (create_snap == NULL) {
- FreeLibrary(dll);
- ERR_raise(ERR_LIB_DSO, DSO_R_UNSUPPORTED);
- return -1;
- }
- /* We take the rest for granted... */
-#ifdef _WIN32_WCE
- close_snap = (CLOSETOOLHELP32SNAPSHOT)
- GetProcAddress(dll, "CloseToolhelp32Snapshot");
-#else
- close_snap = (CLOSETOOLHELP32SNAPSHOT)CloseHandle;
-#endif
- module_first = (MODULE32)GetProcAddress(dll, "Module32First");
- module_next = (MODULE32)GetProcAddress(dll, "Module32Next");
-
/*
- * Take a snapshot of current process which includes
- * list of all involved modules.
+ * If we pass a size of 0 or less, invoke the size-query pattern,
+ * in which we do not actually copy the name to the path buffer,
+ * but return the size the path buffer needs to be for this object
*/
- hModuleSnap = (*create_snap)(TH32CS_SNAPMODULE, 0);
- if (hModuleSnap == INVALID_HANDLE_VALUE) {
- FreeLibrary(dll);
- ERR_raise(ERR_LIB_DSO, DSO_R_UNSUPPORTED);
- return -1;
- }
-
- me32.dwSize = sizeof(me32);
-
- if (!(*module_first)(hModuleSnap, &me32)) {
- (*close_snap)(hModuleSnap);
- FreeLibrary(dll);
- ERR_raise(ERR_LIB_DSO, DSO_R_FAILURE);
- return -1;
+ if (sz <= 0) {
+ utf8len = (int)(wlen + 1);
+ goto out;
}
- /* Enumerate the modules to find one which includes me. */
- do {
- if ((size_t)addr >= (size_t)me32.modBaseAddr && (size_t)addr < (size_t)(me32.modBaseAddr + me32.modBaseSize)) {
- (*close_snap)(hModuleSnap);
- FreeLibrary(dll);
-#ifdef _WIN32_WCE
-#if _WIN32_WCE >= 101
- return WideCharToMultiByte(CP_ACP, 0, me32.szExePath, -1,
- path, sz, NULL, NULL);
-#else
- {
- int i, len = (int)wcslen(me32.szExePath);
- if (sz <= 0)
- return len + 1;
- if (len >= sz)
- len = sz - 1;
- for (i = 0; i < len; i++)
- path[i] = (char)me32.szExePath[i];
- path[len++] = '\0';
- return len;
- }
-#endif
-#else
- {
- int len = (int)strlen(me32.szExePath);
- if (sz <= 0)
- return len + 1;
- if (len >= sz)
- len = sz - 1;
- memcpy(path, me32.szExePath, len);
- path[len++] = '\0';
- return len;
- }
-#endif
- }
- } while ((*module_next)(hModuleSnap, &me32));
-
- (*close_snap)(hModuleSnap);
- FreeLibrary(dll);
- return 0;
+ /*
+ * Convert the wide path to UTF-8
+ */
+ wsz = (DWORD)sz;
+ utf8len = WideCharToMultiByte(CP_UTF8, 0, wpath, -1, NULL, 0, NULL, NULL);
+ if (utf8len <= 0 || (DWORD)utf8len > wsz) {
+ ERR_raise_data(ERR_LIB_DSO, DSO_R_NAME_TRANSLATION_FAILED, "UTF8 query failed (%lu)\n",
+ GetLastError());
+ goto out;
+ }
+
+ if (WideCharToMultiByte(CP_UTF8, 0, wpath, -1, path, wsz, NULL, NULL) <= 0) {
+ ERR_raise_data(ERR_LIB_DSO, DSO_R_NAME_TRANSLATION_FAILED, "UTF8 translation failed (%lu)\n",
+ GetLastError());
+ goto out;
+ }
+out:
+ OPENSSL_free(wpath);
+ if (hModule != NULL)
+ CloseHandle(hModule);
+ return utf8len;
}
static void *win32_globallookup(const char *name)