Bug 1495512: Part 2 - Add test for 10-byte DLL interception; r=handyman
authorAaron Klotz <aklotz@mozilla.com>
Fri, 23 Nov 2018 05:57:01 +0000
changeset 504221 a31b6b37d11c256b60b539495be002114baff3c7
parent 504220 09573684485b7b49ba001af79be6b1fa5bec2848
child 504222 b188f2d1917b840fc6327246444afbbb141964de
push id10290
push userffxbld-merge
push dateMon, 03 Dec 2018 16:23:23 +0000
treeherdermozilla-beta@700bed2445e6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewershandyman
bugs1495512
milestone65.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1495512: Part 2 - Add test for 10-byte DLL interception; r=handyman Added a test to TestCrossProcessInterceptor that forcibly uses a 10-byte patch on NtMapViewOfSection (which is a realistic case) and then ensures that disabling the hook also works. Differential Revision: https://phabricator.services.mozilla.com/D10286
mozglue/tests/interceptor/TestDllInterceptor.cpp
--- a/mozglue/tests/interceptor/TestDllInterceptor.cpp
+++ b/mozglue/tests/interceptor/TestDllInterceptor.cpp
@@ -27,16 +27,30 @@ NTSTATUS NTAPI NtWriteFile(HANDLE, HANDL
                            PIO_STATUS_BLOCK, PVOID, ULONG,
                            PLARGE_INTEGER, PULONG);
 NTSTATUS NTAPI NtWriteFileGather(HANDLE, HANDLE, PIO_APC_ROUTINE, PVOID,
                                  PIO_STATUS_BLOCK, PFILE_SEGMENT_ELEMENT, ULONG,
                                  PLARGE_INTEGER, PULONG);
 NTSTATUS NTAPI NtQueryFullAttributesFile(POBJECT_ATTRIBUTES, PVOID);
 NTSTATUS NTAPI LdrLoadDll(PWCHAR filePath, PULONG flags, PUNICODE_STRING moduleFileName, PHANDLE handle);
 NTSTATUS NTAPI LdrUnloadDll(HMODULE);
+
+enum SECTION_INHERIT
+{
+  ViewShare = 1,
+  ViewUnmap = 2
+};
+
+NTSTATUS NTAPI
+NtMapViewOfSection(HANDLE aSection, HANDLE aProcess, PVOID* aBaseAddress,
+                   ULONG_PTR aZeroBits, SIZE_T aCommitSize,
+                   PLARGE_INTEGER aSectionOffset, PSIZE_T aViewSize,
+                   SECTION_INHERIT aInheritDisposition, ULONG aAllocationType,
+                   ULONG aProtectionFlags);
+
 // These pointers are disguised as PVOID to avoid pulling in obscure headers
 PVOID NTAPI LdrResolveDelayLoadedAPI(PVOID, PVOID, PVOID, PVOID, PVOID, ULONG);
 void CALLBACK ProcessCaretEvents(HWINEVENTHOOK, DWORD, HWND, LONG, LONG, DWORD, DWORD);
 void __fastcall BaseThreadInitThunk(BOOL aIsInitialThread, void* aStartAddress, void* aThreadParam);
 
 using namespace mozilla;
 
 struct payload {
@@ -77,33 +91,33 @@ decltype(auto) Apply(CallableT&& aFunc, 
 {
   return std::forward<CallableT>(aFunc)(Get<Indices>(std::forward<ArgTuple>(aArgs))...);
 }
 
 template <typename CallableT>
 bool TestFunction(CallableT aFunc);
 
 #define DEFINE_TEST_FUNCTION(calling_convention) \
-  template <typename R, typename... Args, typename... TestArgs> \
-  bool TestFunction(WindowsDllInterceptor::FuncHookType<R (calling_convention *)(Args...)>&& aFunc, \
+  template <template <typename I, typename F> class FuncHookT, typename InterceptorT, typename R, typename... Args, typename... TestArgs> \
+  bool TestFunction(FuncHookT<InterceptorT, R (calling_convention *)(Args...)>&& aFunc, \
                     bool (* aPred)(R), TestArgs... aArgs) \
   { \
-    using FuncHookType = WindowsDllInterceptor::FuncHookType<R (calling_convention *)(Args...)>; \
+    using FuncHookType = FuncHookT<InterceptorT, R (calling_convention *)(Args...)>; \
     using ArgTuple = Tuple<Args...>; \
     using Indices = std::index_sequence_for<Args...>; \
     ArgTuple fakeArgs{ std::forward<TestArgs>(aArgs)... }; \
     return aPred(Apply(std::forward<FuncHookType>(aFunc), std::forward<ArgTuple>(fakeArgs), Indices())); \
   } \
   \
   /* Specialization for functions returning void */ \
-  template <typename... Args, typename PredicateT, typename... TestArgs> \
-  bool TestFunction(WindowsDllInterceptor::FuncHookType<void (calling_convention *)(Args...)>&& aFunc, \
+  template <template <typename I, typename F> class FuncHookT, typename InterceptorT, typename... Args, typename PredicateT, typename... TestArgs> \
+  bool TestFunction(FuncHookT<InterceptorT, void (calling_convention *)(Args...)>&& aFunc, \
                     PredicateT&& aPred, TestArgs... aArgs) \
   { \
-    using FuncHookType = WindowsDllInterceptor::FuncHookType<void (calling_convention *)(Args...)>; \
+    using FuncHookType = FuncHookT<InterceptorT, void (calling_convention *)(Args...)>; \
     using ArgTuple = Tuple<Args...>; \
     using Indices = std::index_sequence_for<Args...>; \
     ArgTuple fakeArgs{ std::forward<TestArgs>(aArgs)... }; \
     Apply(std::forward<FuncHookType>(aFunc), std::forward<ArgTuple>(fakeArgs), Indices()); \
     return true; \
   }
 
 // C++11 allows empty arguments to macros. clang works just fine. MSVC does the
@@ -116,21 +130,21 @@ DEFINE_TEST_FUNCTION()
 
 #ifdef _M_IX86
 DEFINE_TEST_FUNCTION(__stdcall)
 DEFINE_TEST_FUNCTION(__fastcall)
 #endif // _M_IX86
 
 // Test the hooked function against the supplied predicate
 template <typename OrigFuncT, typename PredicateT, typename... Args>
-bool CheckHook(WindowsDllInterceptor::FuncHookType<OrigFuncT> &aOrigFunc,
+bool CheckHook(OrigFuncT &aOrigFunc,
                const char* aDllName, const char* aFuncName, PredicateT&& aPred,
                Args... aArgs)
 {
-  if (TestFunction(std::forward<WindowsDllInterceptor::FuncHookType<OrigFuncT>>(aOrigFunc), std::forward<PredicateT>(aPred), std::forward<Args>(aArgs)...)) {
+  if (TestFunction(std::forward<OrigFuncT>(aOrigFunc), std::forward<PredicateT>(aPred), std::forward<Args>(aArgs)...)) {
     printf("TEST-PASS | WindowsDllInterceptor | "
            "Executed hooked function %s from %s\n", aFuncName, aDllName);
     return true;
   }
   printf("TEST-FAILED | WindowsDllInterceptor | "
          "Failed to execute hooked function %s from %s\n", aFuncName, aDllName);
   return false;
 }
@@ -394,16 +408,80 @@ bool ShouldTestTipTsf()
   if (!LoadLibraryW(fullPath)) {
     return false;
   }
 
   // Leak the module so that it's loaded for the interceptor test
   return true;
 }
 
+#if defined(_M_X64)
+
+// Use VMSharingPolicyUnique for the TenByteInterceptor, as it needs to
+// reserve its trampoline memory in a special location.
+using TenByteInterceptor =
+  mozilla::interceptor::WindowsDllInterceptor<
+    mozilla::interceptor::VMSharingPolicyUnique<
+      mozilla::interceptor::MMPolicyInProcess,
+      mozilla::interceptor::kDefaultTrampolineSize>>;
+
+static TenByteInterceptor::FuncHookType<decltype(&::NtMapViewOfSection)>
+  orig_NtMapViewOfSection;
+
+#endif // defined(_M_X64)
+
+bool
+TestTenByteDetour()
+{
+#if defined(_M_X64)
+  auto pNtMapViewOfSection =
+    reinterpret_cast<decltype(&::NtMapViewOfSection)>(
+      ::GetProcAddress(::GetModuleHandleW(L"ntdll.dll"), "NtMapViewOfSection"));
+  if (!pNtMapViewOfSection) {
+    printf("TEST-FAILED | WindowsDllInterceptor | "
+           "Failed to resolve ntdll!NtMapViewOfSection\n");
+    return false;
+  }
+
+  { // Scope for tenByteInterceptor
+    TenByteInterceptor tenByteInterceptor;
+    tenByteInterceptor.TestOnlyDetourInit(L"ntdll.dll",
+      mozilla::interceptor::DetourFlags::eTestOnlyForce10BytePatch);
+    if (!orig_NtMapViewOfSection.SetDetour(tenByteInterceptor,
+                                           "NtMapViewOfSection", nullptr)) {
+      printf("TEST-FAILED | WindowsDllInterceptor | "
+             "Failed to hook ntdll!NtMapViewOfSection via 10-byte patch\n");
+      return false;
+    }
+
+    auto pred = &Predicates<decltype(&::NtMapViewOfSection)>::Ignore<((NTSTATUS)0)>;
+
+    if (!CheckHook(orig_NtMapViewOfSection, "ntdll.dll", "NtMapViewOfSection", pred)) {
+      // CheckHook has already printed the error message for us
+      return false;
+    }
+  }
+
+  // Now ensure that our hook cleanup worked
+  NTSTATUS status = pNtMapViewOfSection(nullptr, nullptr, nullptr, 0, 0, nullptr,
+                                        nullptr, ((SECTION_INHERIT)0), 0, 0);
+  if (NT_SUCCESS(status)) {
+    printf("TEST-FAILED | WindowsDllInterceptor | "
+           "Unexpected successful call to ntdll!NtMapViewOfSection after removing 10-byte hook\n");
+    return false;
+  }
+
+  printf("TEST-PASS | WindowsDllInterceptor | "
+         "Successfully unhooked ntdll!NtMapViewOfSection via 10-byte patch\n");
+  return true;
+#else
+  return true;
+#endif
+}
+
 extern "C"
 int wmain(int argc, wchar_t* argv[])
 {
   LARGE_INTEGER start;
   QueryPerformanceCounter(&start);
 
   // We disable this part of the test because the code coverage instrumentation
   // injects code in rotatePayload in a way that WindowsDllInterceptor doesn't
@@ -532,17 +610,18 @@ int wmain(int argc, wchar_t* argv[])
       TEST_HOOK(wininet.dll, HttpEndRequestA, Equals, FALSE) &&
       TEST_HOOK(wininet.dll, InternetQueryOptionA, Equals, FALSE) &&
 
       TEST_HOOK(sspicli.dll, AcquireCredentialsHandleA, NotEquals, SEC_E_OK) &&
       TEST_HOOK(sspicli.dll, QueryCredentialsAttributesA, NotEquals, SEC_E_OK) &&
       TEST_HOOK(sspicli.dll, FreeCredentialsHandle, NotEquals, SEC_E_OK) &&
 
       TEST_DETOUR_SKIP_EXEC(kernel32.dll, BaseThreadInitThunk) &&
-      TEST_DETOUR_SKIP_EXEC(ntdll.dll, LdrLoadDll)) {
+      TEST_DETOUR_SKIP_EXEC(ntdll.dll, LdrLoadDll) &&
+      TestTenByteDetour()) {
     printf("TEST-PASS | WindowsDllInterceptor | all checks passed\n");
 
     LARGE_INTEGER end, freq;
     QueryPerformanceCounter(&end);
 
     QueryPerformanceFrequency(&freq);
 
     LARGE_INTEGER result;