kernel32: Add some tests for PEB and loader locks.

Dmitry Timoshkov dmitry at baikal.ru
Tue Apr 30 03:07:44 CDT 2013


---
 dlls/kernel32/tests/loader.c | 170 ++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 161 insertions(+), 9 deletions(-)

diff --git a/dlls/kernel32/tests/loader.c b/dlls/kernel32/tests/loader.c
index fa8e974..12eeb67 100644
--- a/dlls/kernel32/tests/loader.c
+++ b/dlls/kernel32/tests/loader.c
@@ -53,6 +53,10 @@ static void (WINAPI *pLdrShutdownProcess)(void);
 static BOOLEAN (WINAPI *pRtlDllShutdownInProgress)(void);
 static NTSTATUS (WINAPI *pNtAllocateVirtualMemory)(HANDLE, PVOID *, ULONG, SIZE_T *, ULONG, ULONG);
 static NTSTATUS (WINAPI *pNtFreeVirtualMemory)(HANDLE, PVOID *, SIZE_T *, ULONG);
+static NTSTATUS (WINAPI *pLdrLockLoaderLock)(ULONG, ULONG *, ULONG *);
+static NTSTATUS (WINAPI *pLdrUnlockLoaderLock)(ULONG, ULONG);
+static void (WINAPI *pRtlAcquirePebLock)(void);
+static void (WINAPI *pRtlReleasePebLock)(void);
 
 static PVOID RVAToAddr(DWORD_PTR rva, HMODULE module)
 {
@@ -1054,12 +1058,14 @@ nt4_is_broken:
 }
 
 #define MAX_COUNT 10
-static HANDLE attached_thread[MAX_COUNT], stop_event, event, mutex, semaphore;
+static HANDLE attached_thread[MAX_COUNT];
 static DWORD attached_thread_count;
-static int test_dll_phase;
+HANDLE stop_event, event, mutex, semaphore, loader_lock_event, peb_lock_event, ack_event;
+static int test_dll_phase, inside_loader_lock, inside_peb_lock;
 
 static DWORD WINAPI mutex_thread_proc(void *param)
 {
+    HANDLE wait_list[3];
     DWORD ret;
 
     ret = WaitForSingleObject(mutex, 0);
@@ -1067,10 +1073,31 @@ static DWORD WINAPI mutex_thread_proc(void *param)
 
     SetEvent(param);
 
+    wait_list[0] = stop_event;
+    wait_list[1] = loader_lock_event;
+    wait_list[2] = peb_lock_event;
+
     while (1)
     {
         trace("%04u: mutex_thread_proc: still alive\n", GetCurrentThreadId());
-        if (WaitForSingleObject(stop_event, 50) != WAIT_TIMEOUT) break;
+        ret = WaitForMultipleObjects(3, wait_list, FALSE, 50);
+        if (ret == WAIT_OBJECT_0) break;
+        else if (ret == WAIT_OBJECT_0 + 1)
+        {
+            ULONG loader_lock_magic;
+            trace("%04u: mutex_thread_proc: Entering loader lock\n", GetCurrentThreadId());
+            ret = pLdrLockLoaderLock(0, NULL, &loader_lock_magic);
+            ok(!ret, "LdrLockLoaderLock error %#x\n", ret);
+            inside_loader_lock++;
+            SetEvent(ack_event);
+        }
+        else if (ret == WAIT_OBJECT_0 + 2)
+        {
+            trace("%04u: mutex_thread_proc: Entering PEB lock\n", GetCurrentThreadId());
+            pRtlAcquirePebLock();
+            inside_peb_lock++;
+            SetEvent(ack_event);
+        }
     }
 
     trace("%04u: mutex_thread_proc: exiting\n", GetCurrentThreadId());
@@ -1113,6 +1140,13 @@ static BOOL WINAPI dll_entry_point(HINSTANCE hinst, DWORD reason, LPVOID param)
     static LONG noop_thread_started;
     DWORD ret;
 
+    ok(!inside_loader_lock, "inside_loader_lock should not be set\n");
+    /* FIXME: remove once Wine is fixed */
+    if (inside_peb_lock && reason == DLL_PROCESS_DETACH) todo_wine
+    ok(!inside_peb_lock, "inside_peb_lock should not be set\n");
+    else
+    ok(!inside_peb_lock, "inside_peb_lock should not be set\n");
+
     switch (reason)
     {
     case DLL_PROCESS_ATTACH:
@@ -1133,6 +1167,13 @@ static BOOL WINAPI dll_entry_point(HINSTANCE hinst, DWORD reason, LPVOID param)
 
         trace("dll: %p, DLL_PROCESS_DETACH, %p\n", hinst, param);
 
+        if (test_dll_phase == 4 || test_dll_phase == 5)
+        {
+todo_wine
+            ok(0, "dll_entry_point(DLL_PROCESS_DETACH) should not be called\n");
+            break;
+        }
+
         if (test_dll_phase == 0 || test_dll_phase == 3)
             ok(param != NULL, "dll: param %p\n", param);
         else
@@ -1369,6 +1410,18 @@ static void child_process(const char *dll_name, DWORD target_offset)
     event = CreateEvent(NULL, TRUE, FALSE, NULL);
     ok(event != 0, "CreateEvent error %d\n", GetLastError());
 
+    SetLastError(0xdeadbeef);
+    loader_lock_event = CreateEvent(NULL, FALSE, FALSE, NULL);
+    ok(loader_lock_event != 0, "CreateEvent error %d\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    peb_lock_event = CreateEvent(NULL, FALSE, FALSE, NULL);
+    ok(peb_lock_event != 0, "CreateEvent error %d\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ack_event = CreateEvent(NULL, FALSE, FALSE, NULL);
+    ok(ack_event != 0, "CreateEvent error %d\n", GetLastError());
+
     file = CreateFile(dll_name, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
     if (file == INVALID_HANDLE_VALUE)
     {
@@ -1527,6 +1580,42 @@ todo_wine
         CloseHandle(stop_event);
         break;
 
+    case 4:
+        trace("setting loader_lock_event\n");
+        SetEvent(loader_lock_event);
+        WaitForSingleObject(ack_event, 1000);
+        ok(inside_loader_lock != 0, "inside_loader_lock is not set\n");
+
+        /* calling NtTerminateProcess should not cause a deadlock */
+        trace("call NtTerminateProcess(0, 198)\n");
+        ret = pNtTerminateProcess(0, 198);
+        ok(!ret, "NtTerminateProcess error %#x\n", ret);
+
+        *child_failures = winetest_get_failures();
+
+        /* Windows fails to release loader lock acquired from another thread,
+         * so the LdrUnlockLoaderLock call fails here and ExitProcess deadlocks
+         * later on, so NtTerminateProcess is used instead.
+         */
+        trace("call NtTerminateProcess(GetCurrentProcess(), 198)\n");
+        pNtTerminateProcess(GetCurrentProcess(), 198);
+        ok(0, "NtTerminateProcess should not return\n");
+        break;
+
+    case 5:
+        trace("setting peb_lock_event\n");
+        SetEvent(peb_lock_event);
+        WaitForSingleObject(ack_event, 1000);
+        ok(inside_peb_lock != 0, "inside_peb_lock is not set\n");
+
+        *child_failures = winetest_get_failures();
+
+        /* calling ExitProcess should cause a deadlock */
+        trace("call ExitProcess(198)\n");
+        ExitProcess(198);
+        ok(0, "ExitProcess should not return\n");
+        break;
+
     default:
         assert(0);
         break;
@@ -1534,6 +1623,7 @@ todo_wine
 
     if (test_dll_phase == 0) expected_code = 195;
     else if (test_dll_phase == 3) expected_code = 196;
+    else if (test_dll_phase == 4) expected_code = 198;
     else expected_code = STILL_ACTIVE;
 
     if (expected_code == STILL_ACTIVE)
@@ -1561,7 +1651,7 @@ todo_wine
 
     *child_failures = winetest_get_failures();
 
-    trace("call ExitProcess()\n");
+    trace("call ExitProcess(195)\n");
     ExitProcess(195);
 }
 
@@ -1685,8 +1775,9 @@ static void test_ExitProcess(void)
     sprintf(cmdline, "\"%s\" loader %s %u 0", argv[0], dll_name, target_offset);
     ret = CreateProcess(argv[0], cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
     ok(ret, "CreateProcess(%s) error %d\n", cmdline, GetLastError());
-    ret = WaitForSingleObject(pi.hProcess, 30000);
+    ret = WaitForSingleObject(pi.hProcess, 10000);
     ok(ret == WAIT_OBJECT_0, "child process failed to terminate\n");
+    if (ret != WAIT_OBJECT_0) TerminateProcess(pi.hProcess, 0);
     GetExitCodeProcess(pi.hProcess, &ret);
     ok(ret == 195, "expected exit code 195, got %u\n", ret);
     if (*child_failures)
@@ -1702,8 +1793,9 @@ static void test_ExitProcess(void)
     sprintf(cmdline, "\"%s\" loader %s %u 1", argv[0], dll_name, target_offset);
     ret = CreateProcess(argv[0], cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
     ok(ret, "CreateProcess(%s) error %d\n", cmdline, GetLastError());
-    ret = WaitForSingleObject(pi.hProcess, 30000);
+    ret = WaitForSingleObject(pi.hProcess, 10000);
     ok(ret == WAIT_OBJECT_0, "child process failed to terminate\n");
+    if (ret != WAIT_OBJECT_0) TerminateProcess(pi.hProcess, 0);
     GetExitCodeProcess(pi.hProcess, &ret);
     ok(ret == 195, "expected exit code 195, got %u\n", ret);
     if (*child_failures)
@@ -1719,8 +1811,9 @@ static void test_ExitProcess(void)
     sprintf(cmdline, "\"%s\" loader %s %u 2", argv[0], dll_name, target_offset);
     ret = CreateProcess(argv[0], cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
     ok(ret, "CreateProcess(%s) error %d\n", cmdline, GetLastError());
-    ret = WaitForSingleObject(pi.hProcess, 30000);
+    ret = WaitForSingleObject(pi.hProcess, 10000);
     ok(ret == WAIT_OBJECT_0, "child process failed to terminate\n");
+    if (ret != WAIT_OBJECT_0) TerminateProcess(pi.hProcess, 0);
     GetExitCodeProcess(pi.hProcess, &ret);
     ok(ret == 197, "expected exit code 197, got %u\n", ret);
     if (*child_failures)
@@ -1736,8 +1829,9 @@ static void test_ExitProcess(void)
     sprintf(cmdline, "\"%s\" loader %s %u 3", argv[0], dll_name, target_offset);
     ret = CreateProcess(argv[0], cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
     ok(ret, "CreateProcess(%s) error %d\n", cmdline, GetLastError());
-    ret = WaitForSingleObject(pi.hProcess, 30000);
+    ret = WaitForSingleObject(pi.hProcess, 10000);
     ok(ret == WAIT_OBJECT_0, "child process failed to terminate\n");
+    if (ret != WAIT_OBJECT_0) TerminateProcess(pi.hProcess, 0);
     GetExitCodeProcess(pi.hProcess, &ret);
     ok(ret == 195, "expected exit code 195, got %u\n", ret);
     if (*child_failures)
@@ -1748,6 +1842,60 @@ static void test_ExitProcess(void)
     CloseHandle(pi.hThread);
     CloseHandle(pi.hProcess);
 
+    /* phase 4 */
+    if (pLdrLockLoaderLock && pLdrUnlockLoaderLock)
+    {
+        *child_failures = -1;
+        sprintf(cmdline, "\"%s\" loader %s %u 4", argv[0], dll_name, target_offset);
+        ret = CreateProcess(argv[0], cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
+        ok(ret, "CreateProcess(%s) error %d\n", cmdline, GetLastError());
+        ret = WaitForSingleObject(pi.hProcess, 10000);
+        ok(ret == WAIT_OBJECT_0, "child process failed to terminate\n");
+        if (ret != WAIT_OBJECT_0) TerminateProcess(pi.hProcess, 0);
+        GetExitCodeProcess(pi.hProcess, &ret);
+        ok(ret == 198, "expected exit code 198, got %u\n", ret);
+        if (*child_failures)
+        {
+            trace("%d failures in child process\n", *child_failures);
+            winetest_add_failures(*child_failures);
+        }
+        CloseHandle(pi.hThread);
+        CloseHandle(pi.hProcess);
+    }
+    else
+        win_skip("LdrLockLoaderLock/LdrUnlockLoaderLock are not available on this platform\n");
+
+    /* phase 5 */
+    if (pRtlAcquirePebLock && pRtlReleasePebLock)
+    {
+        *child_failures = -1;
+        sprintf(cmdline, "\"%s\" loader %s %u 5", argv[0], dll_name, target_offset);
+        ret = CreateProcess(argv[0], cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
+        ok(ret, "CreateProcess(%s) error %d\n", cmdline, GetLastError());
+        ret = WaitForSingleObject(pi.hProcess, 5000);
+todo_wine
+        ok(ret == WAIT_TIMEOUT, "child process should fail to terminate\n");
+        if (ret != WAIT_OBJECT_0)
+        {
+            trace("terminating child process\n");
+            TerminateProcess(pi.hProcess, 199);
+        }
+        ret = WaitForSingleObject(pi.hProcess, 1000);
+        ok(ret == WAIT_OBJECT_0, "child process failed to terminate\n");
+        GetExitCodeProcess(pi.hProcess, &ret);
+todo_wine
+        ok(ret == 199, "expected exit code 199, got %u\n", ret);
+        if (*child_failures)
+        {
+            trace("%d failures in child process\n", *child_failures);
+            winetest_add_failures(*child_failures);
+        }
+        CloseHandle(pi.hThread);
+        CloseHandle(pi.hProcess);
+    }
+    else
+        win_skip("RtlAcquirePebLock/RtlReleasePebLock are not available on this platform\n");
+
     /* test remote process termination */
     SetLastError(0xdeadbeef);
     ret = CreateProcess(argv[0], NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi);
@@ -1800,7 +1948,7 @@ static void test_ExitProcess(void)
     /* Calling GetExitCodeThread() without waiting for thread termination
      * leads to different results due to a race condition.
      */
-    ret = WaitForSingleObject(thread, 100);
+    ret = WaitForSingleObject(thread, 500);
     ok(ret == WAIT_OBJECT_0, "WaitForSingleObject failed: %x\n", ret);
     GetExitCodeThread(thread, &ret);
     ok(ret == 199, "expected exit code 199, got %u\n", ret);
@@ -1929,6 +2077,10 @@ START_TEST(loader)
     pRtlDllShutdownInProgress = (void *)GetProcAddress(GetModuleHandle("ntdll.dll"), "RtlDllShutdownInProgress");
     pNtAllocateVirtualMemory = (void *)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtAllocateVirtualMemory");
     pNtFreeVirtualMemory = (void *)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtFreeVirtualMemory");
+    pLdrLockLoaderLock = (void *)GetProcAddress(GetModuleHandle("ntdll.dll"), "LdrLockLoaderLock");
+    pLdrUnlockLoaderLock = (void *)GetProcAddress(GetModuleHandle("ntdll.dll"), "LdrUnlockLoaderLock");
+    pRtlAcquirePebLock = (void *)GetProcAddress(GetModuleHandle("ntdll.dll"), "RtlAcquirePebLock");
+    pRtlReleasePebLock = (void *)GetProcAddress(GetModuleHandle("ntdll.dll"), "RtlReleasePebLock");
 
     mapping = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 4096, "winetest_loader");
     ok(mapping != 0, "CreateFileMapping failed\n");
-- 
1.8.2.1




More information about the wine-patches mailing list