Commit 3de37fef authored by Dmitry Timoshkov's avatar Dmitry Timoshkov Committed by Alexandre Julliard

kernel32: Add some tests for PEB and loader locks.

parent 10476af6
...@@ -53,6 +53,10 @@ static void (WINAPI *pLdrShutdownProcess)(void); ...@@ -53,6 +53,10 @@ static void (WINAPI *pLdrShutdownProcess)(void);
static BOOLEAN (WINAPI *pRtlDllShutdownInProgress)(void); static BOOLEAN (WINAPI *pRtlDllShutdownInProgress)(void);
static NTSTATUS (WINAPI *pNtAllocateVirtualMemory)(HANDLE, PVOID *, ULONG, SIZE_T *, ULONG, ULONG); static NTSTATUS (WINAPI *pNtAllocateVirtualMemory)(HANDLE, PVOID *, ULONG, SIZE_T *, ULONG, ULONG);
static NTSTATUS (WINAPI *pNtFreeVirtualMemory)(HANDLE, PVOID *, SIZE_T *, 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) static PVOID RVAToAddr(DWORD_PTR rva, HMODULE module)
{ {
...@@ -1054,12 +1058,14 @@ nt4_is_broken: ...@@ -1054,12 +1058,14 @@ nt4_is_broken:
} }
#define MAX_COUNT 10 #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 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) static DWORD WINAPI mutex_thread_proc(void *param)
{ {
HANDLE wait_list[3];
DWORD ret; DWORD ret;
ret = WaitForSingleObject(mutex, 0); ret = WaitForSingleObject(mutex, 0);
...@@ -1067,10 +1073,31 @@ static DWORD WINAPI mutex_thread_proc(void *param) ...@@ -1067,10 +1073,31 @@ static DWORD WINAPI mutex_thread_proc(void *param)
SetEvent(param); SetEvent(param);
wait_list[0] = stop_event;
wait_list[1] = loader_lock_event;
wait_list[2] = peb_lock_event;
while (1) while (1)
{ {
trace("%04u: mutex_thread_proc: still alive\n", GetCurrentThreadId()); 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()); trace("%04u: mutex_thread_proc: exiting\n", GetCurrentThreadId());
...@@ -1113,6 +1140,13 @@ static BOOL WINAPI dll_entry_point(HINSTANCE hinst, DWORD reason, LPVOID param) ...@@ -1113,6 +1140,13 @@ static BOOL WINAPI dll_entry_point(HINSTANCE hinst, DWORD reason, LPVOID param)
static LONG noop_thread_started; static LONG noop_thread_started;
DWORD ret; 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) switch (reason)
{ {
case DLL_PROCESS_ATTACH: case DLL_PROCESS_ATTACH:
...@@ -1133,6 +1167,13 @@ static BOOL WINAPI dll_entry_point(HINSTANCE hinst, DWORD reason, LPVOID param) ...@@ -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); 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) if (test_dll_phase == 0 || test_dll_phase == 3)
ok(param != NULL, "dll: param %p\n", param); ok(param != NULL, "dll: param %p\n", param);
else else
...@@ -1369,6 +1410,18 @@ static void child_process(const char *dll_name, DWORD target_offset) ...@@ -1369,6 +1410,18 @@ static void child_process(const char *dll_name, DWORD target_offset)
event = CreateEvent(NULL, TRUE, FALSE, NULL); event = CreateEvent(NULL, TRUE, FALSE, NULL);
ok(event != 0, "CreateEvent error %d\n", GetLastError()); 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); file = CreateFile(dll_name, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
if (file == INVALID_HANDLE_VALUE) if (file == INVALID_HANDLE_VALUE)
{ {
...@@ -1527,6 +1580,42 @@ todo_wine ...@@ -1527,6 +1580,42 @@ todo_wine
CloseHandle(stop_event); CloseHandle(stop_event);
break; 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: default:
assert(0); assert(0);
break; break;
...@@ -1534,6 +1623,7 @@ todo_wine ...@@ -1534,6 +1623,7 @@ todo_wine
if (test_dll_phase == 0) expected_code = 195; if (test_dll_phase == 0) expected_code = 195;
else if (test_dll_phase == 3) expected_code = 196; else if (test_dll_phase == 3) expected_code = 196;
else if (test_dll_phase == 4) expected_code = 198;
else expected_code = STILL_ACTIVE; else expected_code = STILL_ACTIVE;
if (expected_code == STILL_ACTIVE) if (expected_code == STILL_ACTIVE)
...@@ -1561,7 +1651,7 @@ todo_wine ...@@ -1561,7 +1651,7 @@ todo_wine
*child_failures = winetest_get_failures(); *child_failures = winetest_get_failures();
trace("call ExitProcess()\n"); trace("call ExitProcess(195)\n");
ExitProcess(195); ExitProcess(195);
} }
...@@ -1685,8 +1775,9 @@ static void test_ExitProcess(void) ...@@ -1685,8 +1775,9 @@ static void test_ExitProcess(void)
sprintf(cmdline, "\"%s\" loader %s %u 0", argv[0], dll_name, target_offset); 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); ret = CreateProcess(argv[0], cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
ok(ret, "CreateProcess(%s) error %d\n", cmdline, GetLastError()); 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"); ok(ret == WAIT_OBJECT_0, "child process failed to terminate\n");
if (ret != WAIT_OBJECT_0) TerminateProcess(pi.hProcess, 0);
GetExitCodeProcess(pi.hProcess, &ret); GetExitCodeProcess(pi.hProcess, &ret);
ok(ret == 195, "expected exit code 195, got %u\n", ret); ok(ret == 195, "expected exit code 195, got %u\n", ret);
if (*child_failures) if (*child_failures)
...@@ -1702,8 +1793,9 @@ static void test_ExitProcess(void) ...@@ -1702,8 +1793,9 @@ static void test_ExitProcess(void)
sprintf(cmdline, "\"%s\" loader %s %u 1", argv[0], dll_name, target_offset); 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); ret = CreateProcess(argv[0], cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
ok(ret, "CreateProcess(%s) error %d\n", cmdline, GetLastError()); 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"); ok(ret == WAIT_OBJECT_0, "child process failed to terminate\n");
if (ret != WAIT_OBJECT_0) TerminateProcess(pi.hProcess, 0);
GetExitCodeProcess(pi.hProcess, &ret); GetExitCodeProcess(pi.hProcess, &ret);
ok(ret == 195, "expected exit code 195, got %u\n", ret); ok(ret == 195, "expected exit code 195, got %u\n", ret);
if (*child_failures) if (*child_failures)
...@@ -1719,8 +1811,9 @@ static void test_ExitProcess(void) ...@@ -1719,8 +1811,9 @@ static void test_ExitProcess(void)
sprintf(cmdline, "\"%s\" loader %s %u 2", argv[0], dll_name, target_offset); 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); ret = CreateProcess(argv[0], cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
ok(ret, "CreateProcess(%s) error %d\n", cmdline, GetLastError()); 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"); ok(ret == WAIT_OBJECT_0, "child process failed to terminate\n");
if (ret != WAIT_OBJECT_0) TerminateProcess(pi.hProcess, 0);
GetExitCodeProcess(pi.hProcess, &ret); GetExitCodeProcess(pi.hProcess, &ret);
ok(ret == 197, "expected exit code 197, got %u\n", ret); ok(ret == 197, "expected exit code 197, got %u\n", ret);
if (*child_failures) if (*child_failures)
...@@ -1736,8 +1829,9 @@ static void test_ExitProcess(void) ...@@ -1736,8 +1829,9 @@ static void test_ExitProcess(void)
sprintf(cmdline, "\"%s\" loader %s %u 3", argv[0], dll_name, target_offset); 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); ret = CreateProcess(argv[0], cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
ok(ret, "CreateProcess(%s) error %d\n", cmdline, GetLastError()); 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"); ok(ret == WAIT_OBJECT_0, "child process failed to terminate\n");
if (ret != WAIT_OBJECT_0) TerminateProcess(pi.hProcess, 0);
GetExitCodeProcess(pi.hProcess, &ret); GetExitCodeProcess(pi.hProcess, &ret);
ok(ret == 195, "expected exit code 195, got %u\n", ret); ok(ret == 195, "expected exit code 195, got %u\n", ret);
if (*child_failures) if (*child_failures)
...@@ -1748,6 +1842,60 @@ static void test_ExitProcess(void) ...@@ -1748,6 +1842,60 @@ static void test_ExitProcess(void)
CloseHandle(pi.hThread); CloseHandle(pi.hThread);
CloseHandle(pi.hProcess); 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 */ /* test remote process termination */
SetLastError(0xdeadbeef); SetLastError(0xdeadbeef);
ret = CreateProcess(argv[0], NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi); ret = CreateProcess(argv[0], NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi);
...@@ -1800,7 +1948,7 @@ static void test_ExitProcess(void) ...@@ -1800,7 +1948,7 @@ static void test_ExitProcess(void)
/* Calling GetExitCodeThread() without waiting for thread termination /* Calling GetExitCodeThread() without waiting for thread termination
* leads to different results due to a race condition. * leads to different results due to a race condition.
*/ */
ret = WaitForSingleObject(thread, 100); ret = WaitForSingleObject(thread, 1000);
ok(ret == WAIT_OBJECT_0, "WaitForSingleObject failed: %x\n", ret); ok(ret == WAIT_OBJECT_0, "WaitForSingleObject failed: %x\n", ret);
GetExitCodeThread(thread, &ret); GetExitCodeThread(thread, &ret);
ok(ret == 199, "expected exit code 199, got %u\n", ret); ok(ret == 199, "expected exit code 199, got %u\n", ret);
...@@ -1929,6 +2077,10 @@ START_TEST(loader) ...@@ -1929,6 +2077,10 @@ START_TEST(loader)
pRtlDllShutdownInProgress = (void *)GetProcAddress(GetModuleHandle("ntdll.dll"), "RtlDllShutdownInProgress"); pRtlDllShutdownInProgress = (void *)GetProcAddress(GetModuleHandle("ntdll.dll"), "RtlDllShutdownInProgress");
pNtAllocateVirtualMemory = (void *)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtAllocateVirtualMemory"); pNtAllocateVirtualMemory = (void *)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtAllocateVirtualMemory");
pNtFreeVirtualMemory = (void *)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtFreeVirtualMemory"); 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"); mapping = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 4096, "winetest_loader");
ok(mapping != 0, "CreateFileMapping failed\n"); ok(mapping != 0, "CreateFileMapping failed\n");
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment