kernel32: Add a test for threads state when a process is being terminated. Take 2.

Dmitry Timoshkov dmitry at baikal.ru
Wed Apr 10 00:54:33 CDT 2013


This test demonstrates the problem reported in the bug 33331.

This version of the patch moves the ExitProcess test into a child process.
---
 dlls/kernel32/tests/loader.c | 231 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 228 insertions(+), 3 deletions(-)

diff --git a/dlls/kernel32/tests/loader.c b/dlls/kernel32/tests/loader.c
index 92a2e0b..e81b839 100644
--- a/dlls/kernel32/tests/loader.c
+++ b/dlls/kernel32/tests/loader.c
@@ -19,6 +19,7 @@
  */
 
 #include <stdarg.h>
+#include <stdio.h>
 #include <assert.h>
 
 #include "ntstatus.h"
@@ -265,7 +266,6 @@ static void test_Loader(void)
     BOOL ret;
 
     GetSystemInfo(&si);
-    trace("system page size 0x%04x\n", si.dwPageSize);
 
     /* prevent displaying of the "Unable to load this DLL" message box */
     SetErrorMode(SEM_FAILCRITICALERRORS);
@@ -736,7 +736,6 @@ static void test_VirtualProtect(void *base, void *section)
     SYSTEM_INFO si;
 
     GetSystemInfo(&si);
-    trace("system page size %#x\n", si.dwPageSize);
 
     SetLastError(0xdeadbeef);
     ret = VirtualProtect(section, si.dwPageSize, PAGE_NOACCESS, &old_prot);
@@ -874,7 +873,6 @@ static void test_section_access(void)
     DWORD ret;
 
     GetSystemInfo(&si);
-    trace("system page size %#x\n", si.dwPageSize);
 
     /* prevent displaying of the "Unable to load this DLL" message box */
     SetErrorMode(SEM_FAILCRITICALERRORS);
@@ -1037,12 +1035,239 @@ nt4_is_broken:
     }
 }
 
+#define MAX_COUNT 10
+static HANDLE hthread[MAX_COUNT];
+static DWORD count;
+
+static DWORD WINAPI thread_proc(void *param)
+{
+    SetEvent(param);
+
+    while (1)
+    {
+	trace("%04u: thread_proc: still alive\n", GetCurrentThreadId());
+	Sleep(50);
+    }
+
+    return 0;
+}
+
+static BOOL WINAPI dll_entry_point(HINSTANCE hinst, DWORD reason, LPVOID param)
+{
+    switch (reason)
+    {
+    case DLL_PROCESS_ATTACH:
+        trace("dll: %p, DLL_PROCESS_ATTACH, %p\n", hinst, param);
+        break;
+    case DLL_PROCESS_DETACH:
+    {
+        BOOL ret;
+        DWORD code, i;
+        trace("dll: %p, DLL_PROCESS_DETACH, %p\n", hinst, param);
+
+        ok(count != 0, "attached thread count should not be 0\n");
+
+        for (i = 0; i < count; i++)
+        {
+            ret = GetExitCodeThread(hthread[i], &code);
+            trace("dll: GetExitCodeThread(%u) => %d,%u\n", i, ret, code);
+            ok(ret == 1, "GetExitCodeThread returned %d, expected 1\n", ret);
+        todo_wine
+            ok(code == 0, "expected thread exit code 0, got %u\n", code);
+        }
+        break;
+    }
+    case DLL_THREAD_ATTACH:
+        trace("dll: %p, DLL_THREAD_ATTACH, %p\n", hinst, param);
+        if (count < MAX_COUNT)
+        {
+            DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &hthread[count],
+                            0, TRUE, DUPLICATE_SAME_ACCESS);
+            count++;
+        }
+        break;
+    case DLL_THREAD_DETACH:
+        trace("dll: %p, DLL_THREAD_DETACH, %p\n", hinst, param);
+        break;
+    default:
+        trace("dll: %p, %d, %p\n", hinst, reason, param);
+        break;
+    }
+
+    return TRUE;
+}
+
+static void child_process(const char *dll_name, DWORD target_offset)
+{
+    void *target;
+    DWORD ret, dummy;
+    HANDLE file, thread, event;
+    HMODULE hmod;
+
+    trace("writing %p at %#x\n", dll_entry_point, target_offset);
+
+    file = CreateFile(dll_name, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
+    if (file == INVALID_HANDLE_VALUE)
+    {
+        ok(0, "could not open %s\n", dll_name);
+        return;
+    }
+    SetFilePointer(file, target_offset, NULL, FILE_BEGIN);
+    SetLastError(0xdeadbeef);
+    target = dll_entry_point;
+    ret = WriteFile(file, &target, sizeof(target), &dummy, NULL);
+    ok(ret, "WriteFile error %d\n", GetLastError());
+    CloseHandle(file);
+
+    SetLastError(0xdeadbeef);
+    hmod = LoadLibrary(dll_name);
+    ok(hmod != 0, "LoadLibrary error %d\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    event = CreateEvent(NULL, 0, 0, NULL);
+    ok(event != 0, "CreateEvent error %d\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    thread = CreateThread(NULL, 0, thread_proc, event, 0, &dummy);
+    ok(thread != 0, "CreateThread error %d\n", GetLastError());
+    WaitForSingleObject(event, INFINITE);
+    CloseHandle(thread);
+
+    ResetEvent(event);
+
+    SetLastError(0xdeadbeef);
+    thread = CreateThread(NULL, 0, thread_proc, event, 0, &dummy);
+    ok(thread != 0, "CreateThread error %d\n", GetLastError());
+    WaitForSingleObject(event, INFINITE);
+    CloseHandle(thread);
+
+    CloseHandle(event);
+
+    Sleep(100);
+    trace("call ExitProcess()\n");
+    ExitProcess(0);
+}
+
+static void test_ExitProcess(void)
+{
+#include "pshpack1.h"
+#ifdef JMP_EAX
+    static struct section_data
+    {
+        BYTE mov_eax;
+        void *target;
+        BYTE jmp_eax[2];
+    } section_data = { 0xb8, dll_entry_point, { 0xff,0xe0 } };
+#else
+    static struct section_data
+    {
+        BYTE push;
+        void *target;
+        BYTE ret;
+    } section_data = { 0x68, dll_entry_point, 0xc3 };
+#endif
+#include "poppack.h"
+    static const char filler[0x1000];
+    DWORD dummy, file_align;
+    HANDLE file;
+    char temp_path[MAX_PATH], dll_name[MAX_PATH], cmdline[MAX_PATH * 2];
+    DWORD ret, target_offset;
+    char **argv;
+    PROCESS_INFORMATION pi;
+    STARTUPINFO si = { sizeof(si) };
+
+#ifndef __i386__
+    skip("x86 specific ExitProcess test\n");
+    return;
+#endif
+
+    /* prevent displaying of the "Unable to load this DLL" message box */
+    SetErrorMode(SEM_FAILCRITICALERRORS);
+
+    GetTempPath(MAX_PATH, temp_path);
+    GetTempFileName(temp_path, "ldr", 0, dll_name);
+
+    /*trace("creating %s\n", dll_name);*/
+    file = CreateFile(dll_name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0);
+    if (file == INVALID_HANDLE_VALUE)
+    {
+        ok(0, "could not create %s\n", dll_name);
+        return;
+    }
+
+    SetLastError(0xdeadbeef);
+    ret = WriteFile(file, &dos_header, sizeof(dos_header), &dummy, NULL);
+    ok(ret, "WriteFile error %d\n", GetLastError());
+
+    nt_header.FileHeader.NumberOfSections = 1;
+    nt_header.FileHeader.SizeOfOptionalHeader = sizeof(IMAGE_OPTIONAL_HEADER);
+    nt_header.FileHeader.Characteristics = IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_DLL | IMAGE_FILE_RELOCS_STRIPPED;
+
+    nt_header.OptionalHeader.AddressOfEntryPoint = 0x1000;
+    nt_header.OptionalHeader.SectionAlignment = 0x1000;
+    nt_header.OptionalHeader.FileAlignment = 0x200;
+    nt_header.OptionalHeader.SizeOfImage = sizeof(dos_header) + sizeof(nt_header) + sizeof(IMAGE_SECTION_HEADER) + 0x1000;
+    nt_header.OptionalHeader.SizeOfHeaders = sizeof(dos_header) + sizeof(nt_header) + sizeof(IMAGE_SECTION_HEADER);
+    SetLastError(0xdeadbeef);
+    ret = WriteFile(file, &nt_header, sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER), &dummy, NULL);
+    ok(ret, "WriteFile error %d\n", GetLastError());
+    SetLastError(0xdeadbeef);
+    ret = WriteFile(file, &nt_header.OptionalHeader, sizeof(IMAGE_OPTIONAL_HEADER), &dummy, NULL);
+    ok(ret, "WriteFile error %d\n", GetLastError());
+
+    section.SizeOfRawData = sizeof(section_data);
+    section.PointerToRawData = nt_header.OptionalHeader.FileAlignment;
+    section.VirtualAddress = nt_header.OptionalHeader.SectionAlignment;
+    section.Misc.VirtualSize = sizeof(section_data);
+    section.Characteristics = IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_EXECUTE;
+    SetLastError(0xdeadbeef);
+    ret = WriteFile(file, &section, sizeof(section), &dummy, NULL);
+    ok(ret, "WriteFile error %d\n", GetLastError());
+
+    file_align = nt_header.OptionalHeader.FileAlignment - nt_header.OptionalHeader.SizeOfHeaders;
+    assert(file_align < sizeof(filler));
+    SetLastError(0xdeadbeef);
+    ret = WriteFile(file, filler, file_align, &dummy, NULL);
+    ok(ret, "WriteFile error %d\n", GetLastError());
+
+    target_offset = SetFilePointer(file, 0, NULL, FILE_CURRENT) + FIELD_OFFSET(struct section_data, target);
+
+    /* section data */
+    SetLastError(0xdeadbeef);
+    ret = WriteFile(file, &section_data, sizeof(section_data), &dummy, NULL);
+    ok(ret, "WriteFile error %d\n", GetLastError());
+
+    CloseHandle(file);
+
+    winetest_get_mainargs(&argv);
+    sprintf(cmdline, "\"%s\" loader %s %d", 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());
+    winetest_wait_child_process(pi.hProcess);
+    CloseHandle(pi.hThread);
+    CloseHandle(pi.hProcess);
+
+    ret = DeleteFile(dll_name);
+    ok(ret, "DeleteFile error %d\n", GetLastError());
+}
+
 START_TEST(loader)
 {
+    int argc;
+    char **argv;
+
     pNtMapViewOfSection = (void *)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtMapViewOfSection");
     pNtUnmapViewOfSection = (void *)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtUnmapViewOfSection");
 
+    argc = winetest_get_mainargs(&argv);
+    if (argc > 3)
+    {
+        child_process(argv[2], atol(argv[3]));
+        return;
+    }
+
     test_Loader();
     test_ImportDescriptors();
     test_section_access();
+    test_ExitProcess();
 }
-- 
1.8.2.1




More information about the wine-patches mailing list