[PATCH v2 17/22] kernel32/tests: Add tests for loader locking during library load and unload.
Paul Gofman
pgofman at codeweavers.com
Tue Oct 5 17:49:23 CDT 2021
Signed-off-by: Paul Gofman <pgofman at codeweavers.com>
---
dlls/kernel32/tests/Makefile.in | 2 +
dlls/kernel32/tests/actctx.c | 2 +-
dlls/kernel32/tests/loader.c | 207 +++++++++++++++++++++++++++
dlls/kernel32/tests/locking_dll.c | 45 ++++++
dlls/kernel32/tests/locking_dll.spec | 1 +
5 files changed, 256 insertions(+), 1 deletion(-)
create mode 100644 dlls/kernel32/tests/locking_dll.c
create mode 100644 dlls/kernel32/tests/locking_dll.spec
diff --git a/dlls/kernel32/tests/Makefile.in b/dlls/kernel32/tests/Makefile.in
index e9516603ce9..be3a8fc8dfa 100644
--- a/dlls/kernel32/tests/Makefile.in
+++ b/dlls/kernel32/tests/Makefile.in
@@ -21,6 +21,8 @@ SOURCES = \
heap.c \
loader.c \
locale.c \
+ locking_dll.c \
+ locking_dll.spec \
mailslot.c \
module.c \
path.c \
diff --git a/dlls/kernel32/tests/actctx.c b/dlls/kernel32/tests/actctx.c
index 163ea405222..f37796a6aba 100644
--- a/dlls/kernel32/tests/actctx.c
+++ b/dlls/kernel32/tests/actctx.c
@@ -2584,7 +2584,7 @@ static void delete_manifest_file(const char *filename)
DeleteFileA(path);
}
-static void extract_resource(const char *name, const char *type, const char *path)
+void extract_resource(const char *name, const char *type, const char *path)
{
DWORD written;
HANDLE file;
diff --git a/dlls/kernel32/tests/loader.c b/dlls/kernel32/tests/loader.c
index e97c114c5e9..9f502aaf857 100644
--- a/dlls/kernel32/tests/loader.c
+++ b/dlls/kernel32/tests/loader.c
@@ -92,6 +92,8 @@ static BOOL (WINAPI *pWow64DisableWow64FsRedirection)(void **);
static BOOL (WINAPI *pWow64RevertWow64FsRedirection)(void *);
static HMODULE (WINAPI *pLoadPackagedLibrary)(LPCWSTR lpwLibFileName, DWORD Reserved);
+void extract_resource(const char *name, const char *type, const char *path);
+
static PVOID RVAToAddr(DWORD_PTR rva, HMODULE module)
{
if (rva == 0)
@@ -4043,6 +4045,7 @@ static void test_Wow64Transition(void)
static BOOL test_loader_lock_repeat_lock;
static unsigned int test_loader_notification_count;
static const WCHAR *ldr_notify_track_dll;
+static HMODULE lock_dll_handle;
static HANDLE lock_ready_event, next_test_event;
static BOOL test_loader_lock_abort_test;
static volatile LONG test_loader_lock_timeout_count;
@@ -4061,9 +4064,13 @@ static void CALLBACK test_loader_lock_module_enum_locking_callback(LDR_DATA_TABL
static DWORD WINAPI test_loader_lock_thread(void *param)
{
+ static const WCHAR env_var[] = L"test_wait_reason_value";
ULONG_PTR magic;
NTSTATUS status;
+ HMODULE hmodule;
+ WCHAR str[32];
DWORD result;
+ BOOL bret;
WaitForSingleObject(next_test_event, INFINITE);
SetEvent(lock_ready_event);
@@ -4109,6 +4116,67 @@ static DWORD WINAPI test_loader_lock_thread(void *param)
SetEvent(lock_ready_event);
WaitForSingleObject(next_test_event, INFINITE);
+ /* 3. Test with the thread blocked in DLL entry point during process attach. */
+ swprintf(str, ARRAY_SIZE(str), L"%p", &test_loader_lock_timeout_count);
+ bret = SetEnvironmentVariableW(L"test_timeout_count_addr", str);
+ ok(bret, "SetEnvironmentVariableW failed.\n");
+
+ swprintf(str, ARRAY_SIZE(str), L"%u", DLL_PROCESS_ATTACH);
+ bret = SetEnvironmentVariableW(env_var, str);
+ ok(bret, "SetEnvironmentVariableW failed.\n");
+ do
+ {
+ hmodule = LoadLibraryA("lock.dll");
+ ok(!hmodule, "LoadLibrary succeeded.\n");
+ ok(GetLastError() == ERROR_DLL_INIT_FAILED, "Got unexpected error %u.\n", GetLastError());
+ if (test_loader_lock_timeout_count)
+ {
+ WaitForSingleObject(next_test_event, INFINITE);
+ test_loader_lock_timeout_count = 0;
+ }
+ } while (test_loader_lock_repeat_lock);
+
+ /* A reference was added for the dll during process attach. */
+ if (lock_dll_handle)
+ {
+ bret = FreeLibrary(lock_dll_handle);
+ ok(bret, "FreeLibrary failed, err %u.\n", GetLastError());
+ bret = FreeLibrary(lock_dll_handle);
+ ok(!bret, "FreeLibrary succeeded.\n");
+ ok(GetLastError() == ERROR_MOD_NOT_FOUND, "Got unexpected error %u.\n", GetLastError());
+ }
+
+ SetEvent(lock_ready_event);
+ WaitForSingleObject(next_test_event, INFINITE);
+
+ /* 4. Test with the thread blocked in DLL entry point during process detach. */
+ swprintf(str, ARRAY_SIZE(str), L"%u", DLL_PROCESS_DETACH);
+ bret = SetEnvironmentVariableW(env_var, str);
+ ok(bret, "SetEnvironmentVariableW failed.\n");
+ do
+ {
+ lock_dll_handle = hmodule = LoadLibraryA("lock.dll");
+ ok(!!hmodule, "LoadLibrary failed, err %u.\n", GetLastError());
+
+ bret = FreeLibrary(hmodule);
+ ok(bret, "FreeLibrary failed, err %u.\n", GetLastError());
+
+ lock_dll_handle = NULL;
+
+ if (test_loader_lock_timeout_count)
+ {
+ WaitForSingleObject(next_test_event, INFINITE);
+ test_loader_lock_timeout_count = 0;
+ }
+ } while (test_loader_lock_repeat_lock);
+
+ bret = FreeLibrary(hmodule);
+ ok(!bret, "FreeLibrary succeeded.\n");
+ ok(GetLastError() == ERROR_MOD_NOT_FOUND, "Got unexpected error %u.\n", GetLastError());
+
+ SetEvent(lock_ready_event);
+ WaitForSingleObject(next_test_event, INFINITE);
+
SetEvent(lock_ready_event);
return 0;
}
@@ -4154,13 +4222,18 @@ static void test_loader_lock(void)
{
static const WCHAR not_loaded_dll_name[] = L"authz.dll";
static const WCHAR preloaded_dll_name[] = L"winmm.dll";
+ BOOL blocks_on_load_in_progress_module = FALSE;
+ BOOL blocks_on_decref_library = FALSE;
HMODULE hmodule_preloaded, hmodule;
ULONG_PTR magic;
NTSTATUS status;
HANDLE thread;
void *cookie;
+ void *proc;
BOOL bret;
+ extract_resource("locking_dll.dll", "TESTDLL", "lock.dll");
+
lock_ready_event = CreateEventA(NULL, FALSE, FALSE, "test_lock_ready_event");
next_test_event = CreateEventA(NULL, FALSE, FALSE, "test_next_test_event");
@@ -4233,6 +4306,10 @@ static void test_loader_lock(void)
ok(!status, "Got unexpected status %#x.\n", status);
check_timeout(FALSE);
+ hmodule = GetModuleHandleW(preloaded_dll_name);
+ ok(!!hmodule, "LoadLibrary failed, err %u.\n", GetLastError());
+ check_timeout(FALSE);
+
if (BLOCKING_TESTS_ENABLED)
{
status = pLdrLockLoaderLock(0, NULL, &magic);
@@ -4246,9 +4323,139 @@ static void test_loader_lock(void)
LdrUnregisterDllNotification( cookie );
check_timeout(FALSE);
+ /* 3. Test with the thread blocked in DLL entry point during process attach.
+ * DLL entry point returns failure. */
+ trace("Test 3.\n");
test_loader_lock_next_test();
+ status = LdrRegisterDllNotification(0, test_loader_lock_ldr_notify, NULL, &cookie);
+ ok(!status, "Got unexpected status %#x.\n", status);
+ check_timeout(FALSE);
+
+ bret = GetModuleHandleExW(0, preloaded_dll_name, &hmodule);
+ check_timeout(FALSE);
+ ok(bret, "GetModuleHandleEx failed, err %u.\n", GetLastError());
+ ok(hmodule == hmodule_preloaded, "Got unexpected hmodule %p, expected %p.\n", hmodule, hmodule_preloaded);
+
+ status = pLdrUnloadDll(hmodule);
+ ok(!status, "Got unexpected status %#x.\n", status);
+ if (test_loader_lock_timeout_count)
+ {
+ /* Win10 1507. */
+ check_timeout(TRUE);
+ blocks_on_decref_library = TRUE;
+ win_skip("LdrUnloadDll() timed out for unlrelated library without unload.\n");
+ }
+ else
+ {
+ check_timeout(FALSE);
+ }
+
+ hmodule = GetModuleHandleA("lock.dll");
+ ok(!!hmodule || broken(!hmodule && GetLastError() == ERROR_MOD_NOT_FOUND) /* before Win10 1607 */,
+ "GetModuleHandleA hmodule %p, err %u.\n", hmodule, GetLastError());
+ check_timeout(!hmodule);
+
+ if (hmodule)
+ {
+ bret = GetModuleHandleExW(0, L"lock.dll", &hmodule);
+ check_timeout(FALSE);
+ ok(bret, "GetModuleHandleEx failed, err %u.\n", GetLastError());
+
+ lock_dll_handle = hmodule;
+ bret = FreeLibrary(hmodule);
+ check_timeout(blocks_on_decref_library);
+ ok(bret, "FreeLibrary failed, err %u.\n", GetLastError());
+
+ if (BLOCKING_TESTS_ENABLED)
+ {
+ ok(!!lock_dll_handle, "Got NULL lock_dll_handle.\n");
+ proc = GetProcAddress(lock_dll_handle, "unknown");
+ ok(!proc, "GetProcAddress succeeded.\n");
+ check_timeout(TRUE);
+ }
+ }
+ else
+ {
+ /* Win8. */
+ blocks_on_load_in_progress_module = TRUE;
+ win_skip("GetModuleHandleA failed for DLL being loaded.\n");
+ }
+
+ hmodule = LoadLibraryW(preloaded_dll_name);
+ check_timeout(FALSE);
+ ok(!!hmodule, "LoadLibrary failed, err %u.\n", GetLastError());
+
+ if (!blocks_on_decref_library)
+ {
+ bret = FreeLibrary(hmodule);
+ ok(bret, "FreeLibrary failed, err %u.\n", GetLastError());
+ check_timeout(FALSE);
+ }
+
+ if (!blocks_on_load_in_progress_module)
+ {
+ status = LdrAddRefDll(0, lock_dll_handle);
+ check_timeout(FALSE);
+ ok(!status, "Got unexpected status %#x.\n", status);
+ /* Leaving an extra reference to lock.dll which will fail in process attach. */
+ }
+
+ LdrUnregisterDllNotification( cookie );
+ check_timeout(FALSE);
+
+ /* 4. Test with the thread blocked in DLL entry point during process detach. */
+ trace("Test 4.\n");
+ test_loader_lock_next_test();
+
+ status = LdrRegisterDllNotification(0, test_loader_lock_ldr_notify, NULL, &cookie);
+ ok(!status, "Got unexpected status %#x.\n", status);
+ check_timeout(FALSE);
+
+ bret = GetModuleHandleExW(0, preloaded_dll_name, &hmodule);
+ ok(bret, "GetModuleHandleEx failed, err %u.\n", GetLastError());
+ ok(hmodule == hmodule_preloaded, "Got unexpected hmodule %p, expected %p.\n", hmodule, hmodule_preloaded);
+ check_timeout(FALSE);
+
+ if (!blocks_on_decref_library)
+ {
+ bret = FreeLibrary(hmodule);
+ ok(bret, "FreeLibrary failed, err %u.\n", GetLastError());
+ check_timeout(FALSE);
+ }
+
+ ok(!!lock_dll_handle, "Got NULL lock_dll_handle.\n");
+
+ status = LdrAddRefDll(0, lock_dll_handle);
+ check_timeout(FALSE);
+ ok(status == STATUS_DLL_NOT_FOUND, "Got unexpected status %#x.\n", status);
+
+ if (BLOCKING_TESTS_ENABLED)
+ {
+ hmodule = GetModuleHandleW(L"lock.dll");
+ check_timeout(!blocks_on_load_in_progress_module);
+ ok(!hmodule, "GetModuleHandleW succeeded.\n", GetLastError());
+ ok(GetLastError() == ERROR_MOD_NOT_FOUND, "Got unexpected error %u.\n", GetLastError());
+ }
+
+ LdrUnregisterDllNotification( cookie );
+ check_timeout(FALSE);
+
+ test_loader_lock_next_test();
+
+ if (blocks_on_decref_library)
+ {
+ /* Cleanup. */
+ bret = FreeLibrary(hmodule_preloaded);
+ ok(bret, "FreeLibrary failed, err %u.\n", GetLastError());
+
+ bret = FreeLibrary(hmodule_preloaded);
+ ok(bret, "FreeLibrary failed, err %u.\n", GetLastError());
+ }
+
done:
+ DeleteFileA("lock.dll");
+
WaitForSingleObject(thread, INFINITE);
CloseHandle(thread);
diff --git a/dlls/kernel32/tests/locking_dll.c b/dlls/kernel32/tests/locking_dll.c
new file mode 100644
index 00000000000..5384fae2ea3
--- /dev/null
+++ b/dlls/kernel32/tests/locking_dll.c
@@ -0,0 +1,45 @@
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "ntstatus.h"
+#define WIN32_NO_STATUS
+#include "windef.h"
+#include "winbase.h"
+#include "winternl.h"
+
+BOOL WINAPI DllMain( HINSTANCE instance, DWORD reason, LPVOID reserved )
+{
+ LONG *test_loader_lock_timeout_count = NULL;
+ HANDLE lock_ready_event, next_test_event;
+ DWORD wait_reason = ~0u;
+ DWORD result;
+ WCHAR str[32];
+
+ if (GetEnvironmentVariableW(L"test_wait_reason_value", str, ARRAY_SIZE(str)))
+ wait_reason = _wtoi(str);
+
+ if (reason != wait_reason) return TRUE;
+
+ lock_ready_event = OpenEventA(EVENT_ALL_ACCESS, FALSE, "test_lock_ready_event");
+ next_test_event = OpenEventA(EVENT_ALL_ACCESS, FALSE, "test_next_test_event");
+
+ if (GetEnvironmentVariableW(L"test_timeout_count_addr", str, ARRAY_SIZE(str)))
+ swscanf(str, L"%p", &test_loader_lock_timeout_count);
+
+ SetEvent( lock_ready_event );
+ result = WaitForSingleObject(next_test_event, 2000);
+
+ if (result == WAIT_TIMEOUT)
+ {
+ if (test_loader_lock_timeout_count)
+ ++*test_loader_lock_timeout_count;
+ }
+
+ CloseHandle(lock_ready_event);
+ CloseHandle(next_test_event);
+ return reason != DLL_PROCESS_ATTACH;
+}
+
+void WINAPI test_proc(void)
+{
+}
diff --git a/dlls/kernel32/tests/locking_dll.spec b/dlls/kernel32/tests/locking_dll.spec
new file mode 100644
index 00000000000..e3121f28aae
--- /dev/null
+++ b/dlls/kernel32/tests/locking_dll.spec
@@ -0,0 +1 @@
+@ stdcall test_proc()
--
2.31.1
More information about the wine-devel
mailing list