Skip to content

Commit cf2f735

Browse files
committed
mingw: spawned processes need to inherit only standard handles
By default, CreateProcess() does not inherit any open file handles, unless the bInheritHandles parameter is set to TRUE. Which we do need to set because we need to pass in stdin/stdout/stderr to talk to the child processes. Sadly, this means that all file handles (unless marked via O_NOINHERIT) are inherited. This lead to problems in GVFS Git, where a long-running read-object hook is used to hydrate missing objects, and depending on the circumstances, might only be called *after* Git opened a file handle. Ideally, we would not open files without O_NOINHERIT unless *really* necessary (i.e. when we want to pass the opened file handle as standard handle into a child process), but apparently it is all-too-easy to introduce incorrect open() calls: this happened, and prevented updating a file after the read-object hook was started because the hook still held a handle on said file. Happily, there is a solution: as described in the "Old New Thing" https://blogs.msdn.microsoft.com/oldnewthing/20111216-00/?p=8873 there is a way, starting with Windows Vista, that lets us define precisely which handles should be inherited by the child process. And since we bumped the minimum Windows version for use with Git for Windows to Vista with v2.10.1 (i.e. a *long* time ago), we can use this method. So let's do exactly that. Signed-off-by: Johannes Schindelin <[email protected]>
1 parent 31a5d68 commit cf2f735

File tree

2 files changed

+43
-9
lines changed

2 files changed

+43
-9
lines changed

compat/mingw.c

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1594,8 +1594,12 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
15941594
int fhin, int fhout, int fherr)
15951595
{
15961596
static int atexit_handler_initialized;
1597-
STARTUPINFOW si;
1597+
STARTUPINFOEXW si;
15981598
PROCESS_INFORMATION pi;
1599+
LPPROC_THREAD_ATTRIBUTE_LIST attr_list = NULL;
1600+
HANDLE stdhandles[3];
1601+
DWORD stdhandles_count = 0;
1602+
SIZE_T size;
15991603
struct strbuf args;
16001604
wchar_t wcmd[MAX_PATH], wdir[MAX_PATH], *wargs, *wenvblk = NULL;
16011605
unsigned flags = CREATE_UNICODE_ENVIRONMENT;
@@ -1643,11 +1647,19 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
16431647
CloseHandle(cons);
16441648
}
16451649
memset(&si, 0, sizeof(si));
1646-
si.cb = sizeof(si);
1647-
si.dwFlags = STARTF_USESTDHANDLES;
1648-
si.hStdInput = winansi_get_osfhandle(fhin);
1649-
si.hStdOutput = winansi_get_osfhandle(fhout);
1650-
si.hStdError = winansi_get_osfhandle(fherr);
1650+
si.StartupInfo.cb = sizeof(si);
1651+
si.StartupInfo.hStdInput = winansi_get_osfhandle(fhin);
1652+
si.StartupInfo.hStdOutput = winansi_get_osfhandle(fhout);
1653+
si.StartupInfo.hStdError = winansi_get_osfhandle(fherr);
1654+
1655+
if (si.StartupInfo.hStdInput != INVALID_HANDLE_VALUE)
1656+
stdhandles[stdhandles_count++] = si.StartupInfo.hStdInput;
1657+
if (si.StartupInfo.hStdOutput != INVALID_HANDLE_VALUE)
1658+
stdhandles[stdhandles_count++] = si.StartupInfo.hStdOutput;
1659+
if (si.StartupInfo.hStdError != INVALID_HANDLE_VALUE)
1660+
stdhandles[stdhandles_count++] = si.StartupInfo.hStdError;
1661+
if (stdhandles_count)
1662+
si.StartupInfo.dwFlags |= STARTF_USESTDHANDLES;
16511663

16521664
/* executables and the current directory don't support long paths */
16531665
if (*argv && !strcmp(cmd, *argv))
@@ -1706,8 +1718,30 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
17061718
wenvblk = make_environment_block(deltaenv);
17071719

17081720
memset(&pi, 0, sizeof(pi));
1709-
ret = CreateProcessW(*wcmd ? wcmd : NULL, wargs, NULL, NULL, TRUE,
1710-
flags, wenvblk, dir ? wdir : NULL, &si, &pi);
1721+
if (stdhandles_count &&
1722+
(InitializeProcThreadAttributeList(NULL, 1, 0, &size) ||
1723+
GetLastError() == ERROR_INSUFFICIENT_BUFFER) &&
1724+
(attr_list = (LPPROC_THREAD_ATTRIBUTE_LIST)
1725+
(HeapAlloc(GetProcessHeap(), 0, size))) &&
1726+
InitializeProcThreadAttributeList(attr_list, 1, 0, &size) &&
1727+
UpdateProcThreadAttribute(attr_list, 0,
1728+
PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
1729+
stdhandles,
1730+
stdhandles_count * sizeof(HANDLE),
1731+
NULL, NULL)) {
1732+
si.lpAttributeList = attr_list;
1733+
flags |= EXTENDED_STARTUPINFO_PRESENT;
1734+
}
1735+
1736+
ret = CreateProcessW(*wcmd ? wcmd : NULL, wargs, NULL, NULL,
1737+
stdhandles_count ? TRUE : FALSE,
1738+
flags, wenvblk, dir ? wdir : NULL,
1739+
&si.StartupInfo, &pi);
1740+
1741+
if (si.lpAttributeList)
1742+
DeleteProcThreadAttributeList(si.lpAttributeList);
1743+
if (attr_list)
1744+
HeapFree(GetProcessHeap(), 0, attr_list);
17111745

17121746
free(wenvblk);
17131747
free(wargs);

t/t0061-run-command.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ cat >hello-script <<-EOF
1313
EOF
1414
>empty
1515

16-
test_expect_failure MINGW 'subprocess inherits only std handles' '
16+
test_expect_success MINGW 'subprocess inherits only std handles' '
1717
test-run-command inherited-handle
1818
'
1919

0 commit comments

Comments
 (0)