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