Speed handle object comparison by reducing NtQuerySystemInformation calls

This commit is contained in:
Ryan Prichard 2015-10-25 11:51:28 -05:00
parent ac238429fc
commit 28f11b7f9b
6 changed files with 126 additions and 52 deletions

View File

@ -50,16 +50,6 @@ static void Test_CreateProcess_STARTUPINFOEX() {
// handles didn't revert to the original default, but were inherited.
p.openConout(true);
// Verify that compareObjectHandles is working...
{
CHECK(!compareObjectHandles(ph1, ph2));
auto dupTest = ph1.dup();
CHECK(compareObjectHandles(ph1, dupTest));
dupTest.close();
Worker other;
CHECK(compareObjectHandles(ph1, ph1.dup(other)));
}
auto testSetupOneHandle = [&](SpawnParams sp, size_t cb, HANDLE inherit) {
sp.sui.cb = cb;
sp.inheritCount = 1;
@ -159,10 +149,13 @@ static void Test_CreateProcess_STARTUPINFOEX() {
auto ch2 = Handle::invent(ph2.value(), c);
auto ch3 = Handle::invent(ph3.value(), c);
auto ch4 = Handle::invent(ph4.value(), c);
CHECK(!compareObjectHandles(ph1, ch1));
CHECK(!compareObjectHandles(ph2, ch2));
CHECK(!compareObjectHandles(ph3, ch3));
CHECK(!compareObjectHandles(ph4, ch4));
{
ObjectSnap snap;
CHECK(!snap.eq(ph1, ch1));
CHECK(!snap.eq(ph2, ch2));
CHECK(!snap.eq(ph3, ch3));
CHECK(!snap.eq(ph4, ch4));
}
}
}

View File

@ -17,9 +17,10 @@ static void Test_CreateProcess_DefaultInherit() {
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));
{
ObjectSnap snap;
CHECK(snap.eq({ c.getStdin(), c.getStdout(), 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());
@ -27,9 +28,10 @@ static void Test_CreateProcess_DefaultInherit() {
// 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));
{
ObjectSnap snap;
CHECK(snap.eq({ c.getStdin(), c.getStdout(), c.getStderr(), wh }));
}
}
{
// Bogus values are transformed into zero.
@ -49,8 +51,9 @@ static void Test_CreateProcess_DefaultInherit() {
auto ph = stdHandles(p);
auto ch = stdHandles(c);
auto check = [&]() {
ObjectSnap snap;
for (int i = 0; i < 3; ++i) {
CHECK(compareObjectHandles(ph[i], ch[i]));
CHECK(snap.eq(ph[i], ch[i]));
CHECK_EQ(ph[i].inheritable(), ch[i].inheritable());
}
};
@ -100,9 +103,9 @@ static void Test_CreateProcess_DefaultInherit() {
} 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()));
ObjectSnap snap;
CHECK(snap.eq({ p.getStdout(), p.getStderr(),
c.getStdout(), c.getStderr() }));
CHECK(!c.getStdout().inheritable());
CHECK(!c.getStderr().inheritable());
}

View File

@ -10,9 +10,10 @@ static void Test_CreateProcess_DefaultInherit_XPPipeBug() {
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));
ObjectSnap snap;
CHECK(snap.eq({
proc.getStdin(), proc.getStdout(), proc.getStderr(), correct
}));
}
};

View File

@ -1,5 +1,34 @@
#include <TestCommon.h>
REGISTER(Test_CompareObjectHandles, always);
static void Test_CompareObjectHandles() {
// Verify that compareObjectHandles and ObjectSnap are working.
Worker p;
Worker other;
auto pipe1 = newPipe(p, true);
auto ph1 = std::get<0>(pipe1);
auto ph2 = std::get<1>(pipe1);
auto ph1dup = ph1.dup();
auto ph1other = ph1.dup(other);
ObjectSnap snap;
CHECK(!compareObjectHandles(ph1, ph2));
CHECK(compareObjectHandles(ph1, ph1dup));
CHECK(compareObjectHandles(ph1, ph1other));
CHECK(!snap.eq(ph1, ph2));
CHECK(snap.eq(ph1, ph1dup));
CHECK(snap.eq(ph1, ph1other));
CHECK(snap.eq({ ph1, ph1other, ph1dup }));
CHECK(!snap.eq({ ph2, ph1, ph1other, ph1dup }));
CHECK(!snap.eq({ ph1, ph2, ph1other, ph1dup }));
CHECK(!snap.eq({ ph1, ph1other, ph2, ph1dup }));
CHECK(!snap.eq({ ph1, ph1other, ph1dup, ph2 }));
}
REGISTER(Test_IntrinsicInheritFlags, always);
static void Test_IntrinsicInheritFlags() {
// Console handles have an inherit flag, just as kernel handles do.

View File

@ -59,40 +59,77 @@ void *ntHandlePointer(const std::vector<SYSTEM_HANDLE_ENTRY> &table,
return ret;
}
bool hasBuiltinCompareObjectHandles() {
static auto kernelbase = LoadLibraryW(L"KernelBase.dll");
if (kernelbase != nullptr) {
static auto proc = GetProcAddress(kernelbase, "CompareObjectHandles");
if (proc != nullptr) {
return true;
}
}
return false;
}
bool compareObjectHandles(RemoteHandle h1, RemoteHandle h2) {
static auto kernelbaseCheck = LoadLibraryW(L"KernelBase.dll");
if (kernelbaseCheck != nullptr) {
if (hasBuiltinCompareObjectHandles()) {
static OsModule kernelbase(L"KernelBase.dll");
static auto comp =
reinterpret_cast<BOOL(WINAPI*)(HANDLE,HANDLE)>(
kernelbase.proc("CompareObjectHandles"));
if (comp != nullptr) {
HANDLE h1local = nullptr;
HANDLE h2local = nullptr;
bool dup1 = DuplicateHandle(
h1.worker().processHandle(),
h1.value(),
GetCurrentProcess(),
&h1local,
0, false, DUPLICATE_SAME_ACCESS);
bool dup2 = DuplicateHandle(
h2.worker().processHandle(),
h2.value(),
GetCurrentProcess(),
&h2local,
0, false, DUPLICATE_SAME_ACCESS);
bool ret = dup1 && dup2 && comp(h1local, h2local);
if (dup1) {
CloseHandle(h1local);
ASSERT(comp != nullptr);
HANDLE h1local = nullptr;
HANDLE h2local = nullptr;
bool dup1 = DuplicateHandle(
h1.worker().processHandle(),
h1.value(),
GetCurrentProcess(),
&h1local,
0, false, DUPLICATE_SAME_ACCESS);
bool dup2 = DuplicateHandle(
h2.worker().processHandle(),
h2.value(),
GetCurrentProcess(),
&h2local,
0, false, DUPLICATE_SAME_ACCESS);
bool ret = dup1 && dup2 && comp(h1local, h2local);
if (dup1) {
CloseHandle(h1local);
}
if (dup2) {
CloseHandle(h2local);
}
return ret;
} else {
auto table = queryNtHandles();
return ntHandlePointer(table, h1) == ntHandlePointer(table, h2);
}
}
ObjectSnap::ObjectSnap() {
if (!hasBuiltinCompareObjectHandles()) {
m_table = queryNtHandles();
}
}
bool ObjectSnap::eq(std::initializer_list<RemoteHandle> handles) {
if (handles.size() < 2) {
return true;
}
if (hasBuiltinCompareObjectHandles()) {
for (auto i = handles.begin() + 1; i < handles.end(); ++i) {
if (!compareObjectHandles(*handles.begin(), *i)) {
return false;
}
if (dup2) {
CloseHandle(h2local);
}
} else {
HANDLE first = ntHandlePointer(m_table, *handles.begin());
for (auto i = handles.begin() + 1; i < handles.end(); ++i) {
if (first != ntHandlePointer(m_table, *i)) {
return false;
}
return ret;
}
}
auto table = queryNtHandles();
return ntHandlePointer(table, h1) == ntHandlePointer(table, h2);
return true;
}
void registerTest(const std::string &name, bool (&cond)(), void (&func)()) {

View File

@ -50,8 +50,19 @@ void printTestName(const std::string &name);
// NT kernel handle query
void *ntHandlePointer(const std::vector<SYSTEM_HANDLE_ENTRY> &table,
RemoteHandle h);
bool hasBuiltinCompareObjectHandles();
bool compareObjectHandles(RemoteHandle h1, RemoteHandle h2);
// NT kernel handle->object snapshot
class ObjectSnap {
public:
ObjectSnap();
bool eq(std::initializer_list<RemoteHandle> handles);
bool eq(RemoteHandle h1, RemoteHandle h2) { return eq({h1, h2}); }
private:
std::vector<SYSTEM_HANDLE_ENTRY> m_table;
};
// Misc
std::tuple<RemoteHandle, RemoteHandle> newPipe(
RemoteWorker &w, BOOL inheritable=FALSE);