Skip to content

Commit af2e534

Browse files
committed
Merge branch 'inherit-only-stdhandles'
When spawning child processes, we do want them to inherit the standard handles so that we can talk to them. We do *not* want them to inherit any other handle, as that would hold a lock to the respective files (preventing them from being renamed, modified or deleted), and the child process would not know how to access that handle anyway. Happily, there is an API to make that happen. It is supported in Windows Vista and later, which is exactly what we promise to support in Git for Windows for the time being. This also means that we lift, at long last, the target Windows version from Windows XP to Windows Vista. Signed-off-by: Johannes Schindelin <[email protected]>
2 parents 1b4ff21 + f9298c1 commit af2e534

File tree

7 files changed

+187
-20
lines changed

7 files changed

+187
-20
lines changed

compat/mingw.c

Lines changed: 110 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,6 @@ int mingw_core_config(const char *var, const char *value, void *cb)
274274
}
275275

276276
static DWORD symlink_file_flags = 0, symlink_directory_flags = 1;
277-
DECLARE_PROC_ADDR(kernel32.dll, BOOLEAN, CreateSymbolicLinkW, LPCWSTR, LPCWSTR, DWORD);
278277

279278
enum phantom_symlink_result {
280279
PHANTOM_SYMLINK_RETRY,
@@ -1604,8 +1603,13 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
16041603
const char *dir, const char *prepend_cmd,
16051604
int fhin, int fhout, int fherr)
16061605
{
1607-
STARTUPINFOW si;
1606+
static int restrict_handle_inheritance = 1;
1607+
STARTUPINFOEXW si;
16081608
PROCESS_INFORMATION pi;
1609+
LPPROC_THREAD_ATTRIBUTE_LIST attr_list = NULL;
1610+
HANDLE stdhandles[3];
1611+
DWORD stdhandles_count = 0;
1612+
SIZE_T size;
16091613
struct strbuf args;
16101614
wchar_t wcmd[MAX_PATH], wdir[MAX_PATH], *wargs, *wenvblk = NULL;
16111615
unsigned flags = CREATE_UNICODE_ENVIRONMENT;
@@ -1642,11 +1646,23 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
16421646
CloseHandle(cons);
16431647
}
16441648
memset(&si, 0, sizeof(si));
1645-
si.cb = sizeof(si);
1646-
si.dwFlags = STARTF_USESTDHANDLES;
1647-
si.hStdInput = winansi_get_osfhandle(fhin);
1648-
si.hStdOutput = winansi_get_osfhandle(fhout);
1649-
si.hStdError = winansi_get_osfhandle(fherr);
1649+
si.StartupInfo.cb = sizeof(si);
1650+
si.StartupInfo.hStdInput = winansi_get_osfhandle(fhin);
1651+
si.StartupInfo.hStdOutput = winansi_get_osfhandle(fhout);
1652+
si.StartupInfo.hStdError = winansi_get_osfhandle(fherr);
1653+
1654+
/* The list of handles cannot contain duplicates */
1655+
if (si.StartupInfo.hStdInput != INVALID_HANDLE_VALUE)
1656+
stdhandles[stdhandles_count++] = si.StartupInfo.hStdInput;
1657+
if (si.StartupInfo.hStdOutput != INVALID_HANDLE_VALUE &&
1658+
si.StartupInfo.hStdOutput != si.StartupInfo.hStdInput)
1659+
stdhandles[stdhandles_count++] = si.StartupInfo.hStdOutput;
1660+
if (si.StartupInfo.hStdError != INVALID_HANDLE_VALUE &&
1661+
si.StartupInfo.hStdError != si.StartupInfo.hStdInput &&
1662+
si.StartupInfo.hStdError != si.StartupInfo.hStdOutput)
1663+
stdhandles[stdhandles_count++] = si.StartupInfo.hStdError;
1664+
if (stdhandles_count)
1665+
si.StartupInfo.dwFlags |= STARTF_USESTDHANDLES;
16501666

16511667
/* executables and the current directory don't support long paths */
16521668
if (*argv && !strcmp(cmd, *argv))
@@ -1705,16 +1721,97 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
17051721
wenvblk = make_environment_block(deltaenv);
17061722

17071723
memset(&pi, 0, sizeof(pi));
1708-
ret = CreateProcessW(*wcmd ? wcmd : NULL, wargs, NULL, NULL, TRUE,
1709-
flags, wenvblk, dir ? wdir : NULL, &si, &pi);
1724+
if (restrict_handle_inheritance && stdhandles_count &&
1725+
(InitializeProcThreadAttributeList(NULL, 1, 0, &size) ||
1726+
GetLastError() == ERROR_INSUFFICIENT_BUFFER) &&
1727+
(attr_list = (LPPROC_THREAD_ATTRIBUTE_LIST)
1728+
(HeapAlloc(GetProcessHeap(), 0, size))) &&
1729+
InitializeProcThreadAttributeList(attr_list, 1, 0, &size) &&
1730+
UpdateProcThreadAttribute(attr_list, 0,
1731+
PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
1732+
stdhandles,
1733+
stdhandles_count * sizeof(HANDLE),
1734+
NULL, NULL)) {
1735+
si.lpAttributeList = attr_list;
1736+
flags |= EXTENDED_STARTUPINFO_PRESENT;
1737+
}
1738+
1739+
ret = CreateProcessW(*wcmd ? wcmd : NULL, wargs, NULL, NULL,
1740+
stdhandles_count ? TRUE : FALSE,
1741+
flags, wenvblk, dir ? wdir : NULL,
1742+
&si.StartupInfo, &pi);
1743+
1744+
/*
1745+
* On Windows 2008 R2, it seems that specifying certain types of handles
1746+
* (such as FILE_TYPE_CHAR or FILE_TYPE_PIPE) will always produce an
1747+
* error. Rather than playing finicky and fragile games, let's just try
1748+
* to detect this situation and simply try again without restricting any
1749+
* handle inheritance. This is still better than failing to create
1750+
* processes.
1751+
*/
1752+
if (!ret && restrict_handle_inheritance && stdhandles_count) {
1753+
DWORD err = GetLastError();
1754+
struct strbuf buf = STRBUF_INIT;
1755+
1756+
if (err != ERROR_NO_SYSTEM_RESOURCES &&
1757+
/*
1758+
* On Windows 7 and earlier, handles on pipes and character
1759+
* devices are inherited automatically, and cannot be
1760+
* specified in the thread handle list. Rather than trying
1761+
* to catch each and every corner case (and running the
1762+
* chance of *still* forgetting a few), let's just fall
1763+
* back to creating the process without trying to limit the
1764+
* handle inheritance.
1765+
*/
1766+
!(err == ERROR_INVALID_PARAMETER &&
1767+
GetVersion() >> 16 < 9200) &&
1768+
!getenv("SUPPRESS_HANDLE_INHERITANCE_WARNING")) {
1769+
DWORD fl = 0;
1770+
int i;
1771+
1772+
setenv("SUPPRESS_HANDLE_INHERITANCE_WARNING", "1", 1);
1773+
1774+
for (i = 0; i < stdhandles_count; i++) {
1775+
HANDLE h = stdhandles[i];
1776+
strbuf_addf(&buf, "handle #%d: %p (type %lx, "
1777+
"handle info (%d) %lx\n", i, h,
1778+
GetFileType(h),
1779+
GetHandleInformation(h, &fl),
1780+
fl);
1781+
}
1782+
strbuf_addstr(&buf, "\nThis is a bug; please report it "
1783+
"at\nhttps://github.com/git-for-windows/"
1784+
"git/issues/new\n\n"
1785+
"To suppress this warning, please set "
1786+
"the environment variable\n\n"
1787+
"\tSUPPRESS_HANDLE_INHERITANCE_WARNING=1"
1788+
"\n");
1789+
}
1790+
restrict_handle_inheritance = 0;
1791+
flags &= ~EXTENDED_STARTUPINFO_PRESENT;
1792+
ret = CreateProcessW(*wcmd ? wcmd : NULL, wargs, NULL, NULL,
1793+
TRUE, flags, wenvblk, dir ? wdir : NULL,
1794+
&si.StartupInfo, &pi);
1795+
if (ret && buf.len) {
1796+
errno = err_win_to_posix(GetLastError());
1797+
warning("failed to restrict file handles (%ld)\n\n%s",
1798+
err, buf.buf);
1799+
}
1800+
strbuf_release(&buf);
1801+
} else if (!ret)
1802+
errno = err_win_to_posix(GetLastError());
1803+
1804+
if (si.lpAttributeList)
1805+
DeleteProcThreadAttributeList(si.lpAttributeList);
1806+
if (attr_list)
1807+
HeapFree(GetProcessHeap(), 0, attr_list);
17101808

17111809
free(wenvblk);
17121810
free(wargs);
17131811

1714-
if (!ret) {
1715-
errno = ENOENT;
1812+
if (!ret)
17161813
return -1;
1717-
}
1814+
17181815
CloseHandle(pi.hThread);
17191816

17201817
/*
@@ -2685,7 +2782,7 @@ int symlink(const char *target, const char *link)
26852782
int len;
26862783

26872784
/* fail if symlinks are disabled or API is not supported (WinXP) */
2688-
if (!has_symlinks || !INIT_PROC_ADDR(CreateSymbolicLinkW)) {
2785+
if (!has_symlinks) {
26892786
errno = ENOSYS;
26902787
return -1;
26912788
}

compat/poll/poll.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,21 @@
2121
#ifndef _GL_POLL_H
2222
#define _GL_POLL_H
2323

24+
#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x600
25+
/* Vista has its own, socket-only poll() */
26+
#undef POLLIN
27+
#undef POLLPRI
28+
#undef POLLOUT
29+
#undef POLLERR
30+
#undef POLLHUP
31+
#undef POLLNVAL
32+
#undef POLLRDNORM
33+
#undef POLLRDBAND
34+
#undef POLLWRNORM
35+
#undef POLLWRBAND
36+
#define pollfd compat_pollfd
37+
#endif
38+
2439
/* fake a poll(2) environment */
2540
#define POLLIN 0x0001 /* any readable data available */
2641
#define POLLPRI 0x0002 /* OOB/Urgent readable data */

compat/winansi.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -660,10 +660,20 @@ void winansi_init(void)
660660
*/
661661
HANDLE winansi_get_osfhandle(int fd)
662662
{
663+
HANDLE ret;
664+
663665
if (fd == 1 && (fd_is_interactive[1] & FD_SWAPPED))
664666
return hconsole1;
665667
if (fd == 2 && (fd_is_interactive[2] & FD_SWAPPED))
666668
return hconsole2;
667669

668-
return (HANDLE)_get_osfhandle(fd);
670+
ret = (HANDLE)_get_osfhandle(fd);
671+
672+
/*
673+
* There are obviously circumstances under which _get_osfhandle()
674+
* returns (HANDLE)-2. This is not documented anywhere, but that is so
675+
* clearly an invalid handle value that we can just work around this
676+
* and return the correct value for invalid handles.
677+
*/
678+
return ret == (HANDLE)-2 ? INVALID_HANDLE_VALUE : ret;
669679
}

config.mak.uname

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -398,8 +398,6 @@ ifeq ($(uname_S),Windows)
398398
NO_GETTEXT = YesPlease
399399
NO_PYTHON = YesPlease
400400
ETAGS_TARGET = ETAGS
401-
NO_INET_PTON = YesPlease
402-
NO_INET_NTOP = YesPlease
403401
NO_POSIX_GOODIES = UnfortunatelyYes
404402
NATIVE_CRLF = YesPlease
405403
DEFAULT_HELP_FORMAT = html
@@ -567,8 +565,6 @@ ifneq (,$(findstring MINGW,$(uname_S)))
567565
NO_REGEX = YesPlease
568566
NO_PYTHON = YesPlease
569567
ETAGS_TARGET = ETAGS
570-
NO_INET_PTON = YesPlease
571-
NO_INET_NTOP = YesPlease
572568
NO_POSIX_GOODIES = UnfortunatelyYes
573569
DEFAULT_HELP_FORMAT = html
574570
COMPAT_CFLAGS += -DNOGDI -Icompat -Icompat/win32

git-compat-util.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,8 +155,8 @@
155155
#define _SGI_SOURCE 1
156156

157157
#if defined(WIN32) && !defined(__CYGWIN__) /* Both MinGW and MSVC */
158-
# if defined (_MSC_VER) && !defined(_WIN32_WINNT)
159-
# define _WIN32_WINNT 0x0502
158+
# if !defined(_WIN32_WINNT)
159+
# define _WIN32_WINNT 0x0600
160160
# endif
161161
#define WIN32_LEAN_AND_MEAN /* stops windows.h including winsock.h */
162162
#include <winsock2.h>

t/helper/test-run-command.c

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,13 +188,58 @@ static int testsuite(int argc, const char **argv)
188188
return !!ret;
189189
}
190190

191+
static int inherit_handle(const char *argv0)
192+
{
193+
struct child_process cp = CHILD_PROCESS_INIT;
194+
char path[PATH_MAX];
195+
int tmp;
196+
197+
/* First, open an inheritable handle */
198+
sprintf(path, "out-XXXXXX");
199+
tmp = xmkstemp(path);
200+
201+
argv_array_pushl(&cp.args, argv0, "inherited-handle-child", NULL);
202+
cp.in = -1;
203+
cp.no_stdout = cp.no_stderr = 1;
204+
if (start_command(&cp) < 0)
205+
die("Could not start child process");
206+
207+
/* Then close it, and try to delete it. */
208+
close(tmp);
209+
if (unlink(path))
210+
die("Could not delete '%s'", path);
211+
212+
if (close(cp.in) < 0 || finish_command(&cp) < 0)
213+
die("Child did not finish");
214+
215+
return 0;
216+
}
217+
218+
static int inherit_handle_child(void)
219+
{
220+
struct strbuf buf = STRBUF_INIT;
221+
222+
if (strbuf_read(&buf, 0, 0) < 0)
223+
die("Could not read stdin");
224+
printf("Received %s\n", buf.buf);
225+
strbuf_release(&buf);
226+
227+
return 0;
228+
}
229+
191230
int cmd_main(int argc, const char **argv)
192231
{
193232
struct child_process proc = CHILD_PROCESS_INIT;
194233
int jobs;
195234

196235
if (argc > 1 && !strcmp(argv[1], "testsuite"))
197236
exit(testsuite(argc - 1, argv + 1));
237+
238+
if (!strcmp(argv[1], "inherited-handle"))
239+
exit(inherit_handle(argv[0]));
240+
if (!strcmp(argv[1], "inherited-handle-child"))
241+
exit(inherit_handle_child());
242+
198243
if (argc < 3)
199244
return 1;
200245
while (!strcmp(argv[1], "env")) {

t/t0061-run-command.sh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ cat >hello-script <<-EOF
1313
EOF
1414
>empty
1515

16+
test_expect_success MINGW 'subprocess inherits only std handles' '
17+
test-run-command inherited-handle
18+
'
19+
1620
test_expect_success 'start_command reports ENOENT' '
1721
test-run-command start-command-ENOENT ./does-not-exist
1822
'

0 commit comments

Comments
 (0)