1 | // Licensed to the .NET Foundation under one or more agreements. |
2 | // The .NET Foundation licenses this file to you under the MIT license. |
3 | // See the LICENSE file in the project root for more information. |
4 | |
5 | /*============================================================================= |
6 | ** |
7 | ** Source: test11.c |
8 | ** |
9 | ** Purpose: |
10 | ** |
11 | ** Test to ensure proper operation of the DuplicateHandle API. |
12 | ** The test launches a trivial child process, then opens |
13 | ** a handle to it using OpenProcess. It then duplicates that |
14 | ** handle and uses it to wait for the child process to terminate, |
15 | ** and then checks the exit code of the child process in order to |
16 | ** verify that it was in fact a handle to the correct |
17 | ** process. The test tries to duplicate the handle again after |
18 | ** the process has been closed, to verify that failure ensues. |
19 | ** |
20 | ** Dependencies: PAL_Initialize |
21 | ** PAL_Terminate |
22 | ** Fail |
23 | ** ZeroMemory |
24 | ** GetCurrentDirectoryW |
25 | ** CreateProcessW |
26 | ** WaitForSingleObject |
27 | ** CreateMutexW |
28 | ** ReleaseMutex |
29 | ** CloseHandle |
30 | ** GetLastError |
31 | ** strlen |
32 | ** strncpy |
33 | ** |
34 | ** |
35 | **===========================================================================*/ |
36 | #include <palsuite.h> |
37 | #include "myexitcode.h" |
38 | |
39 | |
40 | static const char* rgchPathDelim = "\\" ; |
41 | |
42 | |
43 | int |
44 | mkAbsoluteFilename( LPSTR dirName, |
45 | DWORD dwDirLength, |
46 | LPCSTR fileName, |
47 | DWORD dwFileLength, |
48 | LPSTR absPathName ) |
49 | { |
50 | DWORD sizeDN, sizeFN, sizeAPN; |
51 | |
52 | sizeDN = strlen( dirName ); |
53 | sizeFN = strlen( fileName ); |
54 | sizeAPN = (sizeDN + 1 + sizeFN + 1); |
55 | |
56 | /* ensure ((dirName + DELIM + fileName + \0) =< _MAX_PATH ) */ |
57 | if( sizeAPN > _MAX_PATH ) |
58 | { |
59 | return ( 0 ); |
60 | } |
61 | |
62 | strncpy( absPathName, dirName, dwDirLength +1 ); |
63 | strncpy( absPathName, rgchPathDelim, 2 ); |
64 | strncpy( absPathName, fileName, dwFileLength +1 ); |
65 | |
66 | return (sizeAPN); |
67 | |
68 | } |
69 | |
70 | |
71 | int __cdecl main( int argc, char **argv ) |
72 | |
73 | { |
74 | const char* rgchChildFile = "childprocess" ; |
75 | |
76 | STARTUPINFO si; |
77 | PROCESS_INFORMATION pi; |
78 | |
79 | DWORD dwError; |
80 | DWORD dwExitCode; |
81 | DWORD dwFileLength; |
82 | DWORD dwDirLength; |
83 | DWORD dwSize; |
84 | DWORD dwRet; |
85 | |
86 | HANDLE hMutex; |
87 | HANDLE hChildProcess; |
88 | HANDLE hDupChildProcess; |
89 | |
90 | char rgchDirName[_MAX_DIR]; |
91 | char absPathBuf[_MAX_PATH]; |
92 | char* rgchAbsPathName; |
93 | |
94 | BOOL ret = FAIL; |
95 | BOOL bChildDone = FALSE; |
96 | WCHAR wszMutexName[] = { 'T','E','S','T','1','1','\0' }; |
97 | |
98 | /* initialize the PAL */ |
99 | if( PAL_Initialize(argc, argv) != 0 ) |
100 | { |
101 | return( FAIL ); |
102 | } |
103 | |
104 | /* create a mutex to synchronize with the child process */ |
105 | hMutex = CreateMutexW( NULL, TRUE, wszMutexName ); |
106 | if( hMutex == NULL ) |
107 | { |
108 | Fail( "ERROR:%lu:CreateMutex() call failed\r\n" , GetLastError() ); |
109 | } |
110 | |
111 | /* zero our process and startup info structures */ |
112 | ZeroMemory( &si, sizeof(si) ); |
113 | si.cb = sizeof( si ); |
114 | ZeroMemory( &pi, sizeof(pi) ); |
115 | |
116 | /* build the absolute path to the child process */ |
117 | rgchAbsPathName = &absPathBuf[0]; |
118 | dwFileLength = strlen( rgchChildFile ); |
119 | |
120 | dwDirLength = GetCurrentDirectory( _MAX_PATH, rgchDirName ); |
121 | if( dwDirLength == 0 ) |
122 | { |
123 | dwError = GetLastError(); |
124 | if( ReleaseMutex( hMutex ) == 0 ) |
125 | { |
126 | Trace( "ERROR:%lu:ReleaseMutex() call failed\n" , GetLastError() ); |
127 | } |
128 | if( CloseHandle( hMutex ) == 0 ) |
129 | { |
130 | Trace( "ERROR:%lu:CloseHandle() call failed\n" , GetLastError() ); |
131 | } |
132 | Fail( "GetCurrentDirectory call failed with error code %d\n" , |
133 | dwError ); |
134 | } |
135 | |
136 | dwSize = mkAbsoluteFilename( rgchDirName, |
137 | dwDirLength, |
138 | rgchChildFile, |
139 | dwFileLength, |
140 | rgchAbsPathName ); |
141 | if( dwSize == 0 ) |
142 | { |
143 | if( ReleaseMutex( hMutex ) == 0 ) |
144 | { |
145 | Trace( "ERROR:%lu:ReleaseMutex() call failed\n" , GetLastError() ); |
146 | } |
147 | if( CloseHandle( hMutex ) == 0 ) |
148 | { |
149 | Trace( "ERROR:%lu:CloseHandle() call failed\n" , GetLastError() ); |
150 | } |
151 | Fail( "Palsuite Code: mkAbsoluteFilename() call failed. Could " , |
152 | "not build absolute path name to file\n. Exiting.\n" ); |
153 | } |
154 | |
155 | /* launch the child process */ |
156 | if( !CreateProcess( NULL, /* module name to execute */ |
157 | rgchAbsPathName, /* command line */ |
158 | NULL, /* process handle not */ |
159 | /* inheritable */ |
160 | NULL, /* thread handle not */ |
161 | /*inheritable */ |
162 | FALSE, /* handle inheritance */ |
163 | CREATE_NEW_CONSOLE, /* dwCreationFlags */ |
164 | NULL, /* use parent's environment */ |
165 | NULL, /* use parent's starting */ |
166 | /* directory */ |
167 | &si, /* startup info struct */ |
168 | &pi ) /* process info struct */ |
169 | ) |
170 | { |
171 | dwError = GetLastError(); |
172 | if( ReleaseMutex( hMutex ) == 0 ) |
173 | { |
174 | Trace( "ERROR:%lu:ReleaseMutex() call failed\n" , GetLastError() ); |
175 | } |
176 | if( CloseHandle( hMutex ) == 0 ) |
177 | { |
178 | Trace( "ERROR:%lu:CloseHandle() call failed\n" , GetLastError() ); |
179 | } |
180 | Fail( "CreateProcess call failed with error code %d\n" , |
181 | dwError ); |
182 | } |
183 | |
184 | /* open another handle to the child process */ |
185 | hChildProcess = OpenProcess( PROCESS_ALL_ACCESS, /* access */ |
186 | FALSE, /* inheritable */ |
187 | pi.dwProcessId /* process id */ |
188 | ); |
189 | if( hChildProcess == NULL ) |
190 | { |
191 | dwError = GetLastError(); |
192 | if( ReleaseMutex( hMutex ) == 0 ) |
193 | { |
194 | Trace( "ERROR:%lu:ReleaseMutex() call failed\n" , GetLastError() ); |
195 | } |
196 | Trace( "ERROR:%lu:OpenProcess call failed\n" , dwError ); |
197 | goto cleanup3; |
198 | } |
199 | |
200 | /* duplicate the child process handle */ |
201 | if( ! DuplicateHandle( GetCurrentProcess(), |
202 | hChildProcess, |
203 | GetCurrentProcess(), |
204 | &hDupChildProcess, |
205 | GENERIC_READ|GENERIC_WRITE, |
206 | FALSE, |
207 | DUPLICATE_SAME_ACCESS) ) |
208 | { |
209 | Trace( "ERROR:%lu:DuplicateHandle() call failed\n" , GetLastError() ); |
210 | if( ReleaseMutex( hMutex ) == 0 ) |
211 | { |
212 | Trace( "ERROR:%lu:ReleaseMutex() call failed\n" , GetLastError() ); |
213 | } |
214 | goto cleanup2; |
215 | } |
216 | |
217 | /* release the mutex so the child can proceed */ |
218 | if( ReleaseMutex( hMutex ) == 0 ) |
219 | { |
220 | Trace( "ERROR:%lu:ReleaseMutex() call failed\n" , GetLastError() ); |
221 | goto cleanup1; |
222 | } |
223 | |
224 | /* wait for the child process to complete, using the new handle */ |
225 | dwRet = WaitForSingleObject( hDupChildProcess, 10000 ); |
226 | if( dwRet != WAIT_OBJECT_0 ) |
227 | { |
228 | Trace( "ERROR:WaitForSingleObject call returned %lu, " |
229 | "expected WAIT_OBJECT_0" , |
230 | dwRet ); |
231 | goto cleanup1; |
232 | } |
233 | |
234 | /* remember that we waited until the child was finished */ |
235 | bChildDone = TRUE; |
236 | |
237 | /* check the exit code from the process -- this is a bit of an */ |
238 | /* extra verification that we opened the correct process handle */ |
239 | if( ! GetExitCodeProcess( hDupChildProcess, &dwExitCode ) ) |
240 | { |
241 | Trace( "ERROR:%lu:GetExitCodeProcess call failed\n" , GetLastError() ); |
242 | goto cleanup1; |
243 | } |
244 | |
245 | /* verification */ |
246 | if( (dwExitCode & 0xFF) != (TEST_EXIT_CODE & 0xFF) ) |
247 | { |
248 | Trace( "GetExitCodeProcess returned an incorrect exit code %d, " |
249 | "expected value is %d\n" , |
250 | (dwExitCode & 0xFF), |
251 | (TEST_EXIT_CODE & 0xFF)); |
252 | goto cleanup1; |
253 | } |
254 | |
255 | /* close the duplicate handle */ |
256 | if( ! CloseHandle( hDupChildProcess ) ) |
257 | { |
258 | Trace( "ERROR:%lu:CloseHandle call failed\n" , GetLastError() ); |
259 | goto cleanup2; |
260 | } |
261 | |
262 | /* close the child process handle */ |
263 | if( ! CloseHandle ( hChildProcess ) ) |
264 | { |
265 | Trace( "ERROR:%lu:CloseHandle() call failed\n" , GetLastError() ); |
266 | goto cleanup3; |
267 | } |
268 | |
269 | /* try to call duplicate handle on the closed child process handle */ |
270 | if( DuplicateHandle( GetCurrentProcess(), |
271 | hChildProcess, |
272 | GetCurrentProcess(), |
273 | &hDupChildProcess, |
274 | GENERIC_READ|GENERIC_WRITE, |
275 | FALSE, |
276 | DUPLICATE_SAME_ACCESS) ) |
277 | { |
278 | Trace( "ERROR:%lu:DuplicateHandle call succeeded on " |
279 | "a closed process handle, expected ERROR_INVALID_HANDLE\n" ); |
280 | if( ! CloseHandle( hDupChildProcess ) ) |
281 | { |
282 | Trace( "ERROR:%lu:CloseHandle call failed\n" , GetLastError() ); |
283 | } |
284 | goto cleanup3; |
285 | } |
286 | |
287 | /* verify that the last error was ERROR_INVALID_HANDLE */ |
288 | dwRet = GetLastError(); |
289 | if( dwRet != ERROR_INVALID_HANDLE ) |
290 | { |
291 | Trace( "ERROR:DuplicateHandle returned %lu, " |
292 | "expected ERROR_INVALID_HANDLE\n" , |
293 | dwRet ); |
294 | goto cleanup3; |
295 | } |
296 | |
297 | |
298 | /* success if we get here */ |
299 | ret = PASS; |
300 | |
301 | /* skip the cleanup stuff that's already done */ |
302 | goto cleanup3; |
303 | |
304 | |
305 | cleanup1: |
306 | /* close our duplicate handle */ |
307 | if( ! CloseHandle( hDupChildProcess ) ) |
308 | { |
309 | Trace( "ERROR:%lu:CloseHandle call failed\n" , GetLastError() ); |
310 | ret = FAIL; |
311 | } |
312 | |
313 | cleanup2: |
314 | /* wait on the child process to complete if necessary */ |
315 | if( ! bChildDone ) |
316 | { |
317 | dwRet = WaitForSingleObject( hChildProcess, 10000 ); |
318 | if( dwRet != WAIT_OBJECT_0 ) |
319 | { |
320 | Trace( "ERROR:WaitForSingleObject call returned %lu, " |
321 | "expected WAIT_OBJECT_0" , |
322 | dwRet ); |
323 | ret = FAIL; |
324 | } |
325 | } |
326 | |
327 | /* close our child process handle */ |
328 | if( CloseHandle ( hChildProcess ) == 0 ) |
329 | { |
330 | Trace( "ERROR:%lu:CloseHandle() call failed\n" , GetLastError() ); |
331 | ret = FAIL; |
332 | } |
333 | |
334 | cleanup3: |
335 | /* close all our other handles */ |
336 | if( CloseHandle ( pi.hProcess ) == 0 ) |
337 | { |
338 | Trace( "ERROR:%lu:CloseHandle() call failed\n" , GetLastError() ); |
339 | ret = FAIL; |
340 | } |
341 | if( CloseHandle ( pi.hThread ) == 0 ) |
342 | { |
343 | Trace( "ERROR:%lu:CloseHandle() call failed\n" , GetLastError() ); |
344 | ret = FAIL; |
345 | } |
346 | if( CloseHandle( hMutex ) == 0 ) |
347 | { |
348 | Trace( "ERROR:%lu:CloseHandle() call failed\n" , GetLastError() ); |
349 | ret = FAIL; |
350 | } |
351 | |
352 | if( ret == FAIL ) |
353 | { |
354 | Fail( "test failed\n" ); |
355 | } |
356 | |
357 | |
358 | |
359 | /* terminate the PAL */ |
360 | PAL_Terminate(); |
361 | |
362 | /* return success */ |
363 | return PASS; |
364 | } |
365 | |