kernel32: Add some tests for remote process termination.

Dmitry Timoshkov dmitry at baikal.ru
Fri Apr 26 03:56:59 CDT 2013


---
 dlls/kernel32/tests/loader.c | 237 +++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 229 insertions(+), 8 deletions(-)

diff --git a/dlls/kernel32/tests/loader.c b/dlls/kernel32/tests/loader.c
index 5c05d9b..fa8e974 100644
--- a/dlls/kernel32/tests/loader.c
+++ b/dlls/kernel32/tests/loader.c
@@ -31,14 +31,28 @@
 
 #define ALIGN_SIZE(size, alignment) (((size) + (alignment - 1)) & ~((alignment - 1)))
 
+struct PROCESS_BASIC_INFORMATION_PRIVATE
+{
+    DWORD_PTR ExitStatus;
+    PPEB      PebBaseAddress;
+    DWORD_PTR AffinityMask;
+    DWORD_PTR BasePriority;
+    ULONG_PTR UniqueProcessId;
+    ULONG_PTR InheritedFromUniqueProcessId;
+};
+
 static BOOL is_child;
 static LONG *child_failures;
 
 static NTSTATUS (WINAPI *pNtMapViewOfSection)(HANDLE, HANDLE, PVOID *, ULONG, SIZE_T, const LARGE_INTEGER *, SIZE_T *, ULONG, ULONG, ULONG);
 static NTSTATUS (WINAPI *pNtUnmapViewOfSection)(HANDLE, PVOID);
+static NTSTATUS (WINAPI *pNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);
+static NTSTATUS (WINAPI *pNtSetInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG);
 static NTSTATUS (WINAPI *pNtTerminateProcess)(HANDLE, DWORD);
 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 PVOID RVAToAddr(DWORD_PTR rva, HMODULE module)
 {
@@ -1336,9 +1350,10 @@ static void child_process(const char *dll_name, DWORD target_offset)
 {
     void *target;
     DWORD ret, dummy, i, code, expected_code;
-    HANDLE file, thread;
+    HANDLE file, thread, process;
     HMODULE hmod;
-    NTSTATUS status;
+    struct PROCESS_BASIC_INFORMATION_PRIVATE pbi;
+    DWORD_PTR affinity;
 
     trace("phase %d: writing %p at %#x\n", test_dll_phase, dll_entry_point, target_offset);
 
@@ -1417,12 +1432,20 @@ static void child_process(const char *dll_name, DWORD target_offset)
     ok(!ret, "RtlDllShutdownInProgress returned %d\n", ret);
 
     SetLastError(0xdeadbeef);
+    process = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId());
+    ok(process != NULL, "OpenProcess error %d\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
     ret = TerminateProcess(0, 195);
     ok(!ret, "TerminateProcess(0) should fail\n");
     ok(GetLastError() == ERROR_INVALID_HANDLE, "expected ERROR_INVALID_HANDLE, got %d\n", GetLastError());
 
     Sleep(100);
 
+    affinity = 1;
+    ret = pNtSetInformationProcess(process, ProcessAffinityMask, &affinity, sizeof(affinity));
+    ok(!ret, "NtSetInformationProcess error %#x\n", ret);
+
     switch (test_dll_phase)
     {
     case 0:
@@ -1430,8 +1453,16 @@ static void child_process(const char *dll_name, DWORD target_offset)
         ok(!ret, "RtlDllShutdownInProgress returned %d\n", ret);
 
         trace("call NtTerminateProcess(0, 195)\n");
-        status = pNtTerminateProcess(0, 195);
-        ok(!status, "NtTerminateProcess error %#x\n", status);
+        ret = pNtTerminateProcess(0, 195);
+        ok(!ret, "NtTerminateProcess error %#x\n", ret);
+
+        memset(&pbi, 0, sizeof(pbi));
+        ret = pNtQueryInformationProcess(process, ProcessBasicInformation, &pbi, sizeof(pbi), NULL);
+        ok(!ret, "NtQueryInformationProcess error %#x\n", ret);
+        ok(pbi.ExitStatus == STILL_ACTIVE, "expected STILL_ACTIVE, got %lu\n", pbi.ExitStatus);
+        affinity = 1;
+        ret = pNtSetInformationProcess(process, ProcessAffinityMask, &affinity, sizeof(affinity));
+        ok(!ret, "NtSetInformationProcess error %#x\n", ret);
 
         ret = pRtlDllShutdownInProgress();
         ok(!ret, "RtlDllShutdownInProgress returned %d\n", ret);
@@ -1460,6 +1491,14 @@ todo_wine
 
         hmod = GetModuleHandle(dll_name);
         ok(hmod != 0, "DLL should not be unloaded\n");
+
+        memset(&pbi, 0, sizeof(pbi));
+        ret = pNtQueryInformationProcess(process, ProcessBasicInformation, &pbi, sizeof(pbi), NULL);
+        ok(!ret, "NtQueryInformationProcess error %#x\n", ret);
+        ok(pbi.ExitStatus == STILL_ACTIVE, "expected STILL_ACTIVE, got %lu\n", pbi.ExitStatus);
+        affinity = 1;
+        ret = pNtSetInformationProcess(process, ProcessAffinityMask, &affinity, sizeof(affinity));
+        ok(!ret, "NtSetInformationProcess error %#x\n", ret);
         break;
 
     case 1:
@@ -1547,12 +1586,18 @@ static void test_ExitProcess(void)
 #include "poppack.h"
     static const char filler[0x1000];
     DWORD dummy, file_align;
-    HANDLE file;
+    HANDLE file, thread, hmap;
     char temp_path[MAX_PATH], dll_name[MAX_PATH], cmdline[MAX_PATH * 2];
-    DWORD ret, target_offset;
-    char **argv;
+    DWORD ret, target_offset, old_prot;
+    char **argv, buf[256];
     PROCESS_INFORMATION pi;
     STARTUPINFO si = { sizeof(si) };
+    CONTEXT ctx;
+    struct PROCESS_BASIC_INFORMATION_PRIVATE pbi;
+    DWORD_PTR affinity;
+    void *addr;
+    LARGE_INTEGER offset;
+    SIZE_T size;
 
 #if !defined(__i386__) && !defined(__x86_64__)
     skip("x86 specific ExitProcess test\n");
@@ -1561,7 +1606,17 @@ static void test_ExitProcess(void)
 
     if (!pRtlDllShutdownInProgress)
     {
-        skip("RtlDllShutdownInProgress is not available on this platform (XP+)\n");
+        win_skip("RtlDllShutdownInProgress is not available on this platform (XP+)\n");
+        return;
+    }
+    if (!pNtQueryInformationProcess || !pNtSetInformationProcess)
+    {
+        win_skip("NtQueryInformationProcess/NtSetInformationProcess are not available on this platform\n");
+        return;
+    }
+    if (!pNtAllocateVirtualMemory || !pNtFreeVirtualMemory)
+    {
+        win_skip("NtAllocateVirtualMemory/NtFreeVirtualMemory are not available on this platform\n");
         return;
     }
 
@@ -1693,6 +1748,168 @@ static void test_ExitProcess(void)
     CloseHandle(pi.hThread);
     CloseHandle(pi.hProcess);
 
+    /* test remote process termination */
+    SetLastError(0xdeadbeef);
+    ret = CreateProcess(argv[0], NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi);
+    ok(ret, "CreateProcess(%s) error %d\n", argv[0], GetLastError());
+
+    SetLastError(0xdeadbeef);
+    addr = VirtualAllocEx(pi.hProcess, NULL, 4096, MEM_COMMIT, PAGE_READWRITE);
+    ok(addr != NULL, "VirtualAllocEx error %d\n", GetLastError());
+    SetLastError(0xdeadbeef);
+    ret = VirtualProtectEx(pi.hProcess, addr, 4096, PAGE_READONLY, &old_prot);
+    ok(ret, "VirtualProtectEx error %d\n", GetLastError());
+    ok(old_prot == PAGE_READWRITE, "expected PAGE_READWRITE, got %#x\n", old_prot);
+    SetLastError(0xdeadbeef);
+    ret = ReadProcessMemory(pi.hProcess, addr, buf, 4, &size);
+    ok(ret, "ReadProcessMemory error %d\n", GetLastError());
+    ok(size == 4, "expected 4, got %lu\n", size);
+
+    SetLastError(0xdeadbeef);
+    hmap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 4096, NULL);
+    ok(hmap != 0, "CreateFileMapping error %d\n", GetLastError());
+
+    offset.u.LowPart = 0;
+    offset.u.HighPart = 0;
+    addr = NULL;
+    size = 0;
+    ret = pNtMapViewOfSection(hmap, pi.hProcess, &addr, 0, 0, &offset,
+                              &size, 1 /* ViewShare */, 0, PAGE_READONLY);
+    ok(!ret, "NtMapViewOfSection error %#x\n", ret);
+    ret = pNtUnmapViewOfSection(pi.hProcess, addr);
+    ok(!ret, "NtUnmapViewOfSection error %#x\n", ret);
+
+    SetLastError(0xdeadbeef);
+    thread = CreateRemoteThread(pi.hProcess, NULL, 0, (void *)0xdeadbeef, NULL, CREATE_SUSPENDED, &ret);
+    ok(thread != 0, "CreateRemoteThread error %d\n", GetLastError());
+    SetLastError(0xdeadbeef);
+    ctx.ContextFlags = CONTEXT_INTEGER;
+    ret = GetThreadContext(thread, &ctx);
+    ok(ret, "GetThreadContext error %d\n", GetLastError());
+    SetLastError(0xdeadbeef);
+    ctx.ContextFlags = CONTEXT_INTEGER;
+    ret = SetThreadContext(thread, &ctx);
+    ok(ret, "SetThreadContext error %d\n", GetLastError());
+    SetLastError(0xdeadbeef);
+    ret = SetThreadPriority(thread, 0);
+    ok(ret, "SetThreadPriority error %d\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = TerminateThread(thread, 199);
+    ok(ret, "TerminateThread error %d\n", GetLastError());
+    /* Calling GetExitCodeThread() without waiting for thread termination
+     * leads to different results due to a race condition.
+     */
+    ret = WaitForSingleObject(thread, 100);
+    ok(ret == WAIT_OBJECT_0, "WaitForSingleObject failed: %x\n", ret);
+    GetExitCodeThread(thread, &ret);
+    ok(ret == 199, "expected exit code 199, got %u\n", ret);
+
+    SetLastError(0xdeadbeef);
+    ret = TerminateProcess(pi.hProcess, 198);
+    ok(ret, "TerminateProcess error %d\n", GetLastError());
+    /* Checking process state without waiting for process termination
+     * leads to different results due to a race condition.
+     */
+    ret = WaitForSingleObject(pi.hProcess, 100);
+    ok(ret == WAIT_OBJECT_0, "WaitForSingleObject failed: %x\n", ret);
+
+    memset(&pbi, 0, sizeof(pbi));
+    ret = pNtQueryInformationProcess(pi.hProcess, ProcessBasicInformation, &pbi, sizeof(pbi), NULL);
+    ok(!ret, "NtQueryInformationProcess error %#x\n", ret);
+    ok(pbi.ExitStatus == 198, "expected 198, got %lu\n", pbi.ExitStatus);
+    affinity = 1;
+    ret = pNtSetInformationProcess(pi.hProcess, ProcessAffinityMask, &affinity, sizeof(affinity));
+todo_wine
+    ok(ret == STATUS_PROCESS_IS_TERMINATING, "expected STATUS_PROCESS_IS_TERMINATING, got %#x\n", ret);
+
+    SetLastError(0xdeadbeef);
+    ctx.ContextFlags = CONTEXT_INTEGER;
+    ret = GetThreadContext(thread, &ctx);
+    ok(!ret || broken(ret) /* XP 64-bit */, "GetThreadContext should fail\n");
+    if (!ret)
+        ok(GetLastError() == ERROR_INVALID_PARAMETER ||
+           GetLastError() == ERROR_GEN_FAILURE /* win7 64-bit */ ||
+           GetLastError() == ERROR_INVALID_FUNCTION /* vista 64-bit */,
+           "expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+    SetLastError(0xdeadbeef);
+    ctx.ContextFlags = CONTEXT_INTEGER;
+    ret = SetThreadContext(thread, &ctx);
+    ok(!ret || broken(ret) /* XP 64-bit */, "SetThreadContext should fail\n");
+    if (!ret)
+        ok(GetLastError() == ERROR_ACCESS_DENIED ||
+           GetLastError() == ERROR_GEN_FAILURE /* win7 64-bit */ ||
+           GetLastError() == ERROR_INVALID_FUNCTION /* vista 64-bit */,
+           "expected ERROR_ACCESS_DENIED, got %d\n", GetLastError());
+    SetLastError(0xdeadbeef);
+    ret = SetThreadPriority(thread, 0);
+    ok(ret, "SetThreadPriority error %d\n", GetLastError());
+    CloseHandle(thread);
+
+    SetLastError(0xdeadbeef);
+    ctx.ContextFlags = CONTEXT_INTEGER;
+    ret = GetThreadContext(pi.hThread, &ctx);
+    ok(!ret || broken(ret) /* XP 64-bit */, "GetThreadContext should fail\n");
+    if (!ret)
+        ok(GetLastError() == ERROR_INVALID_PARAMETER ||
+           GetLastError() == ERROR_GEN_FAILURE /* win7 64-bit */ ||
+           GetLastError() == ERROR_INVALID_FUNCTION /* vista 64-bit */,
+           "expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+    SetLastError(0xdeadbeef);
+    ctx.ContextFlags = CONTEXT_INTEGER;
+    ret = SetThreadContext(pi.hThread, &ctx);
+    ok(!ret || broken(ret) /* XP 64-bit */, "SetThreadContext should fail\n");
+    if (!ret)
+        ok(GetLastError() == ERROR_ACCESS_DENIED ||
+           GetLastError() == ERROR_GEN_FAILURE /* win7 64-bit */ ||
+           GetLastError() == ERROR_INVALID_FUNCTION /* vista 64-bit */,
+           "expected ERROR_ACCESS_DENIED, got %d\n", GetLastError());
+    SetLastError(0xdeadbeef);
+    ret = VirtualProtectEx(pi.hProcess, addr, 4096, PAGE_READWRITE, &old_prot);
+    ok(!ret, "VirtualProtectEx should fail\n");
+    ok(GetLastError() == ERROR_ACCESS_DENIED, "expected ERROR_ACCESS_DENIED, got %d\n", GetLastError());
+    SetLastError(0xdeadbeef);
+    size = 0;
+    ret = ReadProcessMemory(pi.hProcess, addr, buf, 4, &size);
+    ok(!ret, "ReadProcessMemory should fail\n");
+    ok(GetLastError() == ERROR_PARTIAL_COPY || GetLastError() == ERROR_ACCESS_DENIED,
+       "expected ERROR_PARTIAL_COPY, got %d\n", GetLastError());
+    ok(!size, "expected 0, got %lu\n", size);
+    SetLastError(0xdeadbeef);
+    ret = VirtualFreeEx(pi.hProcess, addr, 0, MEM_RELEASE);
+    ok(!ret, "VirtualFreeEx should fail\n");
+    ok(GetLastError() == ERROR_ACCESS_DENIED, "expected ERROR_ACCESS_DENIED, got %d\n", GetLastError());
+    SetLastError(0xdeadbeef);
+    addr = VirtualAllocEx(pi.hProcess, NULL, 4096, MEM_COMMIT, PAGE_READWRITE);
+    ok(!addr, "VirtualAllocEx should fail\n");
+    ok(GetLastError() == ERROR_ACCESS_DENIED, "expected ERROR_ACCESS_DENIED, got %d\n", GetLastError());
+
+    offset.u.LowPart = 0;
+    offset.u.HighPart = 0;
+    addr = NULL;
+    size = 0;
+    ret = pNtMapViewOfSection(hmap, pi.hProcess, &addr, 0, 0, &offset,
+                              &size, 1 /* ViewShare */, 0, PAGE_READONLY);
+todo_wine
+    ok(ret == STATUS_PROCESS_IS_TERMINATING, "expected STATUS_PROCESS_IS_TERMINATING, got %#x\n", ret);
+
+    SetLastError(0xdeadbeef);
+    thread = CreateRemoteThread(pi.hProcess, NULL, 0, (void *)0xdeadbeef, NULL, CREATE_SUSPENDED, &ret);
+    ok(!thread, "CreateRemoteThread should fail\n");
+    ok(GetLastError() == ERROR_ACCESS_DENIED, "expected ERROR_ACCESS_DENIED, got %d\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = DebugActiveProcess(pi.dwProcessId);
+    ok(!ret, "DebugActiveProcess should fail\n");
+    ok(GetLastError() == ERROR_ACCESS_DENIED /* 64-bit */ || GetLastError() == ERROR_NOT_SUPPORTED /* 32-bit */,
+      "ERROR_ACCESS_DENIED, got %d\n", GetLastError());
+
+    GetExitCodeProcess(pi.hProcess, &ret);
+    ok(ret == 198 || broken(ret != 198) /* some 32-bit XP version in a VM returns random exit code */,
+       "expected exit code 198, got %u\n", ret);
+    CloseHandle(pi.hThread);
+    CloseHandle(pi.hProcess);
+
     ret = DeleteFile(dll_name);
     ok(ret, "DeleteFile error %d\n", GetLastError());
 }
@@ -1706,8 +1923,12 @@ START_TEST(loader)
     pNtMapViewOfSection = (void *)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtMapViewOfSection");
     pNtUnmapViewOfSection = (void *)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtUnmapViewOfSection");
     pNtTerminateProcess = (void *)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtTerminateProcess");
+    pNtQueryInformationProcess = (void *)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtQueryInformationProcess");
+    pNtSetInformationProcess = (void *)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtSetInformationProcess");
     pLdrShutdownProcess = (void *)GetProcAddress(GetModuleHandle("ntdll.dll"), "LdrShutdownProcess");
     pRtlDllShutdownInProgress = (void *)GetProcAddress(GetModuleHandle("ntdll.dll"), "RtlDllShutdownInProgress");
+    pNtAllocateVirtualMemory = (void *)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtAllocateVirtualMemory");
+    pNtFreeVirtualMemory = (void *)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtFreeVirtualMemory");
 
     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