1 /*
2  * PROJECT:         ReactOS api tests
3  * LICENSE:         LGPLv2.1+ - See COPYING.LIB in the top level directory
4  * PURPOSE:         Test for TerminateProcess
5  * PROGRAMMER:      Thomas Faber <thomas.faber@reactos.org>
6  */
7 
8 #include "precomp.h"
9 
10 #include <ndk/obfuncs.h>
11 
12 static
13 HANDLE
14 StartChild(
15     _In_ PCWSTR Argument,
16     _In_ DWORD Flags,
17     _Out_opt_ PDWORD ProcessId)
18 {
19     BOOL Success;
20     WCHAR FileName[MAX_PATH];
21     WCHAR CommandLine[MAX_PATH];
22     STARTUPINFOW StartupInfo;
23     PROCESS_INFORMATION ProcessInfo;
24 
25     GetModuleFileNameW(NULL, FileName, _countof(FileName));
26     StringCbPrintfW(CommandLine,
27                     sizeof(CommandLine),
28                     L"\"%ls\" TerminateProcess %ls",
29                     FileName,
30                     Argument);
31 
32     RtlZeroMemory(&StartupInfo, sizeof(StartupInfo));
33     StartupInfo.cb = sizeof(StartupInfo);
34     /* HACK: running the test under rosautotest seems to keep another reference
35      * to the child process around until the test finishes (on both ROS and
36      * Windows)... I'm too lazy to investigate very much so let's just redirect
37      * the child std handles to nowhere. ok() is useless in half the child
38      * processes anyway.
39      */
40     StartupInfo.dwFlags = STARTF_USESTDHANDLES;
41 
42     Success = CreateProcessW(FileName,
43                              CommandLine,
44                              NULL,
45                              NULL,
46                              FALSE,
47                              Flags,
48                              NULL,
49                              NULL,
50                              &StartupInfo,
51                              &ProcessInfo);
52     if (!Success)
53     {
54         skip("CreateProcess failed with %lu\n", GetLastError());
55         if (ProcessId)
56             *ProcessId = 0;
57         return NULL;
58     }
59     CloseHandle(ProcessInfo.hThread);
60     if (ProcessId)
61         *ProcessId = ProcessInfo.dwProcessId;
62     return ProcessInfo.hProcess;
63 }
64 
65 static
66 VOID
67 TraceHandleCount_(
68     _In_ HANDLE hObject,
69     _In_ PCSTR File,
70     _In_ INT Line)
71 {
72     NTSTATUS Status;
73     OBJECT_BASIC_INFORMATION BasicInfo;
74 
75     Status = NtQueryObject(hObject,
76                            ObjectBasicInformation,
77                            &BasicInfo,
78                            sizeof(BasicInfo),
79                            NULL);
80     if (!NT_SUCCESS(Status))
81     {
82         ok_(File, Line)(0, "NtQueryObject failed with status 0x%lx\n", Status);
83         return;
84     }
85     ok_(File, Line)(0, "Handle %p still has %lu open handles, %lu references\n", hObject, BasicInfo.HandleCount, BasicInfo.PointerCount);
86 }
87 
88 #define WaitExpectSuccess(h, ms) WaitExpect_(h, ms, WAIT_OBJECT_0, __FILE__, __LINE__)
89 #define WaitExpectTimeout(h, ms) WaitExpect_(h, ms, WAIT_TIMEOUT, __FILE__, __LINE__)
90 static
91 VOID
92 WaitExpect_(
93     _In_ HANDLE hWait,
94     _In_ DWORD Milliseconds,
95     _In_ DWORD ExpectedError,
96     _In_ PCSTR File,
97     _In_ INT Line)
98 {
99     DWORD Error;
100 
101     Error = WaitForSingleObject(hWait, Milliseconds);
102     ok_(File, Line)(Error == ExpectedError, "Wait for %p return %lu\n", hWait, Error);
103 }
104 
105 #define CloseProcessAndVerify(hp, pid, code) CloseProcessAndVerify_(hp, pid, code, __FILE__, __LINE__)
106 static
107 VOID
108 CloseProcessAndVerify_(
109     _In_ HANDLE hProcess,
110     _In_ DWORD ProcessId,
111     _In_ UINT ExpectedExitCode,
112     _In_ PCSTR File,
113     _In_ INT Line)
114 {
115     int i = 0;
116     DWORD Error;
117     DWORD ExitCode;
118     BOOL Success;
119 
120     WaitExpect_(hProcess, 0, WAIT_OBJECT_0, File, Line);
121     Success = GetExitCodeProcess(hProcess, &ExitCode);
122     ok_(File, Line)(Success, "GetExitCodeProcess failed with %lu\n", GetLastError());
123     CloseHandle(hProcess);
124     while ((hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, ProcessId)) != NULL)
125     {
126         if (++i >= 100)
127         {
128             TraceHandleCount_(hProcess, File, Line);
129             CloseHandle(hProcess);
130             break;
131         }
132         CloseHandle(hProcess);
133         Sleep(100);
134     }
135     Error = GetLastError();
136     ok_(File, Line)(hProcess == NULL, "OpenProcess succeeded unexpectedly for pid 0x%lx\n", ProcessId);
137     ok_(File, Line)(Error == ERROR_INVALID_PARAMETER, "Error = %lu\n", Error);
138     ok_(File, Line)(ExitCode == ExpectedExitCode, "Exit code is %lu but expected %u\n", ExitCode, ExpectedExitCode);
139 }
140 
141 static
142 VOID
143 TestTerminateProcess(
144     _In_ HANDLE hEvent)
145 {
146     HANDLE hProcess;
147     DWORD ProcessId;
148 
149     /* Regular child process that returns from the test function */
150     /* HACK: These two tests don't work if stdout is a pipe. See StartChild */
151     ResetEvent(hEvent);
152     hProcess = StartChild(L"child", 0, &ProcessId);
153     WaitExpectSuccess(hEvent, 5000);
154     WaitExpectSuccess(hProcess, 5000);
155     CloseProcessAndVerify(hProcess, ProcessId, 0);
156 
157     ResetEvent(hEvent);
158     hProcess = StartChild(L"child", 0, &ProcessId);
159     WaitExpectSuccess(hProcess, 5000);
160     WaitExpectSuccess(hEvent, 0);
161     CloseProcessAndVerify(hProcess, ProcessId, 0);
162 
163     /* Suspended process -- never gets a chance to initialize */
164     ResetEvent(hEvent);
165     hProcess = StartChild(L"child", CREATE_SUSPENDED, &ProcessId);
166     WaitExpectTimeout(hEvent, 100);
167     WaitExpectTimeout(hProcess, 100);
168     TerminateProcess(hProcess, 123);
169     WaitExpectSuccess(hProcess, 5000);
170     CloseProcessAndVerify(hProcess, ProcessId, 123);
171 
172     /* Waiting process -- we have to terminate it */
173     ResetEvent(hEvent);
174     hProcess = StartChild(L"wait", 0, &ProcessId);
175     WaitExpectTimeout(hProcess, 100);
176     TerminateProcess(hProcess, 123);
177     WaitExpectSuccess(hProcess, 5000);
178     CloseProcessAndVerify(hProcess, ProcessId, 123);
179 
180     /* Process calls ExitProcess */
181     ResetEvent(hEvent);
182     hProcess = StartChild(L"child exit 456", 0, &ProcessId);
183     WaitExpectSuccess(hEvent, 5000);
184     WaitExpectSuccess(hProcess, 5000);
185     CloseProcessAndVerify(hProcess, ProcessId, 456);
186 
187     /* Process calls TerminateProcess with GetCurrentProcess */
188     ResetEvent(hEvent);
189     hProcess = StartChild(L"child terminate 456", 0, &ProcessId);
190     WaitExpectSuccess(hEvent, 5000);
191     WaitExpectSuccess(hProcess, 5000);
192     CloseProcessAndVerify(hProcess, ProcessId, 456);
193 
194     /* Process calls TerminateProcess with real handle to itself */
195     ResetEvent(hEvent);
196     hProcess = StartChild(L"child terminate2 456", 0, &ProcessId);
197     WaitExpectSuccess(hEvent, 5000);
198     WaitExpectSuccess(hProcess, 5000);
199     CloseProcessAndVerify(hProcess, ProcessId, 456);
200 }
201 
202 START_TEST(TerminateProcess)
203 {
204     HANDLE hEvent;
205     BOOL Success;
206     DWORD Error;
207     int argc;
208     char **argv;
209 
210     hEvent = CreateEventW(NULL, TRUE, FALSE, L"kernel32_apitest_TerminateProcess_event");
211     Error = GetLastError();
212     if (!hEvent)
213     {
214         skip("CreateEvent failed with error %lu\n", Error);
215         return;
216     }
217     argc = winetest_get_mainargs(&argv);
218     if (argc >= 3)
219     {
220         ok(Error == ERROR_ALREADY_EXISTS, "Error = %lu\n", Error);
221         if (!strcmp(argv[2], "wait"))
222         {
223             WaitExpectSuccess(hEvent, 30000);
224         }
225         else
226         {
227             Success = SetEvent(hEvent);
228             ok(Success, "SetEvent failed with return %d, error %lu\n", Success, GetLastError());
229         }
230     }
231     else
232     {
233         ok(Error == NO_ERROR, "Error = %lu\n", Error);
234         TestTerminateProcess(hEvent);
235     }
236     CloseHandle(hEvent);
237     if (argc >= 5)
238     {
239         UINT ExitCode = strtol(argv[4], NULL, 10);
240 
241         fflush(stdout);
242         if (!strcmp(argv[3], "exit"))
243             ExitProcess(ExitCode);
244         else if (!strcmp(argv[3], "terminate"))
245             TerminateProcess(GetCurrentProcess(), ExitCode);
246         else if (!strcmp(argv[3], "terminate2"))
247         {
248             HANDLE hProcess;
249             hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, GetCurrentProcessId());
250             TerminateProcess(hProcess, ExitCode);
251         }
252         ok(0, "Should have terminated\n");
253     }
254 }
255