[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