On failure returns NULL with *stage naming the failing step and *gle the * GetLastError value captured at that step (0 when errno is the signal). */
| 232 | /* On failure returns NULL with *stage naming the failing step and *gle the |
| 233 | * GetLastError value captured at that step (0 when errno is the signal). */ |
| 234 | static FILE *cbm_popen_isolated(const char *cmd, const char **stage, DWORD *gle) { |
| 235 | *stage = ""; |
| 236 | *gle = 0; |
| 237 | InitOnceExecuteOnce(&g_popen_once, cbm_popen_init, NULL, NULL); |
| 238 | |
| 239 | SECURITY_ATTRIBUTES sa; |
| 240 | sa.nLength = sizeof(sa); |
| 241 | sa.lpSecurityDescriptor = NULL; |
| 242 | sa.bInheritHandle = TRUE; |
| 243 | |
| 244 | HANDLE rd = NULL, wr = NULL; |
| 245 | if (!CreatePipe(&rd, &wr, &sa, 0)) { |
| 246 | *stage = "pipe"; |
| 247 | *gle = GetLastError(); |
| 248 | return NULL; |
| 249 | } |
| 250 | /* The parent read-end must never cross into the child. */ |
| 251 | SetHandleInformation(rd, HANDLE_FLAG_INHERIT, 0); |
| 252 | |
| 253 | /* NUL for the child's stdin/stderr so it never touches our real stdin |
| 254 | * pipe. If NUL cannot be opened, fail: STARTF_USESTDHANDLES slots must |
| 255 | * never carry INVALID_HANDLE_VALUE. */ |
| 256 | HANDLE nul = CreateFileW(L"NUL", GENERIC_READ | GENERIC_WRITE, |
| 257 | FILE_SHARE_READ | FILE_SHARE_WRITE, &sa, OPEN_EXISTING, 0, NULL); |
| 258 | if (nul == INVALID_HANDLE_VALUE) { |
| 259 | *stage = "nul"; |
| 260 | *gle = GetLastError(); |
| 261 | CloseHandle(rd); |
| 262 | CloseHandle(wr); |
| 263 | return NULL; |
| 264 | } |
| 265 | |
| 266 | HANDLE inherit[2]; |
| 267 | inherit[0] = wr; |
| 268 | inherit[1] = nul; |
| 269 | |
| 270 | SIZE_T attr_sz = 0; |
| 271 | InitializeProcThreadAttributeList(NULL, 1, 0, &attr_sz); |
| 272 | LPPROC_THREAD_ATTRIBUTE_LIST attr = (LPPROC_THREAD_ATTRIBUTE_LIST)malloc(attr_sz); |
| 273 | BOOL attr_init = attr && InitializeProcThreadAttributeList(attr, 1, 0, &attr_sz); |
| 274 | BOOL prepared = |
| 275 | attr_init && UpdateProcThreadAttribute(attr, 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST, inherit, |
| 276 | sizeof(inherit), NULL, NULL); |
| 277 | DWORD attr_gle = prepared ? 0 : GetLastError(); |
| 278 | |
| 279 | STARTUPINFOEXW si; |
| 280 | ZeroMemory(&si, sizeof(si)); |
| 281 | si.StartupInfo.cb = sizeof(si); |
| 282 | si.StartupInfo.dwFlags = STARTF_USESTDHANDLES; |
| 283 | si.StartupInfo.hStdInput = nul; |
| 284 | si.StartupInfo.hStdOutput = wr; |
| 285 | si.StartupInfo.hStdError = nul; |
| 286 | si.lpAttributeList = attr; |
| 287 | |
| 288 | /* Run through cmd.exe /c so command quoting and `2>NUL` behave as under |
| 289 | * _popen. The command line is heap-composed (no fixed-size truncation) |
| 290 | * and widened via UTF-8 so non-ASCII repo paths survive intact. */ |
| 291 | wchar_t *app = cbm_resolve_comspec(); |
no test coverage detected