Skip to content

Commit 2d76081

Browse files
committed
syscall: restrict inherited handles on Windows
Windows does not have CLOEXEC, but rather handles are marked explicitly for being inherited by new processes. This can cause problems when different Windows functions create new processes from different threads. syscall.StartProcess has traditionally used a mutex to prevent races with itself, but this doesn't handle races with other win32 functions. Fortunately there's a solution: PROC_THREAD_ATTRIBUTE_HANDLE_LIST allows us to pass the entire list of handles that we want to be inherited. This lets us get rid of the mutex and also makes process creation safe across the Go runtime, no matter the context. Updates #44011. Change-Id: Ia3424cd2ec64868849cbd6cbb5b0d765224bf4ab Reviewed-on: https://go-review.googlesource.com/c/go/+/288297 Trust: Jason A. Donenfeld <[email protected]> Trust: Alex Brainman <[email protected]> Reviewed-by: Alex Brainman <[email protected]>
1 parent ba9168b commit 2d76081

File tree

1 file changed

+15
-10
lines changed

1 file changed

+15
-10
lines changed

src/syscall/exec_windows.go

+15-10
Original file line numberDiff line numberDiff line change
@@ -310,12 +310,6 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle
310310
}
311311
}
312312

313-
// Acquire the fork lock so that no other threads
314-
// create new fds that are not yet close-on-exec
315-
// before we fork.
316-
ForkLock.Lock()
317-
defer ForkLock.Unlock()
318-
319313
p, _ := GetCurrentProcess()
320314
fd := make([]Handle, len(attr.Files))
321315
for i := range attr.Files {
@@ -327,7 +321,12 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle
327321
defer CloseHandle(Handle(fd[i]))
328322
}
329323
}
330-
si := new(StartupInfo)
324+
si := new(_STARTUPINFOEXW)
325+
si.ProcThreadAttributeList, err = newProcThreadAttributeList(1)
326+
if err != nil {
327+
return 0, 0, err
328+
}
329+
defer deleteProcThreadAttributeList(si.ProcThreadAttributeList)
331330
si.Cb = uint32(unsafe.Sizeof(*si))
332331
si.Flags = STARTF_USESTDHANDLES
333332
if sys.HideWindow {
@@ -338,13 +337,19 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle
338337
si.StdOutput = fd[1]
339338
si.StdErr = fd[2]
340339

340+
// Do not accidentally inherit more than these handles.
341+
err = updateProcThreadAttribute(si.ProcThreadAttributeList, 0, _PROC_THREAD_ATTRIBUTE_HANDLE_LIST, uintptr(unsafe.Pointer(&fd[0])), uintptr(len(fd))*unsafe.Sizeof(fd[0]), 0, nil)
342+
if err != nil {
343+
return 0, 0, err
344+
}
345+
341346
pi := new(ProcessInformation)
342347

343-
flags := sys.CreationFlags | CREATE_UNICODE_ENVIRONMENT
348+
flags := sys.CreationFlags | CREATE_UNICODE_ENVIRONMENT | _EXTENDED_STARTUPINFO_PRESENT
344349
if sys.Token != 0 {
345-
err = CreateProcessAsUser(sys.Token, argv0p, argvp, sys.ProcessAttributes, sys.ThreadAttributes, !sys.NoInheritHandles, flags, createEnvBlock(attr.Env), dirp, si, pi)
350+
err = CreateProcessAsUser(sys.Token, argv0p, argvp, sys.ProcessAttributes, sys.ThreadAttributes, !sys.NoInheritHandles, flags, createEnvBlock(attr.Env), dirp, &si.StartupInfo, pi)
346351
} else {
347-
err = CreateProcess(argv0p, argvp, sys.ProcessAttributes, sys.ThreadAttributes, !sys.NoInheritHandles, flags, createEnvBlock(attr.Env), dirp, si, pi)
352+
err = CreateProcess(argv0p, argvp, sys.ProcessAttributes, sys.ThreadAttributes, !sys.NoInheritHandles, flags, createEnvBlock(attr.Env), dirp, &si.StartupInfo, pi)
348353
}
349354
if err != nil {
350355
return 0, 0, err

0 commit comments

Comments
 (0)