Skip to content

Commit f473526

Browse files
dschoGit for Windows Build Agent
authored andcommitted
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 be1f8e4 + e46fad2 commit f473526

File tree

7 files changed

+190
-20
lines changed

7 files changed

+190
-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,
@@ -1641,8 +1640,13 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
16411640
const char *dir, const char *prepend_cmd,
16421641
int fhin, int fhout, int fherr)
16431642
{
1644-
STARTUPINFOW si;
1643+
static int restrict_handle_inheritance = 1;
1644+
STARTUPINFOEXW si;
16451645
PROCESS_INFORMATION pi;
1646+
LPPROC_THREAD_ATTRIBUTE_LIST attr_list = NULL;
1647+
HANDLE stdhandles[3];
1648+
DWORD stdhandles_count = 0;
1649+
SIZE_T size;
16461650
struct strbuf args;
16471651
wchar_t wcmd[MAX_PATH], wdir[MAX_PATH], *wargs, *wenvblk = NULL;
16481652
unsigned flags = CREATE_UNICODE_ENVIRONMENT;
@@ -1679,11 +1683,23 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
16791683
CloseHandle(cons);
16801684
}
16811685
memset(&si, 0, sizeof(si));
1682-
si.cb = sizeof(si);
1683-
si.dwFlags = STARTF_USESTDHANDLES;
1684-
si.hStdInput = winansi_get_osfhandle(fhin);
1685-
si.hStdOutput = winansi_get_osfhandle(fhout);
1686-
si.hStdError = winansi_get_osfhandle(fherr);
1686+
si.StartupInfo.cb = sizeof(si);
1687+
si.StartupInfo.hStdInput = winansi_get_osfhandle(fhin);
1688+
si.StartupInfo.hStdOutput = winansi_get_osfhandle(fhout);
1689+
si.StartupInfo.hStdError = winansi_get_osfhandle(fherr);
1690+
1691+
/* The list of handles cannot contain duplicates */
1692+
if (si.StartupInfo.hStdInput != INVALID_HANDLE_VALUE)
1693+
stdhandles[stdhandles_count++] = si.StartupInfo.hStdInput;
1694+
if (si.StartupInfo.hStdOutput != INVALID_HANDLE_VALUE &&
1695+
si.StartupInfo.hStdOutput != si.StartupInfo.hStdInput)
1696+
stdhandles[stdhandles_count++] = si.StartupInfo.hStdOutput;
1697+
if (si.StartupInfo.hStdError != INVALID_HANDLE_VALUE &&
1698+
si.StartupInfo.hStdError != si.StartupInfo.hStdInput &&
1699+
si.StartupInfo.hStdError != si.StartupInfo.hStdOutput)
1700+
stdhandles[stdhandles_count++] = si.StartupInfo.hStdError;
1701+
if (stdhandles_count)
1702+
si.StartupInfo.dwFlags |= STARTF_USESTDHANDLES;
16871703

16881704
/* executables and the current directory don't support long paths */
16891705
if (*argv && !strcmp(cmd, *argv))
@@ -1742,16 +1758,97 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
17421758
wenvblk = make_environment_block(deltaenv);
17431759

17441760
memset(&pi, 0, sizeof(pi));
1745-
ret = CreateProcessW(*wcmd ? wcmd : NULL, wargs, NULL, NULL, TRUE,
1746-
flags, wenvblk, dir ? wdir : NULL, &si, &pi);
1761+
if (restrict_handle_inheritance && stdhandles_count &&
1762+
(InitializeProcThreadAttributeList(NULL, 1, 0, &size) ||
1763+
GetLastError() == ERROR_INSUFFICIENT_BUFFER) &&
1764+
(attr_list = (LPPROC_THREAD_ATTRIBUTE_LIST)
1765+
(HeapAlloc(GetProcessHeap(), 0, size))) &&
1766+
InitializeProcThreadAttributeList(attr_list, 1, 0, &size) &&
1767+
UpdateProcThreadAttribute(attr_list, 0,
1768+
PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
1769+
stdhandles,
1770+
stdhandles_count * sizeof(HANDLE),
1771+
NULL, NULL)) {
1772+
si.lpAttributeList = attr_list;
1773+
flags |= EXTENDED_STARTUPINFO_PRESENT;
1774+
}
1775+
1776+
ret = CreateProcessW(*wcmd ? wcmd : NULL, wargs, NULL, NULL,
1777+
stdhandles_count ? TRUE : FALSE,
1778+
flags, wenvblk, dir ? wdir : NULL,
1779+
&si.StartupInfo, &pi);
1780+
1781+
/*
1782+
* On Windows 2008 R2, it seems that specifying certain types of handles
1783+
* (such as FILE_TYPE_CHAR or FILE_TYPE_PIPE) will always produce an
1784+
* error. Rather than playing finicky and fragile games, let's just try
1785+
* to detect this situation and simply try again without restricting any
1786+
* handle inheritance. This is still better than failing to create
1787+
* processes.
1788+
*/
1789+
if (!ret && restrict_handle_inheritance && stdhandles_count) {
1790+
DWORD err = GetLastError();
1791+
struct strbuf buf = STRBUF_INIT;
1792+
1793+
if (err != ERROR_NO_SYSTEM_RESOURCES &&
1794+
/*
1795+
* On Windows 7 and earlier, handles on pipes and character
1796+
* devices are inherited automatically, and cannot be
1797+
* specified in the thread handle list. Rather than trying
1798+
* to catch each and every corner case (and running the
1799+
* chance of *still* forgetting a few), let's just fall
1800+
* back to creating the process without trying to limit the
1801+
* handle inheritance.
1802+
*/
1803+
!(err == ERROR_INVALID_PARAMETER &&
1804+
GetVersion() >> 16 < 9200) &&
1805+
!getenv("SUPPRESS_HANDLE_INHERITANCE_WARNING")) {
1806+
DWORD fl = 0;
1807+
int i;
1808+
1809+
setenv("SUPPRESS_HANDLE_INHERITANCE_WARNING", "1", 1);
1810+
1811+
for (i = 0; i < stdhandles_count; i++) {
1812+
HANDLE h = stdhandles[i];
1813+
strbuf_addf(&buf, "handle #%d: %p (type %lx, "
1814+
"handle info (%d) %lx\n", i, h,
1815+
GetFileType(h),
1816+
GetHandleInformation(h, &fl),
1817+
fl);
1818+
}
1819+
strbuf_addstr(&buf, "\nThis is a bug; please report it "
1820+
"at\nhttps://github.com/git-for-windows/"
1821+
"git/issues/new\n\n"
1822+
"To suppress this warning, please set "
1823+
"the environment variable\n\n"
1824+
"\tSUPPRESS_HANDLE_INHERITANCE_WARNING=1"
1825+
"\n");
1826+
}
1827+
restrict_handle_inheritance = 0;
1828+
flags &= ~EXTENDED_STARTUPINFO_PRESENT;
1829+
ret = CreateProcessW(*wcmd ? wcmd : NULL, wargs, NULL, NULL,
1830+
TRUE, flags, wenvblk, dir ? wdir : NULL,
1831+
&si.StartupInfo, &pi);
1832+
if (ret && buf.len) {
1833+
errno = err_win_to_posix(GetLastError());
1834+
warning("failed to restrict file handles (%ld)\n\n%s",
1835+
err, buf.buf);
1836+
}
1837+
strbuf_release(&buf);
1838+
} else if (!ret)
1839+
errno = err_win_to_posix(GetLastError());
1840+
1841+
if (si.lpAttributeList)
1842+
DeleteProcThreadAttributeList(si.lpAttributeList);
1843+
if (attr_list)
1844+
HeapFree(GetProcessHeap(), 0, attr_list);
17471845

17481846
free(wenvblk);
17491847
free(wargs);
17501848

1751-
if (!ret) {
1752-
errno = ENOENT;
1849+
if (!ret)
17531850
return -1;
1754-
}
1851+
17551852
CloseHandle(pi.hThread);
17561853

17571854
/*
@@ -2722,7 +2819,7 @@ int symlink(const char *target, const char *link)
27222819
int len;
27232820

27242821
/* fail if symlinks are disabled or API is not supported (WinXP) */
2725-
if (!has_symlinks || !INIT_PROC_ADDR(CreateSymbolicLinkW)) {
2822+
if (!has_symlinks) {
27262823
errno = ENOSYS;
27272824
return -1;
27282825
}

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
@@ -417,8 +417,6 @@ ifeq ($(uname_S),Windows)
417417
NO_GETTEXT = YesPlease
418418
NO_PYTHON = YesPlease
419419
ETAGS_TARGET = ETAGS
420-
NO_INET_PTON = YesPlease
421-
NO_INET_NTOP = YesPlease
422420
NO_POSIX_GOODIES = UnfortunatelyYes
423421
NATIVE_CRLF = YesPlease
424422
DEFAULT_HELP_FORMAT = html
@@ -587,8 +585,6 @@ ifneq (,$(findstring MINGW,$(uname_S)))
587585
NO_REGEX = YesPlease
588586
NO_PYTHON = YesPlease
589587
ETAGS_TARGET = ETAGS
590-
NO_INET_PTON = YesPlease
591-
NO_INET_NTOP = YesPlease
592588
NO_POSIX_GOODIES = UnfortunatelyYes
593589
DEFAULT_HELP_FORMAT = html
594590
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: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,13 +190,61 @@ static int testsuite(int argc, const char **argv)
190190
return !!ret;
191191
}
192192

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

198238
if (argc > 1 && !strcmp(argv[1], "testsuite"))
199239
exit(testsuite(argc - 1, argv + 1));
240+
241+
if (argc < 2)
242+
return 1;
243+
if (!strcmp(argv[1], "inherited-handle"))
244+
exit(inherit_handle(argv[0]));
245+
if (!strcmp(argv[1], "inherited-handle-child"))
246+
exit(inherit_handle_child());
247+
200248
if (argc < 3)
201249
return 1;
202250
while (!strcmp(argv[1], "env")) {

t/t0061-run-command.sh

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

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

0 commit comments

Comments
 (0)