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, §ion, 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, §ion_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