Author: tfaber
Date: Fri Feb 20 10:29:47 2015
New Revision: 66367
URL:
http://svn.reactos.org/svn/reactos?rev=66367&view=rev
Log:
[KERNEL32_APITEST]
- Add a test for process termination behavior. This shows that CreateProcess with
CREATE_SUSPENDED followed by TerminateProcess causes a handle leak.
CORE-9234
Added:
trunk/rostests/apitests/kernel32/TerminateProcess.c (with props)
Modified:
trunk/rostests/apitests/kernel32/CMakeLists.txt
trunk/rostests/apitests/kernel32/testlist.c
Modified: trunk/rostests/apitests/kernel32/CMakeLists.txt
URL:
http://svn.reactos.org/svn/reactos/trunk/rostests/apitests/kernel32/CMakeLi…
==============================================================================
--- trunk/rostests/apitests/kernel32/CMakeLists.txt [iso-8859-1] (original)
+++ trunk/rostests/apitests/kernel32/CMakeLists.txt [iso-8859-1] Fri Feb 20 10:29:47 2015
@@ -10,6 +10,7 @@
MultiByteToWideChar.c
SetCurrentDirectory.c
SetUnhandledExceptionFilter.c
+ TerminateProcess.c
testlist.c)
add_executable(kernel32_apitest ${SOURCE})
Added: trunk/rostests/apitests/kernel32/TerminateProcess.c
URL:
http://svn.reactos.org/svn/reactos/trunk/rostests/apitests/kernel32/Termina…
==============================================================================
--- trunk/rostests/apitests/kernel32/TerminateProcess.c (added)
+++ trunk/rostests/apitests/kernel32/TerminateProcess.c [iso-8859-1] Fri Feb 20 10:29:47
2015
@@ -0,0 +1,255 @@
+/*
+ * PROJECT: ReactOS api tests
+ * LICENSE: LGPLv2.1+ - See COPYING.LIB in the top level directory
+ * PURPOSE: Test for TerminateProcess
+ * PROGRAMMER: Thomas Faber <thomas.faber(a)reactos.org>
+ */
+
+#include <apitest.h>
+
+#include <ndk/obfuncs.h>
+#include <strsafe.h>
+
+static
+HANDLE
+StartChild(
+ _In_ PCWSTR Argument,
+ _In_ DWORD Flags,
+ _Out_opt_ PDWORD ProcessId)
+{
+ BOOL Success;
+ WCHAR FileName[MAX_PATH];
+ WCHAR CommandLine[MAX_PATH];
+ STARTUPINFOW StartupInfo;
+ PROCESS_INFORMATION ProcessInfo;
+
+ GetModuleFileNameW(NULL, FileName, _countof(FileName));
+ StringCbPrintfW(CommandLine,
+ sizeof(CommandLine),
+ L"\"%ls\" TerminateProcess %ls",
+ FileName,
+ Argument);
+
+ RtlZeroMemory(&StartupInfo, sizeof(StartupInfo));
+ StartupInfo.cb = sizeof(StartupInfo);
+ /* HACK: running the test under rosautotest seems to keep another reference
+ * to the child process around until the test finishes (on both ROS and
+ * Windows)... I'm too lazy to investigate very much so let's just redirect
+ * the child std handles to nowhere. ok() is useless in half the child
+ * processes anyway.
+ */
+ StartupInfo.dwFlags = STARTF_USESTDHANDLES;
+
+ Success = CreateProcessW(FileName,
+ CommandLine,
+ NULL,
+ NULL,
+ FALSE,
+ Flags,
+ NULL,
+ NULL,
+ &StartupInfo,
+ &ProcessInfo);
+ if (!Success)
+ {
+ skip("CreateProcess failed with %lu\n", GetLastError());
+ if (ProcessId)
+ *ProcessId = 0;
+ return NULL;
+ }
+ CloseHandle(ProcessInfo.hThread);
+ if (ProcessId)
+ *ProcessId = ProcessInfo.dwProcessId;
+ return ProcessInfo.hProcess;
+}
+
+static
+VOID
+TraceHandleCount_(
+ _In_ HANDLE hObject,
+ _In_ PCSTR File,
+ _In_ INT Line)
+{
+ NTSTATUS Status;
+ OBJECT_BASIC_INFORMATION BasicInfo;
+
+ Status = NtQueryObject(hObject,
+ ObjectBasicInformation,
+ &BasicInfo,
+ sizeof(BasicInfo),
+ NULL);
+ if (!NT_SUCCESS(Status))
+ {
+ ok_(File, Line)(0, "NtQueryObject failed with status 0x%lx\n",
Status);
+ return;
+ }
+ ok_(File, Line)(0, "Handle %p still has %lu open handles, %lu
references\n", hObject, BasicInfo.HandleCount, BasicInfo.PointerCount);
+}
+
+#define WaitExpectSuccess(h, ms) WaitExpect_(h, ms, WAIT_OBJECT_0, __FILE__, __LINE__)
+#define WaitExpectTimeout(h, ms) WaitExpect_(h, ms, WAIT_TIMEOUT, __FILE__, __LINE__)
+static
+VOID
+WaitExpect_(
+ _In_ HANDLE hWait,
+ _In_ DWORD Milliseconds,
+ _In_ DWORD ExpectedError,
+ _In_ PCSTR File,
+ _In_ INT Line)
+{
+ DWORD Error;
+
+ Error = WaitForSingleObject(hWait, Milliseconds);
+ ok_(File, Line)(Error == ExpectedError, "Wait for %p return %lu\n", hWait,
Error);
+}
+
+#define CloseProcessAndVerify(hp, pid, code) CloseProcessAndVerify_(hp, pid, code,
__FILE__, __LINE__)
+static
+VOID
+CloseProcessAndVerify_(
+ _In_ HANDLE hProcess,
+ _In_ DWORD ProcessId,
+ _In_ UINT ExpectedExitCode,
+ _In_ PCSTR File,
+ _In_ INT Line)
+{
+ int i = 0;
+ DWORD Error;
+ DWORD ExitCode;
+ BOOL Success;
+
+ WaitExpect_(hProcess, 0, WAIT_OBJECT_0, File, Line);
+ Success = GetExitCodeProcess(hProcess, &ExitCode);
+ ok_(File, Line)(Success, "GetExitCodeProcess failed with %lu\n",
GetLastError());
+ CloseHandle(hProcess);
+ while ((hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, ProcessId)) !=
NULL)
+ {
+ if (++i >= 100)
+ {
+ TraceHandleCount_(hProcess, File, Line);
+ CloseHandle(hProcess);
+ break;
+ }
+ CloseHandle(hProcess);
+ Sleep(100);
+ }
+ Error = GetLastError();
+ ok_(File, Line)(hProcess == NULL, "OpenProcess succeeded unexpectedly for pid
0x%lx\n", ProcessId);
+ ok_(File, Line)(Error == ERROR_INVALID_PARAMETER, "Error = %lu\n", Error);
+ ok_(File, Line)(ExitCode == ExpectedExitCode, "Exit code is %lu but expected
%lu\n", ExitCode, ExpectedExitCode);
+}
+
+static
+VOID
+TestTerminateProcess(
+ _In_ HANDLE hEvent)
+{
+ HANDLE hProcess;
+ DWORD ProcessId;
+
+ /* Regular child process that returns from the test function */
+ /* HACK: These two tests don't work if stdout is a pipe. See StartChild */
+ ResetEvent(hEvent);
+ hProcess = StartChild(L"child", 0, &ProcessId);
+ WaitExpectSuccess(hEvent, 5000);
+ WaitExpectSuccess(hProcess, 5000);
+ CloseProcessAndVerify(hProcess, ProcessId, 0);
+
+ ResetEvent(hEvent);
+ hProcess = StartChild(L"child", 0, &ProcessId);
+ WaitExpectSuccess(hProcess, 5000);
+ WaitExpectSuccess(hEvent, 0);
+ CloseProcessAndVerify(hProcess, ProcessId, 0);
+
+ /* Suspended process -- never gets a chance to initialize */
+ ResetEvent(hEvent);
+ hProcess = StartChild(L"child", CREATE_SUSPENDED, &ProcessId);
+ WaitExpectTimeout(hEvent, 100);
+ WaitExpectTimeout(hProcess, 100);
+ TerminateProcess(hProcess, 123);
+ WaitExpectSuccess(hProcess, 5000);
+ CloseProcessAndVerify(hProcess, ProcessId, 123);
+
+ /* Waiting process -- we have to terminate it */
+ ResetEvent(hEvent);
+ hProcess = StartChild(L"wait", 0, &ProcessId);
+ WaitExpectTimeout(hProcess, 100);
+ TerminateProcess(hProcess, 123);
+ WaitExpectSuccess(hProcess, 5000);
+ CloseProcessAndVerify(hProcess, ProcessId, 123);
+
+ /* Process calls ExitProcess */
+ ResetEvent(hEvent);
+ hProcess = StartChild(L"child exit 456", 0, &ProcessId);
+ WaitExpectSuccess(hEvent, 5000);
+ WaitExpectSuccess(hProcess, 5000);
+ CloseProcessAndVerify(hProcess, ProcessId, 456);
+
+ /* Process calls TerminateProcess with GetCurrentProcess */
+ ResetEvent(hEvent);
+ hProcess = StartChild(L"child terminate 456", 0, &ProcessId);
+ WaitExpectSuccess(hEvent, 5000);
+ WaitExpectSuccess(hProcess, 5000);
+ CloseProcessAndVerify(hProcess, ProcessId, 456);
+
+ /* Process calls TerminateProcess with real handle to itself */
+ ResetEvent(hEvent);
+ hProcess = StartChild(L"child terminate2 456", 0, &ProcessId);
+ WaitExpectSuccess(hEvent, 5000);
+ WaitExpectSuccess(hProcess, 5000);
+ CloseProcessAndVerify(hProcess, ProcessId, 456);
+}
+
+START_TEST(TerminateProcess)
+{
+ HANDLE hEvent;
+ BOOL Success;
+ DWORD Error;
+ int argc;
+ char **argv;
+
+ hEvent = CreateEventW(NULL, TRUE, FALSE,
L"kernel32_apitest_TerminateProcess_event");
+ Error = GetLastError();
+ if (!hEvent)
+ {
+ skip("CreateEvent failed with error %lu\n", Error);
+ return;
+ }
+ argc = winetest_get_mainargs(&argv);
+ if (argc >= 3)
+ {
+ ok(Error == ERROR_ALREADY_EXISTS, "Error = %lu\n", Error);
+ if (!strcmp(argv[2], "wait"))
+ {
+ WaitExpectSuccess(hEvent, 30000);
+ }
+ else
+ {
+ Success = SetEvent(hEvent);
+ ok(Success, "SetEvent failed with return %d, error %lu\n", Success,
GetLastError());
+ }
+ }
+ else
+ {
+ ok(Error == NO_ERROR, "Error = %lu\n", Error);
+ TestTerminateProcess(hEvent);
+ }
+ CloseHandle(hEvent);
+ if (argc >= 5)
+ {
+ UINT ExitCode = strtol(argv[4], NULL, 10);
+
+ fflush(stdout);
+ if (!strcmp(argv[3], "exit"))
+ ExitProcess(ExitCode);
+ else if (!strcmp(argv[3], "terminate"))
+ TerminateProcess(GetCurrentProcess(), ExitCode);
+ else if (!strcmp(argv[3], "terminate2"))
+ {
+ HANDLE hProcess;
+ hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, GetCurrentProcessId());
+ TerminateProcess(hProcess, ExitCode);
+ }
+ ok(0, "Should have terminated\n");
+ }
+}
Propchange: trunk/rostests/apitests/kernel32/TerminateProcess.c
------------------------------------------------------------------------------
svn:eol-style = native
Modified: trunk/rostests/apitests/kernel32/testlist.c
URL:
http://svn.reactos.org/svn/reactos/trunk/rostests/apitests/kernel32/testlis…
==============================================================================
--- trunk/rostests/apitests/kernel32/testlist.c [iso-8859-1] (original)
+++ trunk/rostests/apitests/kernel32/testlist.c [iso-8859-1] Fri Feb 20 10:29:47 2015
@@ -13,6 +13,7 @@
extern void func_MultiByteToWideChar(void);
extern void func_SetCurrentDirectory(void);
extern void func_SetUnhandledExceptionFilter(void);
+extern void func_TerminateProcess(void);
const struct test winetest_testlist[] =
{
@@ -25,7 +26,8 @@
{ "lstrcpynW", func_lstrcpynW },
{ "MultiByteToWideChar", func_MultiByteToWideChar },
{ "SetCurrentDirectory", func_SetCurrentDirectory },
- { "SetUnhandledExceptionFilter", func_SetUnhandledExceptionFilter},
+ { "SetUnhandledExceptionFilter", func_SetUnhandledExceptionFilter },
+ { "TerminateProcess", func_TerminateProcess },
{ 0, 0 }
};