Split off two CreateProcess tests.
* Add CreateProcess_DefaultInherit * Add CreateProcess_DefaultInherit_XPPipeBug
This commit is contained in:
parent
a17fac4e5a
commit
11cccfa4ae
@ -195,175 +195,6 @@ static void Test_CreateNoWindow_HiddenVsNothing() {
|
||||
}
|
||||
}
|
||||
|
||||
REGISTER(Test_CreateProcess_DefaultInherit, always);
|
||||
static void Test_CreateProcess_DefaultInherit() {
|
||||
// If CreateProcess is called with these parameters:
|
||||
// - bInheritHandles=FALSE
|
||||
// - STARTF_USESTDHANDLES is not specified
|
||||
// - the "CreationConsoleMode" is Inherit (see console-handles.md)
|
||||
// then Windows duplicates each of STDIN/STDOUT/STDERR to the child.
|
||||
//
|
||||
// There are variations between OS releases, especially with regards to
|
||||
// how console handles work.
|
||||
|
||||
{
|
||||
// Base case: a non-inheritable pipe is still inherited.
|
||||
Worker p;
|
||||
auto pipe = newPipe(p, false);
|
||||
auto wh = std::get<1>(pipe).setStdin().setStdout().setStderr();
|
||||
auto c = p.child({ false });
|
||||
CHECK(compareObjectHandles(c.getStdin(), wh));
|
||||
CHECK(compareObjectHandles(c.getStdout(), wh));
|
||||
CHECK(compareObjectHandles(c.getStderr(), wh));
|
||||
// CreateProcess makes separate handles for stdin/stdout/stderr.
|
||||
CHECK(c.getStdin().value() != c.getStdout().value());
|
||||
CHECK(c.getStdout().value() != c.getStderr().value());
|
||||
CHECK(c.getStdin().value() != c.getStderr().value());
|
||||
// Calling FreeConsole in the child does not free the duplicated
|
||||
// handles.
|
||||
c.detach();
|
||||
CHECK(compareObjectHandles(c.getStdin(), wh));
|
||||
CHECK(compareObjectHandles(c.getStdout(), wh));
|
||||
CHECK(compareObjectHandles(c.getStderr(), wh));
|
||||
}
|
||||
{
|
||||
// Bogus values are transformed into zero.
|
||||
Worker p;
|
||||
Handle::invent(0x10000ull, p).setStdin().setStdout();
|
||||
Handle::invent(0x0ull, p).setStderr();
|
||||
auto c = p.child({ false });
|
||||
CHECK(handleInts(stdHandles(c)) == (std::vector<uint64_t> {0,0,0}));
|
||||
}
|
||||
|
||||
{
|
||||
// The GetCurrentProcess() psuedo-handle (i.e. INVALID_HANDLE_VALUE)
|
||||
// is translated to a real handle value for the child process.
|
||||
// Naturally, this was unintended behavior, and as of Windows 8.1, it
|
||||
// is instead translated to NULL.
|
||||
Worker p;
|
||||
Handle::invent(GetCurrentProcess(), p).setStdout();
|
||||
auto c = p.child({ false });
|
||||
if (isAtLeastWin8_1()) {
|
||||
CHECK(c.getStdout().value() == nullptr);
|
||||
} else {
|
||||
CHECK(c.getStdout().value() != GetCurrentProcess());
|
||||
auto handleToPInP = Handle::dup(p.processHandle(), p);
|
||||
CHECK(compareObjectHandles(c.getStdout(), handleToPInP));
|
||||
}
|
||||
}
|
||||
|
||||
if (isAtLeastWin8()) {
|
||||
// On Windows 8 and up, if a standard handle we duplicate just happens
|
||||
// to be a console handle, that isn't sufficient reason for FreeConsole
|
||||
// to close it.
|
||||
Worker p;
|
||||
auto c = p.child({ false });
|
||||
auto ph = stdHandles(p);
|
||||
auto ch = stdHandles(c);
|
||||
auto check = [&]() {
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
CHECK(compareObjectHandles(ph[i], ch[i]));
|
||||
CHECK_EQ(ph[i].inheritable(), ch[i].inheritable());
|
||||
}
|
||||
};
|
||||
check();
|
||||
c.detach();
|
||||
check();
|
||||
}
|
||||
|
||||
{
|
||||
// Traditional console-like values are passed through as-is,
|
||||
// up to 0x0FFFFFFFull.
|
||||
Worker p;
|
||||
Handle::invent(0x0FFFFFFFull, p).setStdin();
|
||||
Handle::invent(0x10000003ull, p).setStdout();
|
||||
Handle::invent(0x00000003ull, p).setStderr();
|
||||
auto c = p.child({ false });
|
||||
if (isAtLeastWin8()) {
|
||||
// These values are invalid on Windows 8 and turned into NULL.
|
||||
CHECK(handleInts(stdHandles(c)) ==
|
||||
(std::vector<uint64_t> { 0, 0, 0 }));
|
||||
} else {
|
||||
CHECK(handleInts(stdHandles(c)) ==
|
||||
(std::vector<uint64_t> { 0x0FFFFFFFull, 0, 3 }));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// Test setting STDIN/STDOUT/STDERR to non-inheritable console handles.
|
||||
//
|
||||
// On old releases, default inheritance's handle duplication does not
|
||||
// apply to console handles, and a console handle is inherited if and
|
||||
// only if it is inheritable.
|
||||
//
|
||||
// On new releases, this will Just Work.
|
||||
//
|
||||
Worker p;
|
||||
p.getStdout().setFirstChar('A');
|
||||
p.openConin(false).setStdin();
|
||||
p.newBuffer(false, 'B').setStdout().setStderr();
|
||||
auto c = p.child({ false });
|
||||
|
||||
if (!isAtLeastWin8()) {
|
||||
CHECK(handleValues(stdHandles(p)) == handleValues(stdHandles(c)));
|
||||
CHECK(!c.getStdin().tryFlags());
|
||||
CHECK(!c.getStdout().tryFlags());
|
||||
CHECK(!c.getStderr().tryFlags());
|
||||
} else {
|
||||
// In Win8, a console handle works like all other handles.
|
||||
CHECK_EQ(c.getStdout().firstChar(), 'B');
|
||||
CHECK(compareObjectHandles(p.getStdout(), p.getStderr()));
|
||||
CHECK(compareObjectHandles(c.getStdout(), c.getStderr()));
|
||||
CHECK(compareObjectHandles(p.getStdout(), c.getStdout()));
|
||||
CHECK(!c.getStdout().inheritable());
|
||||
CHECK(!c.getStderr().inheritable());
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// Windows XP bug: default inheritance doesn't work with the read end
|
||||
// of a pipe, even if it's inheritable. It works with the write end.
|
||||
auto check = [](Worker &proc, Handle correct, bool expectBroken) {
|
||||
CHECK((proc.getStdin().value() == nullptr) == expectBroken);
|
||||
CHECK((proc.getStdout().value() == nullptr) == expectBroken);
|
||||
CHECK((proc.getStderr().value() == nullptr) == expectBroken);
|
||||
if (!expectBroken) {
|
||||
CHECK(compareObjectHandles(proc.getStdin(), correct));
|
||||
CHECK(compareObjectHandles(proc.getStdout(), correct));
|
||||
CHECK(compareObjectHandles(proc.getStderr(), correct));
|
||||
}
|
||||
};
|
||||
|
||||
Worker p;
|
||||
|
||||
auto pipe = newPipe(p, false);
|
||||
auto rh = std::get<0>(pipe).setStdin().setStdout().setStderr();
|
||||
auto c1 = p.child({ false });
|
||||
check(c1, rh, !isAtLeastVista());
|
||||
|
||||
// Marking the handle itself inheritable makes no difference.
|
||||
rh.setInheritable(true);
|
||||
auto c2 = p.child({ false });
|
||||
check(c2, rh, !isAtLeastVista());
|
||||
|
||||
// If we enter bInheritHandles=TRUE mode, it works.
|
||||
auto c3 = p.child({ true });
|
||||
check(c3, rh, false);
|
||||
|
||||
// Using STARTF_USESTDHANDLES works too.
|
||||
Handle::invent(nullptr, p).setStdin().setStdout().setStderr();
|
||||
auto c4 = p.child({ true, 0, { rh, rh, rh }});
|
||||
check(c4, rh, false);
|
||||
|
||||
// Also test the write end of the pipe.
|
||||
auto wh = std::get<1>(pipe).setStdin().setStdout().setStderr();
|
||||
auto c5 = p.child({ false });
|
||||
check(c5, wh, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// MSDN's CreateProcess page currently has this note in it:
|
||||
//
|
||||
// Important The caller is responsible for ensuring that the standard
|
||||
|
127
misc/buffer-tests/HandleTests/CreateProcess_DefaultInherit.cc
Executable file
127
misc/buffer-tests/HandleTests/CreateProcess_DefaultInherit.cc
Executable file
@ -0,0 +1,127 @@
|
||||
#include <TestCommon.h>
|
||||
|
||||
// If CreateProcess is called with these parameters:
|
||||
// - bInheritHandles=FALSE
|
||||
// - STARTF_USESTDHANDLES is not specified
|
||||
// - the "CreationConsoleMode" is Inherit (see console-handles.md)
|
||||
// then Windows duplicates each of STDIN/STDOUT/STDERR to the child.
|
||||
//
|
||||
// There are variations between OS releases, especially with regards to
|
||||
// how console handles work.
|
||||
|
||||
REGISTER(Test_CreateProcess_DefaultInherit, always);
|
||||
static void Test_CreateProcess_DefaultInherit() {
|
||||
{
|
||||
// Base case: a non-inheritable pipe is still inherited.
|
||||
Worker p;
|
||||
auto pipe = newPipe(p, false);
|
||||
auto wh = std::get<1>(pipe).setStdin().setStdout().setStderr();
|
||||
auto c = p.child({ false });
|
||||
CHECK(compareObjectHandles(c.getStdin(), wh));
|
||||
CHECK(compareObjectHandles(c.getStdout(), wh));
|
||||
CHECK(compareObjectHandles(c.getStderr(), wh));
|
||||
// CreateProcess makes separate handles for stdin/stdout/stderr.
|
||||
CHECK(c.getStdin().value() != c.getStdout().value());
|
||||
CHECK(c.getStdout().value() != c.getStderr().value());
|
||||
CHECK(c.getStdin().value() != c.getStderr().value());
|
||||
// Calling FreeConsole in the child does not free the duplicated
|
||||
// handles.
|
||||
c.detach();
|
||||
CHECK(compareObjectHandles(c.getStdin(), wh));
|
||||
CHECK(compareObjectHandles(c.getStdout(), wh));
|
||||
CHECK(compareObjectHandles(c.getStderr(), wh));
|
||||
}
|
||||
{
|
||||
// Bogus values are transformed into zero.
|
||||
Worker p;
|
||||
Handle::invent(0x10000ull, p).setStdin().setStdout();
|
||||
Handle::invent(0x0ull, p).setStderr();
|
||||
auto c = p.child({ false });
|
||||
CHECK(handleInts(stdHandles(c)) == (std::vector<uint64_t> {0,0,0}));
|
||||
}
|
||||
|
||||
{
|
||||
// The GetCurrentProcess() psuedo-handle (i.e. INVALID_HANDLE_VALUE)
|
||||
// is translated to a real handle value for the child process.
|
||||
// Naturally, this was unintended behavior, and as of Windows 8.1, it
|
||||
// is instead translated to NULL.
|
||||
Worker p;
|
||||
Handle::invent(GetCurrentProcess(), p).setStdout();
|
||||
auto c = p.child({ false });
|
||||
if (isAtLeastWin8_1()) {
|
||||
CHECK(c.getStdout().value() == nullptr);
|
||||
} else {
|
||||
CHECK(c.getStdout().value() != GetCurrentProcess());
|
||||
auto handleToPInP = Handle::dup(p.processHandle(), p);
|
||||
CHECK(compareObjectHandles(c.getStdout(), handleToPInP));
|
||||
}
|
||||
}
|
||||
|
||||
if (isAtLeastWin8()) {
|
||||
// On Windows 8 and up, if a standard handle we duplicate just happens
|
||||
// to be a console handle, that isn't sufficient reason for FreeConsole
|
||||
// to close it.
|
||||
Worker p;
|
||||
auto c = p.child({ false });
|
||||
auto ph = stdHandles(p);
|
||||
auto ch = stdHandles(c);
|
||||
auto check = [&]() {
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
CHECK(compareObjectHandles(ph[i], ch[i]));
|
||||
CHECK_EQ(ph[i].inheritable(), ch[i].inheritable());
|
||||
}
|
||||
};
|
||||
check();
|
||||
c.detach();
|
||||
check();
|
||||
}
|
||||
|
||||
{
|
||||
// Traditional console-like values are passed through as-is,
|
||||
// up to 0x0FFFFFFFull.
|
||||
Worker p;
|
||||
Handle::invent(0x0FFFFFFFull, p).setStdin();
|
||||
Handle::invent(0x10000003ull, p).setStdout();
|
||||
Handle::invent(0x00000003ull, p).setStderr();
|
||||
auto c = p.child({ false });
|
||||
if (isAtLeastWin8()) {
|
||||
// These values are invalid on Windows 8 and turned into NULL.
|
||||
CHECK(handleInts(stdHandles(c)) ==
|
||||
(std::vector<uint64_t> { 0, 0, 0 }));
|
||||
} else {
|
||||
CHECK(handleInts(stdHandles(c)) ==
|
||||
(std::vector<uint64_t> { 0x0FFFFFFFull, 0, 3 }));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// Test setting STDIN/STDOUT/STDERR to non-inheritable console handles.
|
||||
//
|
||||
// On old releases, default inheritance's handle duplication does not
|
||||
// apply to console handles, and a console handle is inherited if and
|
||||
// only if it is inheritable.
|
||||
//
|
||||
// On new releases, this will Just Work.
|
||||
//
|
||||
Worker p;
|
||||
p.getStdout().setFirstChar('A');
|
||||
p.openConin(false).setStdin();
|
||||
p.newBuffer(false, 'B').setStdout().setStderr();
|
||||
auto c = p.child({ false });
|
||||
|
||||
if (!isAtLeastWin8()) {
|
||||
CHECK(handleValues(stdHandles(p)) == handleValues(stdHandles(c)));
|
||||
CHECK(!c.getStdin().tryFlags());
|
||||
CHECK(!c.getStdout().tryFlags());
|
||||
CHECK(!c.getStderr().tryFlags());
|
||||
} else {
|
||||
// In Win8, a console handle works like all other handles.
|
||||
CHECK_EQ(c.getStdout().firstChar(), 'B');
|
||||
CHECK(compareObjectHandles(p.getStdout(), p.getStderr()));
|
||||
CHECK(compareObjectHandles(c.getStdout(), c.getStderr()));
|
||||
CHECK(compareObjectHandles(p.getStdout(), c.getStdout()));
|
||||
CHECK(!c.getStdout().inheritable());
|
||||
CHECK(!c.getStderr().inheritable());
|
||||
}
|
||||
}
|
||||
}
|
44
misc/buffer-tests/HandleTests/CreateProcess_DefaultInherit_XPPipeBug.cc
Executable file
44
misc/buffer-tests/HandleTests/CreateProcess_DefaultInherit_XPPipeBug.cc
Executable file
@ -0,0 +1,44 @@
|
||||
#include <TestCommon.h>
|
||||
|
||||
// Windows XP bug: default inheritance doesn't work with the read end
|
||||
// of a pipe, even if it's inheritable. It works with the write end.
|
||||
|
||||
REGISTER(Test_CreateProcess_DefaultInherit_XPPipeBug, always);
|
||||
static void Test_CreateProcess_DefaultInherit_XPPipeBug() {
|
||||
auto check = [](Worker &proc, Handle correct, bool expectBroken) {
|
||||
CHECK((proc.getStdin().value() == nullptr) == expectBroken);
|
||||
CHECK((proc.getStdout().value() == nullptr) == expectBroken);
|
||||
CHECK((proc.getStderr().value() == nullptr) == expectBroken);
|
||||
if (!expectBroken) {
|
||||
CHECK(compareObjectHandles(proc.getStdin(), correct));
|
||||
CHECK(compareObjectHandles(proc.getStdout(), correct));
|
||||
CHECK(compareObjectHandles(proc.getStderr(), correct));
|
||||
}
|
||||
};
|
||||
|
||||
Worker p;
|
||||
|
||||
auto pipe = newPipe(p, false);
|
||||
auto rh = std::get<0>(pipe).setStdin().setStdout().setStderr();
|
||||
auto c1 = p.child({ false });
|
||||
check(c1, rh, !isAtLeastVista());
|
||||
|
||||
// Marking the handle itself inheritable makes no difference.
|
||||
rh.setInheritable(true);
|
||||
auto c2 = p.child({ false });
|
||||
check(c2, rh, !isAtLeastVista());
|
||||
|
||||
// If we enter bInheritHandles=TRUE mode, it works.
|
||||
auto c3 = p.child({ true });
|
||||
check(c3, rh, false);
|
||||
|
||||
// Using STARTF_USESTDHANDLES works too.
|
||||
Handle::invent(nullptr, p).setStdin().setStdout().setStderr();
|
||||
auto c4 = p.child({ true, 0, { rh, rh, rh }});
|
||||
check(c4, rh, false);
|
||||
|
||||
// Also test the write end of the pipe.
|
||||
auto wh = std::get<1>(pipe).setStdin().setStdout().setStderr();
|
||||
auto c5 = p.child({ false });
|
||||
check(c5, wh, false);
|
||||
}
|
@ -42,6 +42,8 @@ TEST_OBJECTS = \
|
||||
|
||||
HANDLETESTS_OBJECTS = \
|
||||
build/obj/HandleTests/CreateProcess.o \
|
||||
build/obj/HandleTests/CreateProcess_DefaultInherit.o \
|
||||
build/obj/HandleTests/CreateProcess_DefaultInherit_XPPipeBug.o \
|
||||
build/obj/HandleTests/MiscTests.o \
|
||||
build/obj/HandleTests/Modern.o \
|
||||
build/obj/HandleTests/Traditional.o \
|
||||
|
Loading…
Reference in New Issue
Block a user