6#include <winpr/tchar.h>
7#include <winpr/synch.h>
8#include <winpr/thread.h>
9#include <winpr/environment.h>
10#include <winpr/pipe.h>
11#include <winpr/library.h>
23#include "../../handle/handle.h"
26#define TESTENV_A "HELLO=WORLD"
27#define TESTENV_T _T(TESTENV_A)
31static unsigned long long handle_to_probe_value(HANDLE h)
33 return (
unsigned long long)(UINT_PTR)h;
37static unsigned long long handle_to_probe_value(HANDLE h)
39 return (
unsigned long long)winpr_Handle_getFd(h);
49static int probe_handle_and_report(
const char* valueStr)
54 HANDLE h = (HANDLE)(UINT_PTR)strtoull(valueStr,
nullptr, 10);
56 ok = WriteFile(h,
"PING", 4, &written,
nullptr) && (written == 4);
58 const long fd = strtol(valueStr,
nullptr, 0);
59 if ((fd >= INT32_MIN) || (fd <= INT32_MAX))
60 ok = (write(WINPR_ASSERTING_INT_CAST(
int, fd),
"PING", 4) == 4);
63 printf(ok ?
"OPEN\n" :
"CLOSED\n");
72 MODE_HANDLE_LIST_WITH_PROBE,
73 MODE_HANDLE_LIST_WITHOUT_PROBE
82static int run_inherit_case(
const char* exePath,
const char* label, HANDLE probeHandle,
83 InheritTestMode mode, BOOL expectOpen)
86 HANDLE outRead =
nullptr;
87 HANDLE outWrite =
nullptr;
90 if (!CreatePipe(&outRead, &outWrite, &saAttr, 0))
92 printf(
"[%s] CreatePipe(out) failed\n", label);
96 char cmdline[1024] = WINPR_C_ARRAY_INIT;
97 (void)snprintf(cmdline,
sizeof(cmdline),
"\"%s\" TestThreadCreateProcess --probe-handle %llu",
98 exePath, handle_to_probe_value(probeHandle));
100 const BOOL bInheritHandles = (mode != MODE_NO_INHERIT);
101 const BOOL useExtended =
102 (mode == MODE_HANDLE_LIST_WITH_PROBE) || (mode == MODE_HANDLE_LIST_WITHOUT_PROBE);
105 BOOL created = FALSE;
106 LPPROC_THREAD_ATTRIBUTE_LIST attrList =
nullptr;
111 DWORD createFlags = 0;
112 HANDLE handles[2] = { outWrite, probeHandle };
117 (void)InitializeProcThreadAttributeList(
nullptr, 1, 0, &size);
118 attrList = (LPPROC_THREAD_ATTRIBUTE_LIST)malloc(size);
119 if (!attrList || !InitializeProcThreadAttributeList(attrList, 1, 0, &size))
121 printf(
"[%s] InitializeProcThreadAttributeList failed\n", label);
123 CloseHandle(outRead);
124 CloseHandle(outWrite);
132 const size_t handleCount = (mode == MODE_HANDLE_LIST_WITH_PROBE) ? 2 : 1;
133 if (!UpdateProcThreadAttribute(attrList, 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
134 (
void*)handles, handleCount *
sizeof(HANDLE),
nullptr,
137 printf(
"[%s] UpdateProcThreadAttribute failed\n", label);
138 DeleteProcThreadAttributeList(attrList);
140 CloseHandle(outRead);
141 CloseHandle(outWrite);
145 siEx.StartupInfo.cb =
sizeof(siEx);
146 siEx.StartupInfo.dwFlags = STARTF_USESTDHANDLES;
147 siEx.StartupInfo.hStdOutput = outWrite;
148 siEx.StartupInfo.hStdError = outWrite;
149 siEx.lpAttributeList = attrList;
152 createFlags = EXTENDED_STARTUPINFO_PRESENT;
157 si.dwFlags = STARTF_USESTDHANDLES;
158 si.hStdOutput = outWrite;
159 si.hStdError = outWrite;
162 created = CreateProcessA(
nullptr, cmdline,
nullptr,
nullptr, bInheritHandles, createFlags,
163 nullptr,
nullptr, siPtr, &pi);
167 DeleteProcThreadAttributeList(attrList);
171 CloseHandle(outWrite);
175 printf(
"[%s] CreateProcessA failed, error=%" PRIu32
"\n", label, GetLastError());
176 CloseHandle(outRead);
180 if (WaitForSingleObject(pi.hProcess, 5000) != WAIT_OBJECT_0)
182 printf(
"[%s] child did not exit in time\n", label);
186 char buf[64] = WINPR_C_ARRAY_INIT;
187 DWORD read_bytes = 0;
188 (void)ReadFile(outRead, buf,
sizeof(buf) - 1, &read_bytes,
nullptr);
190 const BOOL open = strstr(buf,
"OPEN") !=
nullptr;
191 result = (open != expectOpen);
192 printf(
"[%s] expected %s, got '%s' -> %s\n", label, expectOpen ?
"OPEN" :
"CLOSED", buf,
193 result ?
"FAIL" :
"OK");
196 CloseHandle(outRead);
197 CloseHandle(pi.hProcess);
198 CloseHandle(pi.hThread);
218static int TestHandleListEarlyCloseIsSafe(
const char* exePath)
221 HANDLE probeRead =
nullptr;
222 HANDLE probeWrite =
nullptr;
223 HANDLE outRead =
nullptr;
224 HANDLE outWrite =
nullptr;
227 if (!CreatePipe(&probeRead, &probeWrite, &saAttr, 0))
229 printf(
"[early-close] CreatePipe(probe) failed\n");
232 if (!CreatePipe(&outRead, &outWrite, &saAttr, 0))
234 printf(
"[early-close] CreatePipe(out) failed\n");
235 CloseHandle(probeRead);
236 CloseHandle(probeWrite);
240 char cmdline[1024] = WINPR_C_ARRAY_INIT;
241 (void)snprintf(cmdline,
sizeof(cmdline),
"\"%s\" TestThreadCreateProcess --probe-handle %llu",
242 exePath, handle_to_probe_value(probeWrite));
245 (void)InitializeProcThreadAttributeList(
nullptr, 1, 0, &size);
246 LPPROC_THREAD_ATTRIBUTE_LIST attrList = (LPPROC_THREAD_ATTRIBUTE_LIST)malloc(size);
247 if (!attrList || !InitializeProcThreadAttributeList(attrList, 1, 0, &size))
249 printf(
"[early-close] InitializeProcThreadAttributeList failed\n");
251 CloseHandle(probeRead);
252 CloseHandle(probeWrite);
253 CloseHandle(outRead);
254 CloseHandle(outWrite);
258 HANDLE handles[2] = { outWrite, probeWrite };
259 if (!UpdateProcThreadAttribute(attrList, 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST, (
void*)handles,
260 sizeof(handles),
nullptr,
nullptr))
262 printf(
"[early-close] UpdateProcThreadAttribute failed\n");
263 DeleteProcThreadAttributeList(attrList);
265 CloseHandle(probeRead);
266 CloseHandle(probeWrite);
267 CloseHandle(outRead);
268 CloseHandle(outWrite);
275 CloseHandle(probeWrite);
278 siEx.StartupInfo.cb =
sizeof(siEx);
279 siEx.StartupInfo.dwFlags = STARTF_USESTDHANDLES;
280 siEx.StartupInfo.hStdOutput = outWrite;
281 siEx.StartupInfo.hStdError = outWrite;
282 siEx.lpAttributeList = attrList;
286 CreateProcessA(
nullptr, cmdline,
nullptr,
nullptr, TRUE, EXTENDED_STARTUPINFO_PRESENT,
288 const DWORD lastError = GetLastError();
290 DeleteProcThreadAttributeList(attrList);
292 CloseHandle(outWrite);
293 CloseHandle(probeRead);
294 CloseHandle(outRead);
298 printf(
"[early-close] expected CreateProcessA to fail (a listed handle was already "
299 "closed), but it succeeded -> FAIL\n");
300 CloseHandle(pi.hProcess);
301 CloseHandle(pi.hThread);
305 result = (lastError != ERROR_INVALID_PARAMETER);
306 printf(
"[early-close] expected CreateProcessA to fail with ERROR_INVALID_PARAMETER, got "
307 "error=%" PRIu32
" -> %s\n",
308 lastError, result ?
"FAIL" :
"OK");
320static int TestHandleInheritance(
void)
322 char exePath[4096] = WINPR_C_ARRAY_INIT;
323 if (GetModuleFileNameA(
nullptr, exePath,
sizeof(exePath)) == 0)
325 printf(
"GetModuleFileNameA failed\n");
330 HANDLE probeRead =
nullptr;
331 HANDLE probeWrite =
nullptr;
332 if (!CreatePipe(&probeRead, &probeWrite, &saAttr, 0))
334 printf(
"CreatePipe(probe) failed\n");
339 rc |= run_inherit_case(exePath,
"bInheritHandles=FALSE", probeWrite, MODE_NO_INHERIT, FALSE);
340 rc |= run_inherit_case(exePath,
"bInheritHandles=TRUE, no list", probeWrite,
341 MODE_INHERIT_NO_LIST, TRUE);
342 rc |= run_inherit_case(exePath,
"handle list CONTAINS probe", probeWrite,
343 MODE_HANDLE_LIST_WITH_PROBE, TRUE);
344 rc |= run_inherit_case(exePath,
"handle list does NOT contain probe", probeWrite,
345 MODE_HANDLE_LIST_WITHOUT_PROBE, FALSE);
347 CloseHandle(probeRead);
348 CloseHandle(probeWrite);
350 rc |= TestHandleListEarlyCloseIsSafe(exePath);
354int TestThreadCreateProcess(
int argc,
char* argv[])
356 if ((argc >= 3) && (strcmp(argv[1],
"--probe-handle") == 0))
357 return probe_handle_and_report(argv[2]);
361 LPCTSTR lpApplicationName =
nullptr;
364 TCHAR lpCommandLine[200] = _T(
"cmd /C set");
366 TCHAR lpCommandLine[200] = _T(
"printenv");
370 LPSECURITY_ATTRIBUTES lpProcessAttributes =
nullptr;
371 LPSECURITY_ATTRIBUTES lpThreadAttributes =
nullptr;
372 BOOL bInheritHandles = 0;
373 DWORD dwCreationFlags = 0;
374 LPVOID lpEnvironment =
nullptr;
375 LPCTSTR lpCurrentDirectory =
nullptr;
378 LPTCH lpszEnvironmentBlock =
nullptr;
379 HANDLE pipe_read =
nullptr;
380 HANDLE pipe_write =
nullptr;
381 char buf[1024] = WINPR_C_ARRAY_INIT;
382 DWORD read_bytes = 0;
389 lpszEnvironmentBlock = GetEnvironmentStrings();
391 lpApplicationName =
nullptr;
393 lpProcessAttributes =
nullptr;
394 lpThreadAttributes =
nullptr;
395 bInheritHandles = FALSE;
398 dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
400 lpEnvironment = lpszEnvironmentBlock;
401 lpCurrentDirectory =
nullptr;
404 status = CreateProcess(lpApplicationName, lpCommandLine, lpProcessAttributes,
405 lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment,
406 lpCurrentDirectory, &StartupInfo, &ProcessInformation);
410 printf(
"CreateProcess failed. error=%" PRIu32
"\n", GetLastError());
414 if (WaitForSingleObject(ProcessInformation.hProcess, 5000) != WAIT_OBJECT_0)
416 printf(
"Failed to wait for first process. error=%" PRIu32
"\n", GetLastError());
421 status = GetExitCodeProcess(ProcessInformation.hProcess, &exitCode);
423 printf(
"GetExitCodeProcess status: %" PRId32
"\n", status);
424 printf(
"Process exited with code: 0x%08" PRIX32
"\n", exitCode);
426 (void)CloseHandle(ProcessInformation.hProcess);
427 (void)CloseHandle(ProcessInformation.hThread);
428 FreeEnvironmentStrings(lpszEnvironmentBlock);
433 saAttr.bInheritHandle = TRUE;
434 saAttr.lpSecurityDescriptor =
nullptr;
436 if (!CreatePipe(&pipe_read, &pipe_write, &saAttr, 0))
438 printf(
"Pipe creation failed. error=%" PRIu32
"\n", GetLastError());
442 bInheritHandles = TRUE;
446 StartupInfo.hStdOutput = pipe_write;
447 StartupInfo.hStdError = pipe_write;
448 StartupInfo.dwFlags = STARTF_USESTDHANDLES;
452 if (!(lpEnvironment = calloc(1,
sizeof(TESTENV_T) +
sizeof(TCHAR))))
454 printf(
"Failed to allocate environment buffer. error=%" PRIu32
"\n", GetLastError());
457 memcpy(lpEnvironment, (
void*)TESTENV_T,
sizeof(TESTENV_T));
459 status = CreateProcess(lpApplicationName, lpCommandLine, lpProcessAttributes,
460 lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment,
461 lpCurrentDirectory, &StartupInfo, &ProcessInformation);
467 (void)CloseHandle(pipe_read);
468 (void)CloseHandle(pipe_write);
469 printf(
"CreateProcess failed. error=%" PRIu32
"\n", GetLastError());
473 if (WaitForSingleObject(ProcessInformation.hProcess, 5000) != WAIT_OBJECT_0)
475 printf(
"Failed to wait for second process. error=%" PRIu32
"\n", GetLastError());
479 ZeroMemory(buf,
sizeof(buf));
480 ReadFile(pipe_read, buf,
sizeof(buf) - 1, &read_bytes,
nullptr);
481 if (!strstr((
const char*)buf, TESTENV_A))
483 printf(
"No or unexpected data read from pipe\n");
487 (void)CloseHandle(pipe_read);
488 (void)CloseHandle(pipe_write);
491 status = GetExitCodeProcess(ProcessInformation.hProcess, &exitCode);
493 printf(
"GetExitCodeProcess status: %" PRId32
"\n", status);
494 printf(
"Process exited with code: 0x%08" PRIX32
"\n", exitCode);
496 (void)CloseHandle(ProcessInformation.hProcess);
497 (void)CloseHandle(ProcessInformation.hThread);
500 ret = TestHandleInheritance();