[PATCH 22/22] kernel32/tests: Add tests for loader locking.

Paul Gofman pgofman at codeweavers.com
Fri Oct 1 13:24:05 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         | 579 +++++++++++++++++++++++++++
 dlls/kernel32/tests/locking_dll.c    |  50 +++
 dlls/kernel32/tests/locking_dll.spec |   1 +
 5 files changed, 633 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 4f1b11338a6..df530055db4 100644
--- a/dlls/kernel32/tests/loader.c
+++ b/dlls/kernel32/tests/loader.c
@@ -70,6 +70,10 @@ static NTSTATUS (WINAPI *pLdrLockLoaderLock)(ULONG, ULONG *, ULONG_PTR *);
 static NTSTATUS (WINAPI *pLdrUnlockLoaderLock)(ULONG, ULONG_PTR);
 static NTSTATUS (WINAPI *pLdrLoadDll)(LPCWSTR,DWORD,const UNICODE_STRING *,HMODULE*);
 static NTSTATUS (WINAPI *pLdrUnloadDll)(HMODULE);
+
+typedef void  (CALLBACK *LDRENUMPROC)(LDR_DATA_TABLE_ENTRY *, void *, BOOLEAN *);
+static NTSTATUS (WINAPI *pLdrEnumerateLoadedModules)( void *unknown, LDRENUMPROC callback, void *context );
+
 static void (WINAPI *pRtlInitUnicodeString)(PUNICODE_STRING,LPCWSTR);
 static void (WINAPI *pRtlAcquirePebLock)(void);
 static void (WINAPI *pRtlReleasePebLock)(void);
@@ -88,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)
@@ -4036,6 +4042,574 @@ static void test_Wow64Transition(void)
             debugstr_wn(name->SectionFileName.Buffer, name->SectionFileName.Length / sizeof(WCHAR)));
 }
 
+static BOOL test_loader_lock_repeat_lock;
+static DWORD test_loader_lock_expected_wait_result;
+static unsigned int test_loader_notification_count;
+static unsigned int test_loader_expected_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_test_failed;
+static volatile LONG test_loader_lock_timeout_count;
+
+#define BLOCKING_TESTS_ENABLED 1
+
+static void CALLBACK test_loader_lock_module_enum_locking_callback(LDR_DATA_TABLE_ENTRY *mod,
+        void *context, BOOLEAN *stop)
+{
+    DWORD *result = context;
+
+    SetEvent(lock_ready_event);
+    *result = WaitForSingleObject(next_test_event, 3000);
+    *stop = TRUE;
+}
+
+static void CALLBACK test_loader_lock_dummy_callback(LDR_DATA_TABLE_ENTRY *mod,
+        void *context, BOOLEAN *stop)
+{
+    *stop = TRUE;
+}
+
+static void CALLBACK test_loader_lock_ldr_notify_locking(ULONG reason, LDR_DLL_NOTIFICATION_DATA *data, void *context)
+{
+    DWORD *result = context;
+    NTSTATUS status;
+    HMODULE hmodule;
+    void *cookie;
+    BOOL bret;
+
+    if (reason != LDR_DLL_NOTIFICATION_REASON_UNLOADED) return;
+
+    status = LdrRegisterDllNotification(0, test_loader_lock_ldr_notify_locking, NULL, &cookie);
+    ok(!status, "Got unexpected status %#x.\n", status);
+    status = LdrUnregisterDllNotification( cookie );
+    ok(!status, "Got unexpected status %#x.\n", status);
+
+    hmodule = LoadLibraryW(L"winmm.dll");
+    ok(!!hmodule, "GetModuleHandleW failed, err %u.\n", GetLastError());
+    bret = FreeLibrary(hmodule);
+    ok(bret, "FreeLibrary failed, err %u.\n", GetLastError());
+
+    bret = GetModuleHandleExW(0, L"lock.dll", &hmodule);
+    ok(bret, "GetModuleHandleExW failed, err %u.\n", GetLastError());
+    ok(!!hmodule, "GetModuleHandleW failed, err %u.\n", GetLastError());
+    bret = FreeLibrary(hmodule);
+    ok(bret, "FreeLibrary failed, err %u.\n", GetLastError());
+
+    hmodule = LoadLibraryW(L"authz.dll");
+    ok(!hmodule, "LoadLibraryW succeeded.\n");
+    ok(GetLastError() == ERROR_NOT_FOUND, "Got unexpected error %u.\n", GetLastError());
+
+    status = pLdrEnumerateLoadedModules(NULL, test_loader_lock_dummy_callback, NULL);
+    ok(!status, "Got unexpected status %#x.\n", status);
+
+    SetEvent(lock_ready_event);
+    *result = WaitForSingleObject(next_test_event, 3000);
+}
+
+static DWORD WINAPI test_loader_lock_thread(void *param)
+{
+    static const WCHAR env_var[] = L"test_wait_reason_value";
+    void (WINAPI *p_set_lock_result_addr)( DWORD *addr );
+    unsigned int test, iter;
+    ULONG_PTR magic;
+    NTSTATUS status;
+    HMODULE hmodule;
+    void *cookie;
+    DWORD result;
+    WCHAR str[8];
+    BOOL bret;
+
+    WaitForSingleObject(next_test_event, INFINITE);
+
+    test = 1;
+    /* 1. Test with loader lock held. */
+    iter = 0;
+    do
+    {
+        status = pLdrLockLoaderLock(0, NULL, &magic);
+        ok(!status, "Got unexpected status %#x.\n", status);
+        SetEvent(lock_ready_event);
+
+        result = WaitForSingleObject(next_test_event, 3000);
+
+        if (!iter && result == WAIT_TIMEOUT)
+        {
+            /* First test timed out, likely the old loader. */
+            test_loader_lock_test_failed = TRUE;
+            status = pLdrUnlockLoaderLock(0, magic);
+            ok(!status, "Got unexpected status %#x.\n", status);
+            SetEvent(lock_ready_event);
+            return 0;
+        }
+
+        ok(result == test_loader_lock_expected_wait_result,
+                "Got unexpected wait result %#x, expected %#x, test %u.%u.\n",
+                result, test_loader_lock_expected_wait_result, test, iter);
+
+        todo_wine_if(test && iter == 1)
+        ok(test_loader_notification_count == test_loader_expected_notification_count,
+                "Got unexpected notification count %u, expected %u, test %u.%u.\n",
+                test_loader_notification_count, test_loader_expected_notification_count, test, iter);
+
+        status = pLdrUnlockLoaderLock(0, magic);
+        ok(!status, "Got unexpected status %#x.\n", status);
+
+        if (result == WAIT_TIMEOUT)
+            WaitForSingleObject(next_test_event, INFINITE);
+        ++iter;
+    } while (test_loader_lock_repeat_lock);
+
+    SetEvent(lock_ready_event);
+    WaitForSingleObject(next_test_event, INFINITE);
+    /* 2. Test with the thread blocked in DLL entry point during process attach. */
+    swprintf(str, ARRAY_SIZE(str), L"%u", DLL_PROCESS_ATTACH);
+    bret = SetEnvironmentVariableW(env_var, str);
+    ok(bret, "SetEnvironmentVariableW failed.\n");
+    ++test;
+    iter = 0;
+    do
+    {
+        hmodule = LoadLibraryA("lock.dll");
+        ok(!!hmodule, "LoadLibrary failed, err %u.\n", GetLastError());
+        p_set_lock_result_addr = (void *)GetProcAddress(hmodule, "set_lock_result_addr");
+        p_set_lock_result_addr(&result);
+
+        if (result == WAIT_TIMEOUT)
+            InterlockedIncrement(&test_loader_lock_timeout_count);
+
+        bret = FreeLibrary(hmodule);
+        ok(bret, "FreeLibrary failed, err %u.\n", GetLastError());
+
+        ok(result == test_loader_lock_expected_wait_result,
+                "Got unexpected wait result %#x, expected %#x, test %u.%u.\n",
+                result, test_loader_lock_expected_wait_result, test, iter);
+
+        if (result == WAIT_TIMEOUT)
+            WaitForSingleObject(next_test_event, INFINITE);
+        ++iter;
+    } while (test_loader_lock_repeat_lock);
+
+    SetEvent(lock_ready_event);
+    WaitForSingleObject(next_test_event, INFINITE);
+
+    /* 3. 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");
+    ++test;
+    iter = 0;
+    do
+    {
+        lock_dll_handle = hmodule = LoadLibraryA("lock.dll");
+        ok(!!hmodule, "LoadLibrary failed, err %u.\n", GetLastError());
+        p_set_lock_result_addr = (void *)GetProcAddress(hmodule, "set_lock_result_addr");
+        ok(!!p_set_lock_result_addr, "Got NULL p_set_lock_result_addr.\n");
+        p_set_lock_result_addr(&result);
+        bret = FreeLibrary(hmodule);
+        ok(bret, "FreeLibrary failed, err %u.\n", GetLastError());
+
+        lock_dll_handle = NULL;
+
+        if (result == WAIT_TIMEOUT)
+            InterlockedIncrement(&test_loader_lock_timeout_count);
+
+        ok(result == test_loader_lock_expected_wait_result,
+                "Got unexpected wait result %#x, expected %#x, test %u.%u.\n",
+                result, test_loader_lock_expected_wait_result, test, iter);
+
+        if (result == WAIT_TIMEOUT)
+            WaitForSingleObject(next_test_event, INFINITE);
+        ++iter;
+    } while (test_loader_lock_repeat_lock);
+
+    SetEvent(lock_ready_event);
+    WaitForSingleObject(next_test_event, INFINITE);
+
+    /* 4. Test with the thread blocked in LdrEnumerateLoadedModules callback. */
+    ++test;
+    iter = 0;
+    do
+    {
+        pLdrEnumerateLoadedModules(NULL, test_loader_lock_module_enum_locking_callback, &result);
+
+        if (result == WAIT_TIMEOUT)
+            InterlockedIncrement(&test_loader_lock_timeout_count);
+
+        ok(result == test_loader_lock_expected_wait_result,
+                "Got unexpected wait result %#x, expected %#x, test %u.%u.\n",
+                result, test_loader_lock_expected_wait_result, test, iter);
+        ok(test_loader_notification_count == test_loader_expected_notification_count,
+                "Got unexpected notification count %u, expected %u, test %u.%u.\n",
+                test_loader_notification_count, test_loader_expected_notification_count, test, iter);
+
+        if (result == WAIT_TIMEOUT)
+            WaitForSingleObject(next_test_event, INFINITE);
+        ++iter;
+    } while (test_loader_lock_repeat_lock);
+
+    SetEvent(lock_ready_event);
+    WaitForSingleObject(next_test_event, INFINITE);
+
+    /* 5. Test with the thread blocked in LDR notification callback. */
+    LdrRegisterDllNotification(0, test_loader_lock_ldr_notify_locking, &result, &cookie);
+    /* lock.dll will return FALSE from the DLL entry point. */
+    bret = SetEnvironmentVariableW(env_var, L"-1");
+    ok(bret, "SetEnvironmentVariableW failed.\n");
+
+    ++test;
+    iter = 0;
+    do
+    {
+        hmodule = LoadLibraryA("lock.dll");
+        ok(!!hmodule, "LoadLibrary failed, err %u.\n", GetLastError());
+        bret = FreeLibrary(hmodule);
+        ok(bret, "FreeLibrary failed, err %u.\n", GetLastError());
+
+        if (result == WAIT_TIMEOUT)
+            InterlockedIncrement(&test_loader_lock_timeout_count);
+
+        ok(result == test_loader_lock_expected_wait_result,
+                "Got unexpected wait result %#x, expected %#x, test %u.%u.\n",
+                result, test_loader_lock_expected_wait_result, test, iter);
+        ok(test_loader_notification_count == test_loader_expected_notification_count,
+                "Got unexpected notification count %u, expected %u, test %u.%u.\n",
+                test_loader_notification_count, test_loader_expected_notification_count, test, iter);
+
+        if (result == WAIT_TIMEOUT)
+            WaitForSingleObject(next_test_event, INFINITE);
+        ++iter;
+    } while (test_loader_lock_repeat_lock);
+    LdrUnregisterDllNotification( cookie );
+    SetEvent(lock_ready_event);
+    return 0;
+}
+
+static void CALLBACK test_loader_lock_ldr_notify(ULONG reason, LDR_DLL_NOTIFICATION_DATA *data, void *context)
+{
+    if (!lstrcmpW(data->Loaded.BaseDllName->Buffer, ldr_notify_track_dll))
+        ++test_loader_notification_count;
+}
+
+static void CALLBACK test_loader_lock_enum_callback(LDR_DATA_TABLE_ENTRY *mod, void *context, BOOLEAN *stop)
+{
+    *stop = TRUE;
+}
+
+static void test_loader_lock(void)
+{
+#define CHECK ok(!test_loader_lock_timeout_count, "Got timeout count %d.\n", test_loader_lock_timeout_count)
+    static const WCHAR not_loaded_dll_name[] = L"authz.dll";
+    static const WCHAR preloaded_dll_name[] = L"winmm.dll";
+    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");
+
+    thread = CreateThread(NULL, 0, test_loader_lock_thread, NULL, 0, NULL);
+
+    hmodule_preloaded = LoadLibraryW(preloaded_dll_name);
+    ok(!!hmodule_preloaded, "LoadLibrary failed, err %u.\n", GetLastError());
+    hmodule = GetModuleHandleW(not_loaded_dll_name);
+    ok(!hmodule, "%s is already loaded.\n", not_loaded_dll_name);
+
+    test_loader_notification_count = 0;
+
+    /* 1. Test with loader lock held. */
+    trace("Test 1.\n");
+    SetEvent(next_test_event);
+    WaitForSingleObject(lock_ready_event, INFINITE);
+    test_loader_lock_expected_wait_result = WAIT_OBJECT_0;
+
+    status = LdrRegisterDllNotification(0, test_loader_lock_ldr_notify, NULL, &cookie);
+    ok(!status, "Got unexpected status %#x.\n", status);
+    CHECK;
+    ldr_notify_track_dll = not_loaded_dll_name;
+
+    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;
+
+    bret = FreeLibrary(hmodule);
+    ok(bret, "FreeLibrary failed, err %u.\n", GetLastError());
+    CHECK;
+
+    ok(!test_loader_notification_count, "Got unexpected test_loader_notification_count %u.\n", test_loader_notification_count);
+
+    if (BLOCKING_TESTS_ENABLED)
+    {
+        CHECK;
+        test_loader_lock_repeat_lock = TRUE;
+        SetEvent(next_test_event);
+        WaitForSingleObject(lock_ready_event, INFINITE);
+
+        if (test_loader_lock_test_failed)
+        {
+            win_skip("Old loader, tests skipped.\n");
+            goto done;
+        }
+
+        /* With loader lock held notification callback is called which should mean that:
+         *  - LDR notifications themselves do not wait on loader lock;
+         *  - The library load goes far enough to call the LDR notification until it blocks on the loader lock.
+         */
+        test_loader_expected_notification_count = 1;
+        test_loader_lock_expected_wait_result = WAIT_TIMEOUT;
+        hmodule = LoadLibraryW(not_loaded_dll_name);
+        ok(!!hmodule, "LoadLibrary failed, err %u.\n", GetLastError());
+
+        SetEvent(next_test_event);
+        WaitForSingleObject(lock_ready_event, INFINITE);
+
+        test_loader_notification_count = 0;
+        test_loader_expected_notification_count = 0;
+        bret = FreeLibrary(hmodule);
+        ok(bret, "FreeLibrary failed, err %u.\n", GetLastError());
+
+        ok(test_loader_notification_count == 1, "Got unexpected notification count %u, expected %u.\n",
+                test_loader_notification_count, 1);
+        test_loader_notification_count = 0;
+        test_loader_expected_notification_count = 0;
+
+        SetEvent(next_test_event);
+        WaitForSingleObject(lock_ready_event, INFINITE);
+        pLdrEnumerateLoadedModules(NULL, test_loader_lock_enum_callback, NULL);
+
+        test_loader_lock_repeat_lock = FALSE;
+    }
+
+    LdrUnregisterDllNotification( cookie );
+
+    SetEvent(next_test_event);
+    WaitForSingleObject(lock_ready_event, INFINITE);
+
+    if (test_loader_lock_test_failed)
+    {
+        win_skip("Old loader, tests skipped.\n");
+        goto done;
+    }
+
+    /* 2. Test with the thread blocked in DLL entry point during process attach. */
+    trace("Test 2.\n");
+    SetEvent(next_test_event);
+
+    WaitForSingleObject(lock_ready_event, INFINITE);
+    test_loader_lock_expected_wait_result = WAIT_OBJECT_0;
+
+    status = LdrRegisterDllNotification(0, test_loader_lock_ldr_notify, NULL, &cookie);
+    ok(!status, "Got unexpected status %#x.\n", status);
+    CHECK;
+
+    bret = GetModuleHandleExW(0, preloaded_dll_name, &hmodule);
+    CHECK;
+    ok(bret, "GetModuleHandleEx failed, err %u.\n", GetLastError());
+    ok(hmodule == hmodule_preloaded, "Got unexpected hmodule %p, expected %p.\n", hmodule, hmodule_preloaded);
+
+    proc = GetProcAddress(hmodule, "timeGetTime");
+    CHECK;
+    ok(!!proc, "GetProcAddress failed.\n");
+
+    bret = FreeLibrary(hmodule);
+    CHECK;
+    ok(bret, "FreeLibrary failed, err %u.\n", GetLastError());
+
+    hmodule = GetModuleHandleA("lock.dll");
+    CHECK;
+    ok(!!hmodule, "GetModuleHandleA failed, err %u.\n", GetLastError());
+
+    bret = GetModuleHandleExW(0, L"lock.dll", &hmodule);
+    CHECK;
+    ok(bret, "GetModuleHandleEx failed, err %u.\n", GetLastError());
+
+    lock_dll_handle = hmodule;
+
+    status = LdrAddRefDll(0, hmodule);
+    CHECK;
+    ok(!status, "Got unexpected status %#x.\n", status);
+
+    bret = FreeLibrary(hmodule);
+    CHECK;
+    ok(bret, "FreeLibrary failed, err %u.\n", GetLastError());
+
+    bret = FreeLibrary(hmodule);
+    CHECK;
+    ok(bret, "FreeLibrary failed, err %u.\n", GetLastError());
+
+    hmodule = LoadLibraryW(preloaded_dll_name);
+    CHECK;
+    ok(!!hmodule, "LoadLibrary failed, err %u.\n", GetLastError());
+
+    bret = FreeLibrary(hmodule);
+    CHECK;
+    ok(bret, "FreeLibrary failed, err %u.\n", GetLastError());
+
+    if (BLOCKING_TESTS_ENABLED)
+    {
+        test_loader_lock_repeat_lock = TRUE;
+        SetEvent(next_test_event);
+        WaitForSingleObject(lock_ready_event, INFINITE);
+
+        test_loader_lock_expected_wait_result = WAIT_TIMEOUT;
+
+        ok(!!lock_dll_handle, "Got NULL lock_dll_handle.\n");
+        proc = GetProcAddress(lock_dll_handle, "unknown");
+        ok(!proc, "GetProcAddress succeeded.\n");
+
+        test_loader_lock_repeat_lock = FALSE;
+    }
+
+    LdrUnregisterDllNotification( cookie );
+    SetEvent(next_test_event);
+    WaitForSingleObject(lock_ready_event, INFINITE);
+    /* 3. Test with the thread blocked in DLL entry point during process detach. */
+    trace("Test 3.\n");
+
+    SetEvent(next_test_event);
+    WaitForSingleObject(lock_ready_event, INFINITE);
+    test_loader_lock_timeout_count = 0;
+    test_loader_lock_expected_wait_result = WAIT_OBJECT_0;
+
+    status = LdrRegisterDllNotification(0, test_loader_lock_ldr_notify, NULL, &cookie);
+    ok(!status, "Got unexpected status %#x.\n", status);
+    CHECK;
+
+    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;
+
+    proc = GetProcAddress(hmodule, "timeGetTime");
+    ok(!!proc, "GetProcAddress failed.\n");
+    CHECK;
+
+    bret = FreeLibrary(hmodule);
+    ok(bret, "FreeLibrary failed, err %u.\n", GetLastError());
+    CHECK;
+
+    ok(!!lock_dll_handle, "Got NULL lock_dll_handle.\n");
+
+    proc = GetProcAddress(lock_dll_handle, "set_lock_result_addr");
+    CHECK;
+
+    ok(!proc, "GetProcAddress failed, err %u.\n", GetLastError());
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "Got unexpected error %u.\n", GetLastError());
+
+    status = LdrAddRefDll(0, lock_dll_handle);
+    CHECK;
+    ok(status == STATUS_DLL_NOT_FOUND, "Got unexpected status %#x.\n", status);
+
+    if (BLOCKING_TESTS_ENABLED)
+    {
+        test_loader_lock_repeat_lock = TRUE;
+        SetEvent(next_test_event);
+        WaitForSingleObject(lock_ready_event, INFINITE);
+
+        test_loader_lock_expected_wait_result = WAIT_TIMEOUT;
+        hmodule = GetModuleHandleW(L"lock.dll");
+        ok(!hmodule, "GetModuleHandleW succeeded.\n", GetLastError());
+        ok(GetLastError() == ERROR_MOD_NOT_FOUND, "Got unexpected error %u.\n", GetLastError());
+        test_loader_lock_repeat_lock = FALSE;
+    }
+
+    LdrUnregisterDllNotification( cookie );
+    SetEvent(next_test_event);
+    WaitForSingleObject(lock_ready_event, INFINITE);
+
+    /* 4. Test with the thread blocked in LdrEnumerateLoadedModules callback. */
+    trace("Test 4.\n");
+
+    SetEvent(next_test_event);
+    WaitForSingleObject(lock_ready_event, INFINITE);
+    test_loader_lock_timeout_count = 0;
+    test_loader_lock_expected_wait_result = WAIT_OBJECT_0;
+
+    status = LdrRegisterDllNotification(0, test_loader_lock_ldr_notify, NULL, &cookie);
+    ok(!status, "Got unexpected status %#x.\n", status);
+    CHECK;
+
+    hmodule = GetModuleHandleW(preloaded_dll_name);
+    ok(!!hmodule, "LoadLibrary failed, err %u.\n", GetLastError());
+    CHECK;
+
+    if (BLOCKING_TESTS_ENABLED)
+    {
+        test_loader_lock_repeat_lock = TRUE;
+        SetEvent(next_test_event);
+        WaitForSingleObject(lock_ready_event, INFINITE);
+
+        test_loader_lock_expected_wait_result = WAIT_TIMEOUT;
+
+        status = pLdrLockLoaderLock(0, NULL, &magic);
+        ok(!status, "Got unexpected status %#x.\n", status);
+        status = pLdrUnlockLoaderLock(0, magic);
+        ok(!status, "Got unexpected status %#x.\n", status);
+
+        test_loader_lock_repeat_lock = FALSE;
+    }
+
+    LdrUnregisterDllNotification( cookie );
+
+    SetEvent(next_test_event);
+    WaitForSingleObject(lock_ready_event, INFINITE);
+
+    /* 5. Test with the thread blocked in LDR notification callback. */
+    trace("Test 5.\n");
+    SetEvent(next_test_event);
+    WaitForSingleObject(lock_ready_event, INFINITE);
+
+    test_loader_lock_timeout_count = 0;
+    test_loader_lock_expected_wait_result = WAIT_OBJECT_0;
+
+    hmodule = GetModuleHandleW(preloaded_dll_name);
+    ok(!!hmodule, "LoadLibrary failed, err %u.\n", GetLastError());
+    CHECK;
+
+    if (BLOCKING_TESTS_ENABLED)
+    {
+        test_loader_lock_repeat_lock = TRUE;
+        SetEvent(next_test_event);
+        WaitForSingleObject(lock_ready_event, INFINITE);
+        test_loader_lock_expected_wait_result = WAIT_TIMEOUT;
+
+        status = LdrRegisterDllNotification(0, test_loader_lock_ldr_notify, NULL, &cookie);
+        ok(!status, "Got unexpected status %#x.\n", status);
+        LdrUnregisterDllNotification( cookie );
+
+        SetEvent(next_test_event);
+        WaitForSingleObject(lock_ready_event, INFINITE);
+
+        /* This doesn't block in load notifications on Windows, only unload. */
+        status = pLdrLockLoaderLock(0, NULL, &magic);
+        ok(!status, "Got unexpected status %#x.\n", status);
+        status = pLdrUnlockLoaderLock(0, magic);
+        ok(!status, "Got unexpected status %#x.\n", status);
+
+        test_loader_lock_repeat_lock = FALSE;
+    }
+
+    SetEvent(next_test_event);
+    WaitForSingleObject(lock_ready_event, INFINITE);
+
+
+    WaitForSingleObject(thread, INFINITE);
+    CloseHandle(thread);
+
+done:
+    DeleteFileA("lock.dll");
+    FreeLibrary(hmodule_preloaded);
+    CloseHandle(lock_ready_event);
+    CloseHandle(next_test_event);
+}
+
 START_TEST(loader)
 {
     int argc;
@@ -4060,6 +4634,7 @@ START_TEST(loader)
     pLdrUnlockLoaderLock = (void *)GetProcAddress(ntdll, "LdrUnlockLoaderLock");
     pLdrLoadDll = (void *)GetProcAddress(ntdll, "LdrLoadDll");
     pLdrUnloadDll = (void *)GetProcAddress(ntdll, "LdrUnloadDll");
+    pLdrEnumerateLoadedModules = (void *)GetProcAddress(ntdll, "LdrEnumerateLoadedModules");
     pRtlInitUnicodeString = (void *)GetProcAddress(ntdll, "RtlInitUnicodeString");
     pRtlAcquirePebLock = (void *)GetProcAddress(ntdll, "RtlAcquirePebLock");
     pRtlReleasePebLock = (void *)GetProcAddress(ntdll, "RtlReleasePebLock");
@@ -4099,6 +4674,7 @@ START_TEST(loader)
         return;
     }
 
+if(0){
     test_filenames();
     test_ResolveDelayLoadedAPI();
     test_ImportDescriptors();
@@ -4113,6 +4689,9 @@ START_TEST(loader)
     test_dll_file( "advapi32.dll" );
     test_dll_file( "user32.dll" );
     test_Wow64Transition();
+}
+    test_loader_lock();
+return;
     /* loader test must be last, it can corrupt the internal loader state on Windows */
     test_Loader();
 }
diff --git a/dlls/kernel32/tests/locking_dll.c b/dlls/kernel32/tests/locking_dll.c
new file mode 100644
index 00000000000..31aff05fe84
--- /dev/null
+++ b/dlls/kernel32/tests/locking_dll.c
@@ -0,0 +1,50 @@
+#include <stdarg.h>
+#include <stdlib.h>
+#include "ntstatus.h"
+#define WIN32_NO_STATUS
+#include "windef.h"
+#include "winbase.h"
+#include "winternl.h"
+
+static DWORD last_lock_result, *lock_result_addr;
+
+BOOL WINAPI DllMain( HINSTANCE instance, DWORD reason, LPVOID reserved )
+{
+    HANDLE lock_ready_event, next_test_event;
+    DWORD wait_reason;
+    NTSTATUS status;
+    char str[8];
+
+    GetEnvironmentVariableA("test_wait_reason_value", str, sizeof(str));
+    wait_reason = atoi(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");
+
+    SetEvent( lock_ready_event );
+    last_lock_result = WaitForSingleObject(next_test_event, 2000);
+
+    if (0)
+    {
+        if (reason == DLL_PROCESS_DETACH)
+        {
+            status = LdrAddRefDll(0, instance);
+            if (status) last_lock_result = ~0u;
+            status = LdrAddRefDll(LDR_ADDREF_DLL_PIN, instance);
+            if (status != STATUS_UNSUCCESSFUL) last_lock_result = ~0u - 1;
+        }
+    }
+    if (lock_result_addr) *lock_result_addr = last_lock_result;
+
+    CloseHandle(lock_ready_event);
+    CloseHandle(next_test_event);
+    return TRUE;
+}
+
+void WINAPI set_lock_result_addr( DWORD *addr )
+{
+    lock_result_addr = addr;
+    *addr = last_lock_result;
+}
diff --git a/dlls/kernel32/tests/locking_dll.spec b/dlls/kernel32/tests/locking_dll.spec
new file mode 100644
index 00000000000..327515590c3
--- /dev/null
+++ b/dlls/kernel32/tests/locking_dll.spec
@@ -0,0 +1 @@
+@ stdcall set_lock_result_addr(ptr)
-- 
2.31.1




More information about the wine-devel mailing list