Skip to content

Commit f294f83

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 8915bcf + 6b36678 commit f294f83

File tree

7 files changed

+187
-21
lines changed

7 files changed

+187
-21
lines changed

compat/mingw.c

Lines changed: 110 additions & 14 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,
@@ -1594,9 +1593,13 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
15941593
const char *dir, const char *prepend_cmd,
15951594
int fhin, int fhout, int fherr)
15961595
{
1597-
static int atexit_handler_initialized;
1598-
STARTUPINFOW si;
1596+
static int atexit_handler_initialized, restrict_handle_inheritance = 1;
1597+
STARTUPINFOEXW si;
15991598
PROCESS_INFORMATION pi;
1599+
LPPROC_THREAD_ATTRIBUTE_LIST attr_list = NULL;
1600+
HANDLE stdhandles[3];
1601+
DWORD stdhandles_count = 0;
1602+
SIZE_T size;
16001603
struct strbuf args;
16011604
wchar_t wcmd[MAX_PATH], wdir[MAX_PATH], *wargs, *wenvblk = NULL;
16021605
unsigned flags = CREATE_UNICODE_ENVIRONMENT;
@@ -1644,11 +1647,23 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
16441647
CloseHandle(cons);
16451648
}
16461649
memset(&si, 0, sizeof(si));
1647-
si.cb = sizeof(si);
1648-
si.dwFlags = STARTF_USESTDHANDLES;
1649-
si.hStdInput = winansi_get_osfhandle(fhin);
1650-
si.hStdOutput = winansi_get_osfhandle(fhout);
1651-
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+
/* The list of handles cannot contain duplicates */
1656+
if (si.StartupInfo.hStdInput != INVALID_HANDLE_VALUE)
1657+
stdhandles[stdhandles_count++] = si.StartupInfo.hStdInput;
1658+
if (si.StartupInfo.hStdOutput != INVALID_HANDLE_VALUE &&
1659+
si.StartupInfo.hStdOutput != si.StartupInfo.hStdInput)
1660+
stdhandles[stdhandles_count++] = si.StartupInfo.hStdOutput;
1661+
if (si.StartupInfo.hStdError != INVALID_HANDLE_VALUE &&
1662+
si.StartupInfo.hStdError != si.StartupInfo.hStdInput &&
1663+
si.StartupInfo.hStdError != si.StartupInfo.hStdOutput)
1664+
stdhandles[stdhandles_count++] = si.StartupInfo.hStdError;
1665+
if (stdhandles_count)
1666+
si.StartupInfo.dwFlags |= STARTF_USESTDHANDLES;
16521667

16531668
/* executables and the current directory don't support long paths */
16541669
if (*argv && !strcmp(cmd, *argv))
@@ -1707,16 +1722,97 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
17071722
wenvblk = make_environment_block(deltaenv);
17081723

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

17131810
free(wenvblk);
17141811
free(wargs);
17151812

1716-
if (!ret) {
1717-
errno = ENOENT;
1813+
if (!ret)
17181814
return -1;
1719-
}
1815+
17201816
CloseHandle(pi.hThread);
17211817

17221818
/*
@@ -2687,7 +2783,7 @@ int symlink(const char *target, const char *link)
26872783
int len;
26882784

26892785
/* fail if symlinks are disabled or API is not supported (WinXP) */
2690-
if (!has_symlinks || !INIT_PROC_ADDR(CreateSymbolicLinkW)) {
2786+
if (!has_symlinks) {
26912787
errno = ENOSYS;
26922788
return -1;
26932789
}

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)