To reproduce: Download and run the latest Firefox ASAN build (https://firefox-ci-tc.services.mozilla.com/api/index/v1/task/gecko.v2.mozilla-central.latest.firefox.win64-asan-opt/artifacts/public/build/target.zip) on Windows 11 (version 10.0.22621 Build 22621); it will crash on launch. Note that this doesn't seem to crash on another Windows 11 VM I've tried, so I'm not sure how reproducible it is across machines, but it reproduces on my machine every time. The problem seems to be that when overriding the memset function in OverrideFunctionWithRedirectJump(), the relative_offset is stored as a uptr. Per the Intel x64 instruction set reference (https://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.pdf - warning: large PDF), on page 646 the jmp instruction (specifically the near jump flavors that start with E9, which are the ones the OverrideFunctionWithRedirectJump() considers) treats the offset as a signed displacement. This causes an incorrect value to be stored for REAL(memset) which points to uninitialized memory, and a crash the next time that gets called. The fix is to simply treat that offset as signed. I have also added a test case. Fixes https://github.com/llvm/llvm-project/issues/58846 Differential Revision: https://reviews.llvm.org/D137788
725 lines
27 KiB
C++
725 lines
27 KiB
C++
//===-- interception_win_test.cpp -----------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file is a part of ThreadSanitizer/AddressSanitizer runtime.
|
|
// Tests for interception_win.h.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
#include "interception/interception.h"
|
|
|
|
#include "gtest/gtest.h"
|
|
|
|
// Too slow for debug build
|
|
#if !SANITIZER_DEBUG
|
|
#if SANITIZER_WINDOWS
|
|
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#include <windows.h>
|
|
|
|
namespace __interception {
|
|
namespace {
|
|
|
|
enum FunctionPrefixKind {
|
|
FunctionPrefixNone,
|
|
FunctionPrefixPadding,
|
|
FunctionPrefixHotPatch,
|
|
FunctionPrefixDetour,
|
|
};
|
|
|
|
typedef bool (*TestOverrideFunction)(uptr, uptr, uptr*);
|
|
typedef int (*IdentityFunction)(int);
|
|
|
|
#if SANITIZER_WINDOWS64
|
|
|
|
const u8 kIdentityCodeWithPrologue[] = {
|
|
0x55, // push rbp
|
|
0x48, 0x89, 0xE5, // mov rbp,rsp
|
|
0x8B, 0xC1, // mov eax,ecx
|
|
0x5D, // pop rbp
|
|
0xC3, // ret
|
|
};
|
|
|
|
const u8 kIdentityCodeWithPushPop[] = {
|
|
0x55, // push rbp
|
|
0x48, 0x89, 0xE5, // mov rbp,rsp
|
|
0x53, // push rbx
|
|
0x50, // push rax
|
|
0x58, // pop rax
|
|
0x8B, 0xC1, // mov rax,rcx
|
|
0x5B, // pop rbx
|
|
0x5D, // pop rbp
|
|
0xC3, // ret
|
|
};
|
|
|
|
const u8 kIdentityTwiceOffset = 16;
|
|
const u8 kIdentityTwice[] = {
|
|
0x55, // push rbp
|
|
0x48, 0x89, 0xE5, // mov rbp,rsp
|
|
0x8B, 0xC1, // mov eax,ecx
|
|
0x5D, // pop rbp
|
|
0xC3, // ret
|
|
0x90, 0x90, 0x90, 0x90,
|
|
0x90, 0x90, 0x90, 0x90,
|
|
0x55, // push rbp
|
|
0x48, 0x89, 0xE5, // mov rbp,rsp
|
|
0x8B, 0xC1, // mov eax,ecx
|
|
0x5D, // pop rbp
|
|
0xC3, // ret
|
|
};
|
|
|
|
const u8 kIdentityCodeWithMov[] = {
|
|
0x89, 0xC8, // mov eax, ecx
|
|
0xC3, // ret
|
|
};
|
|
|
|
const u8 kIdentityCodeWithJump[] = {
|
|
0xE9, 0x04, 0x00, 0x00,
|
|
0x00, // jmp + 4
|
|
0xCC, 0xCC, 0xCC, 0xCC,
|
|
0x89, 0xC8, // mov eax, ecx
|
|
0xC3, // ret
|
|
};
|
|
|
|
const u8 kIdentityCodeWithJumpBackwards[] = {
|
|
0x89, 0xC8, // mov eax, ecx
|
|
0xC3, // ret
|
|
0xE9, 0xF8, 0xFF, 0xFF,
|
|
0xFF, // jmp - 8
|
|
0xCC, 0xCC, 0xCC, 0xCC,
|
|
};
|
|
const u8 kIdentityCodeWithJumpBackwardsOffset = 3;
|
|
|
|
# else
|
|
|
|
const u8 kIdentityCodeWithPrologue[] = {
|
|
0x55, // push ebp
|
|
0x8B, 0xEC, // mov ebp,esp
|
|
0x8B, 0x45, 0x08, // mov eax,dword ptr [ebp + 8]
|
|
0x5D, // pop ebp
|
|
0xC3, // ret
|
|
};
|
|
|
|
const u8 kIdentityCodeWithPushPop[] = {
|
|
0x55, // push ebp
|
|
0x8B, 0xEC, // mov ebp,esp
|
|
0x53, // push ebx
|
|
0x50, // push eax
|
|
0x58, // pop eax
|
|
0x8B, 0x45, 0x08, // mov eax,dword ptr [ebp + 8]
|
|
0x5B, // pop ebx
|
|
0x5D, // pop ebp
|
|
0xC3, // ret
|
|
};
|
|
|
|
const u8 kIdentityTwiceOffset = 8;
|
|
const u8 kIdentityTwice[] = {
|
|
0x55, // push ebp
|
|
0x8B, 0xEC, // mov ebp,esp
|
|
0x8B, 0x45, 0x08, // mov eax,dword ptr [ebp + 8]
|
|
0x5D, // pop ebp
|
|
0xC3, // ret
|
|
0x55, // push ebp
|
|
0x8B, 0xEC, // mov ebp,esp
|
|
0x8B, 0x45, 0x08, // mov eax,dword ptr [ebp + 8]
|
|
0x5D, // pop ebp
|
|
0xC3, // ret
|
|
};
|
|
|
|
const u8 kIdentityCodeWithMov[] = {
|
|
0x8B, 0x44, 0x24, 0x04, // mov eax,dword ptr [esp + 4]
|
|
0xC3, // ret
|
|
};
|
|
|
|
const u8 kIdentityCodeWithJump[] = {
|
|
0xE9, 0x04, 0x00, 0x00,
|
|
0x00, // jmp + 4
|
|
0xCC, 0xCC, 0xCC, 0xCC,
|
|
0x8B, 0x44, 0x24, 0x04, // mov eax,dword ptr [esp + 4]
|
|
0xC3, // ret
|
|
};
|
|
|
|
const u8 kIdentityCodeWithJumpBackwards[] = {
|
|
0x8B, 0x44, 0x24, 0x04, // mov eax,dword ptr [esp + 4]
|
|
0xC3, // ret
|
|
0xE9, 0xF6, 0xFF, 0xFF,
|
|
0xFF, // jmp - 10
|
|
0xCC, 0xCC, 0xCC, 0xCC,
|
|
};
|
|
const u8 kIdentityCodeWithJumpBackwardsOffset = 5;
|
|
|
|
# endif
|
|
|
|
const u8 kPatchableCode1[] = {
|
|
0xB8, 0x4B, 0x00, 0x00, 0x00, // mov eax,4B
|
|
0x33, 0xC9, // xor ecx,ecx
|
|
0xC3, // ret
|
|
};
|
|
|
|
const u8 kPatchableCode2[] = {
|
|
0x55, // push ebp
|
|
0x8B, 0xEC, // mov ebp,esp
|
|
0x33, 0xC0, // xor eax,eax
|
|
0x5D, // pop ebp
|
|
0xC3, // ret
|
|
};
|
|
|
|
const u8 kPatchableCode3[] = {
|
|
0x55, // push ebp
|
|
0x8B, 0xEC, // mov ebp,esp
|
|
0x6A, 0x00, // push 0
|
|
0xE8, 0x3D, 0xFF, 0xFF, 0xFF, // call <func>
|
|
};
|
|
|
|
const u8 kPatchableCode4[] = {
|
|
0xE9, 0xCC, 0xCC, 0xCC, 0xCC, // jmp <label>
|
|
0x90, 0x90, 0x90, 0x90,
|
|
};
|
|
|
|
const u8 kPatchableCode5[] = {
|
|
0x55, // push ebp
|
|
0x8b, 0xec, // mov ebp,esp
|
|
0x8d, 0xa4, 0x24, 0x30, 0xfd, 0xff, 0xff, // lea esp,[esp-2D0h]
|
|
0x54, // push esp
|
|
};
|
|
|
|
#if SANITIZER_WINDOWS64
|
|
u8 kLoadGlobalCode[] = {
|
|
0x8B, 0x05, 0x00, 0x00, 0x00, 0x00, // mov eax [rip + global]
|
|
0xC3, // ret
|
|
};
|
|
#endif
|
|
|
|
const u8 kUnpatchableCode1[] = {
|
|
0xC3, // ret
|
|
};
|
|
|
|
const u8 kUnpatchableCode2[] = {
|
|
0x33, 0xC9, // xor ecx,ecx
|
|
0xC3, // ret
|
|
};
|
|
|
|
const u8 kUnpatchableCode3[] = {
|
|
0x75, 0xCC, // jne <label>
|
|
0x33, 0xC9, // xor ecx,ecx
|
|
0xC3, // ret
|
|
};
|
|
|
|
const u8 kUnpatchableCode4[] = {
|
|
0x74, 0xCC, // jne <label>
|
|
0x33, 0xC9, // xor ecx,ecx
|
|
0xC3, // ret
|
|
};
|
|
|
|
const u8 kUnpatchableCode5[] = {
|
|
0xEB, 0x02, // jmp <label>
|
|
0x33, 0xC9, // xor ecx,ecx
|
|
0xC3, // ret
|
|
};
|
|
|
|
const u8 kUnpatchableCode6[] = {
|
|
0xE8, 0xCC, 0xCC, 0xCC, 0xCC, // call <func>
|
|
0x90, 0x90, 0x90, 0x90,
|
|
};
|
|
|
|
const u8 kUnpatchableCode7[] = {
|
|
0x33, 0xc0, // xor eax,eax
|
|
0x48, 0x85, 0xd2, // test rdx,rdx
|
|
0x74, 0x10, // je +16 (unpatchable)
|
|
};
|
|
|
|
const u8 kUnpatchableCode8[] = {
|
|
0x48, 0x8b, 0xc1, // mov rax,rcx
|
|
0x0f, 0xb7, 0x10, // movzx edx,word ptr [rax]
|
|
0x48, 0x83, 0xc0, 0x02, // add rax,2
|
|
0x66, 0x85, 0xd2, // test dx,dx
|
|
0x75, 0xf4, // jne -12 (unpatchable)
|
|
};
|
|
|
|
const u8 kUnpatchableCode9[] = {
|
|
0x4c, 0x8b, 0xc1, // mov r8,rcx
|
|
0x8a, 0x01, // mov al,byte ptr [rcx]
|
|
0x48, 0xff, 0xc1, // inc rcx
|
|
0x84, 0xc0, // test al,al
|
|
0x75, 0xf7, // jne -9 (unpatchable)
|
|
};
|
|
|
|
const u8 kPatchableCode6[] = {
|
|
0x48, 0x89, 0x54, 0x24, 0xBB, // mov QWORD PTR [rsp + 0xBB], rdx
|
|
0x33, 0xC9, // xor ecx,ecx
|
|
0xC3, // ret
|
|
};
|
|
|
|
const u8 kPatchableCode7[] = {
|
|
0x4c, 0x89, 0x4c, 0x24, 0xBB, // mov QWORD PTR [rsp + 0xBB], r9
|
|
0x33, 0xC9, // xor ecx,ecx
|
|
0xC3, // ret
|
|
};
|
|
|
|
const u8 kPatchableCode8[] = {
|
|
0x4c, 0x89, 0x44, 0x24, 0xBB, // mov QWORD PTR [rsp + 0xBB], r8
|
|
0x33, 0xC9, // xor ecx,ecx
|
|
0xC3, // ret
|
|
};
|
|
|
|
const u8 kPatchableCode9[] = {
|
|
0x8a, 0x01, // al,byte ptr [rcx]
|
|
0x45, 0x33, 0xc0, // xor r8d,r8d
|
|
0x84, 0xc0, // test al,al
|
|
};
|
|
|
|
const u8 kPatchableCode10[] = {
|
|
0x45, 0x33, 0xc0, // xor r8d,r8d
|
|
0x41, 0x8b, 0xc0, // mov eax,r8d
|
|
0x48, 0x85, 0xd2, // test rdx,rdx
|
|
};
|
|
|
|
const u8 kPatchableCode11[] = {
|
|
0x48, 0x83, 0xec, 0x38, // sub rsp,38h
|
|
0x83, 0x64, 0x24, 0x28, 0x00, // and dword ptr [rsp+28h],0
|
|
};
|
|
|
|
// A buffer holding the dynamically generated code under test.
|
|
u8* ActiveCode;
|
|
const size_t ActiveCodeLength = 4096;
|
|
|
|
int InterceptorFunction(int x);
|
|
|
|
/// Allocate code memory more than 2GB away from Base.
|
|
u8 *AllocateCode2GBAway(u8 *Base) {
|
|
// Find a 64K aligned location after Base plus 2GB.
|
|
size_t TwoGB = 0x80000000;
|
|
size_t AllocGranularity = 0x10000;
|
|
Base = (u8 *)((((uptr)Base + TwoGB + AllocGranularity)) & ~(AllocGranularity - 1));
|
|
|
|
// Check if that location is free, and if not, loop over regions until we find
|
|
// one that is.
|
|
MEMORY_BASIC_INFORMATION mbi = {};
|
|
while (sizeof(mbi) == VirtualQuery(Base, &mbi, sizeof(mbi))) {
|
|
if (mbi.State & MEM_FREE) break;
|
|
Base += mbi.RegionSize;
|
|
}
|
|
|
|
// Allocate one RWX page at the free location.
|
|
return (u8 *)::VirtualAlloc(Base, ActiveCodeLength, MEM_COMMIT | MEM_RESERVE,
|
|
PAGE_EXECUTE_READWRITE);
|
|
}
|
|
|
|
template<class T>
|
|
static void LoadActiveCode(
|
|
const T &code,
|
|
uptr *entry_point,
|
|
FunctionPrefixKind prefix_kind = FunctionPrefixNone) {
|
|
if (ActiveCode == nullptr) {
|
|
ActiveCode = AllocateCode2GBAway((u8*)&InterceptorFunction);
|
|
ASSERT_NE(ActiveCode, nullptr) << "failed to allocate RWX memory 2GB away";
|
|
}
|
|
|
|
size_t position = 0;
|
|
|
|
// Add padding to avoid memory violation when scanning the prefix.
|
|
for (int i = 0; i < 16; ++i)
|
|
ActiveCode[position++] = 0xC3; // Instruction 'ret'.
|
|
|
|
// Add function padding.
|
|
size_t padding = 0;
|
|
if (prefix_kind == FunctionPrefixPadding)
|
|
padding = 16;
|
|
else if (prefix_kind == FunctionPrefixDetour ||
|
|
prefix_kind == FunctionPrefixHotPatch)
|
|
padding = FIRST_32_SECOND_64(5, 6);
|
|
// Insert |padding| instructions 'nop'.
|
|
for (size_t i = 0; i < padding; ++i)
|
|
ActiveCode[position++] = 0x90;
|
|
|
|
// Keep track of the entry point.
|
|
*entry_point = (uptr)&ActiveCode[position];
|
|
|
|
// Add the detour instruction (i.e. mov edi, edi)
|
|
if (prefix_kind == FunctionPrefixDetour) {
|
|
#if SANITIZER_WINDOWS64
|
|
// Note that "mov edi,edi" is NOP in 32-bit only, in 64-bit it clears
|
|
// higher bits of RDI.
|
|
// Use 66,90H as NOP for Windows64.
|
|
ActiveCode[position++] = 0x66;
|
|
ActiveCode[position++] = 0x90;
|
|
#else
|
|
// mov edi,edi.
|
|
ActiveCode[position++] = 0x8B;
|
|
ActiveCode[position++] = 0xFF;
|
|
#endif
|
|
|
|
}
|
|
|
|
// Copy the function body.
|
|
for (size_t i = 0; i < sizeof(T); ++i)
|
|
ActiveCode[position++] = code[i];
|
|
}
|
|
|
|
int InterceptorFunctionCalled;
|
|
IdentityFunction InterceptedRealFunction;
|
|
|
|
int InterceptorFunction(int x) {
|
|
++InterceptorFunctionCalled;
|
|
return InterceptedRealFunction(x);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
// Tests for interception_win.h
|
|
TEST(Interception, InternalGetProcAddress) {
|
|
HMODULE ntdll_handle = ::GetModuleHandle("ntdll");
|
|
ASSERT_NE(nullptr, ntdll_handle);
|
|
uptr DbgPrint_expected = (uptr)::GetProcAddress(ntdll_handle, "DbgPrint");
|
|
uptr isdigit_expected = (uptr)::GetProcAddress(ntdll_handle, "isdigit");
|
|
uptr DbgPrint_adddress = InternalGetProcAddress(ntdll_handle, "DbgPrint");
|
|
uptr isdigit_address = InternalGetProcAddress(ntdll_handle, "isdigit");
|
|
|
|
EXPECT_EQ(DbgPrint_expected, DbgPrint_adddress);
|
|
EXPECT_EQ(isdigit_expected, isdigit_address);
|
|
EXPECT_NE(DbgPrint_adddress, isdigit_address);
|
|
}
|
|
|
|
template <class T>
|
|
static void TestIdentityFunctionPatching(
|
|
const T &code, TestOverrideFunction override,
|
|
FunctionPrefixKind prefix_kind = FunctionPrefixNone,
|
|
int function_start_offset = 0) {
|
|
uptr identity_address;
|
|
LoadActiveCode(code, &identity_address, prefix_kind);
|
|
identity_address += function_start_offset;
|
|
IdentityFunction identity = (IdentityFunction)identity_address;
|
|
|
|
// Validate behavior before dynamic patching.
|
|
InterceptorFunctionCalled = 0;
|
|
EXPECT_EQ(0, identity(0));
|
|
EXPECT_EQ(42, identity(42));
|
|
EXPECT_EQ(0, InterceptorFunctionCalled);
|
|
|
|
// Patch the function.
|
|
uptr real_identity_address = 0;
|
|
bool success = override(identity_address,
|
|
(uptr)&InterceptorFunction,
|
|
&real_identity_address);
|
|
EXPECT_TRUE(success);
|
|
EXPECT_NE(0U, real_identity_address);
|
|
IdentityFunction real_identity = (IdentityFunction)real_identity_address;
|
|
InterceptedRealFunction = real_identity;
|
|
|
|
// Don't run tests if hooking failed or the real function is not valid.
|
|
if (!success || !real_identity_address)
|
|
return;
|
|
|
|
// Calling the redirected function.
|
|
InterceptorFunctionCalled = 0;
|
|
EXPECT_EQ(0, identity(0));
|
|
EXPECT_EQ(42, identity(42));
|
|
EXPECT_EQ(2, InterceptorFunctionCalled);
|
|
|
|
// Calling the real function.
|
|
InterceptorFunctionCalled = 0;
|
|
EXPECT_EQ(0, real_identity(0));
|
|
EXPECT_EQ(42, real_identity(42));
|
|
EXPECT_EQ(0, InterceptorFunctionCalled);
|
|
|
|
TestOnlyReleaseTrampolineRegions();
|
|
}
|
|
|
|
# if !SANITIZER_WINDOWS64
|
|
TEST(Interception, OverrideFunctionWithDetour) {
|
|
TestOverrideFunction override = OverrideFunctionWithDetour;
|
|
FunctionPrefixKind prefix = FunctionPrefixDetour;
|
|
TestIdentityFunctionPatching(kIdentityCodeWithPrologue, override, prefix);
|
|
TestIdentityFunctionPatching(kIdentityCodeWithPushPop, override, prefix);
|
|
TestIdentityFunctionPatching(kIdentityCodeWithMov, override, prefix);
|
|
TestIdentityFunctionPatching(kIdentityCodeWithJump, override, prefix);
|
|
}
|
|
#endif // !SANITIZER_WINDOWS64
|
|
|
|
TEST(Interception, OverrideFunctionWithRedirectJump) {
|
|
TestOverrideFunction override = OverrideFunctionWithRedirectJump;
|
|
TestIdentityFunctionPatching(kIdentityCodeWithJump, override);
|
|
TestIdentityFunctionPatching(kIdentityCodeWithJumpBackwards, override,
|
|
FunctionPrefixNone,
|
|
kIdentityCodeWithJumpBackwardsOffset);
|
|
}
|
|
|
|
TEST(Interception, OverrideFunctionWithHotPatch) {
|
|
TestOverrideFunction override = OverrideFunctionWithHotPatch;
|
|
FunctionPrefixKind prefix = FunctionPrefixHotPatch;
|
|
TestIdentityFunctionPatching(kIdentityCodeWithMov, override, prefix);
|
|
}
|
|
|
|
TEST(Interception, OverrideFunctionWithTrampoline) {
|
|
TestOverrideFunction override = OverrideFunctionWithTrampoline;
|
|
FunctionPrefixKind prefix = FunctionPrefixNone;
|
|
TestIdentityFunctionPatching(kIdentityCodeWithPrologue, override, prefix);
|
|
TestIdentityFunctionPatching(kIdentityCodeWithPushPop, override, prefix);
|
|
|
|
prefix = FunctionPrefixPadding;
|
|
TestIdentityFunctionPatching(kIdentityCodeWithPrologue, override, prefix);
|
|
TestIdentityFunctionPatching(kIdentityCodeWithPushPop, override, prefix);
|
|
}
|
|
|
|
TEST(Interception, OverrideFunction) {
|
|
TestOverrideFunction override = OverrideFunction;
|
|
FunctionPrefixKind prefix = FunctionPrefixNone;
|
|
TestIdentityFunctionPatching(kIdentityCodeWithPrologue, override, prefix);
|
|
TestIdentityFunctionPatching(kIdentityCodeWithPushPop, override, prefix);
|
|
TestIdentityFunctionPatching(kIdentityCodeWithJump, override, prefix);
|
|
|
|
prefix = FunctionPrefixPadding;
|
|
TestIdentityFunctionPatching(kIdentityCodeWithPrologue, override, prefix);
|
|
TestIdentityFunctionPatching(kIdentityCodeWithPushPop, override, prefix);
|
|
TestIdentityFunctionPatching(kIdentityCodeWithMov, override, prefix);
|
|
TestIdentityFunctionPatching(kIdentityCodeWithJump, override, prefix);
|
|
|
|
prefix = FunctionPrefixHotPatch;
|
|
TestIdentityFunctionPatching(kIdentityCodeWithPrologue, override, prefix);
|
|
TestIdentityFunctionPatching(kIdentityCodeWithPushPop, override, prefix);
|
|
TestIdentityFunctionPatching(kIdentityCodeWithMov, override, prefix);
|
|
TestIdentityFunctionPatching(kIdentityCodeWithJump, override, prefix);
|
|
|
|
prefix = FunctionPrefixDetour;
|
|
TestIdentityFunctionPatching(kIdentityCodeWithPrologue, override, prefix);
|
|
TestIdentityFunctionPatching(kIdentityCodeWithPushPop, override, prefix);
|
|
TestIdentityFunctionPatching(kIdentityCodeWithMov, override, prefix);
|
|
TestIdentityFunctionPatching(kIdentityCodeWithJump, override, prefix);
|
|
}
|
|
|
|
template<class T>
|
|
static void TestIdentityFunctionMultiplePatching(
|
|
const T &code,
|
|
TestOverrideFunction override,
|
|
FunctionPrefixKind prefix_kind = FunctionPrefixNone) {
|
|
uptr identity_address;
|
|
LoadActiveCode(code, &identity_address, prefix_kind);
|
|
|
|
// Patch the function.
|
|
uptr real_identity_address = 0;
|
|
bool success = override(identity_address,
|
|
(uptr)&InterceptorFunction,
|
|
&real_identity_address);
|
|
EXPECT_TRUE(success);
|
|
EXPECT_NE(0U, real_identity_address);
|
|
|
|
// Re-patching the function should not work.
|
|
success = override(identity_address,
|
|
(uptr)&InterceptorFunction,
|
|
&real_identity_address);
|
|
EXPECT_FALSE(success);
|
|
|
|
TestOnlyReleaseTrampolineRegions();
|
|
}
|
|
|
|
TEST(Interception, OverrideFunctionMultiplePatchingIsFailing) {
|
|
#if !SANITIZER_WINDOWS64
|
|
TestIdentityFunctionMultiplePatching(kIdentityCodeWithPrologue,
|
|
OverrideFunctionWithDetour,
|
|
FunctionPrefixDetour);
|
|
#endif
|
|
|
|
TestIdentityFunctionMultiplePatching(kIdentityCodeWithMov,
|
|
OverrideFunctionWithHotPatch,
|
|
FunctionPrefixHotPatch);
|
|
|
|
TestIdentityFunctionMultiplePatching(kIdentityCodeWithPushPop,
|
|
OverrideFunctionWithTrampoline,
|
|
FunctionPrefixPadding);
|
|
}
|
|
|
|
TEST(Interception, OverrideFunctionTwice) {
|
|
uptr identity_address1;
|
|
LoadActiveCode(kIdentityTwice, &identity_address1);
|
|
uptr identity_address2 = identity_address1 + kIdentityTwiceOffset;
|
|
IdentityFunction identity1 = (IdentityFunction)identity_address1;
|
|
IdentityFunction identity2 = (IdentityFunction)identity_address2;
|
|
|
|
// Patch the two functions.
|
|
uptr real_identity_address = 0;
|
|
EXPECT_TRUE(OverrideFunction(identity_address1,
|
|
(uptr)&InterceptorFunction,
|
|
&real_identity_address));
|
|
EXPECT_TRUE(OverrideFunction(identity_address2,
|
|
(uptr)&InterceptorFunction,
|
|
&real_identity_address));
|
|
IdentityFunction real_identity = (IdentityFunction)real_identity_address;
|
|
InterceptedRealFunction = real_identity;
|
|
|
|
// Calling the redirected function.
|
|
InterceptorFunctionCalled = 0;
|
|
EXPECT_EQ(42, identity1(42));
|
|
EXPECT_EQ(42, identity2(42));
|
|
EXPECT_EQ(2, InterceptorFunctionCalled);
|
|
|
|
TestOnlyReleaseTrampolineRegions();
|
|
}
|
|
|
|
template<class T>
|
|
static bool TestFunctionPatching(
|
|
const T &code,
|
|
TestOverrideFunction override,
|
|
FunctionPrefixKind prefix_kind = FunctionPrefixNone) {
|
|
uptr address;
|
|
LoadActiveCode(code, &address, prefix_kind);
|
|
uptr unused_real_address = 0;
|
|
bool result = override(
|
|
address, (uptr)&InterceptorFunction, &unused_real_address);
|
|
|
|
TestOnlyReleaseTrampolineRegions();
|
|
return result;
|
|
}
|
|
|
|
TEST(Interception, PatchableFunction) {
|
|
TestOverrideFunction override = OverrideFunction;
|
|
// Test without function padding.
|
|
EXPECT_TRUE(TestFunctionPatching(kPatchableCode1, override));
|
|
EXPECT_TRUE(TestFunctionPatching(kPatchableCode2, override));
|
|
#if SANITIZER_WINDOWS64
|
|
EXPECT_FALSE(TestFunctionPatching(kPatchableCode3, override));
|
|
#else
|
|
EXPECT_TRUE(TestFunctionPatching(kPatchableCode3, override));
|
|
#endif
|
|
EXPECT_TRUE(TestFunctionPatching(kPatchableCode4, override));
|
|
EXPECT_TRUE(TestFunctionPatching(kPatchableCode5, override));
|
|
#if SANITIZER_WINDOWS64
|
|
EXPECT_TRUE(TestFunctionPatching(kLoadGlobalCode, override));
|
|
#endif
|
|
|
|
EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode1, override));
|
|
EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode2, override));
|
|
EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode3, override));
|
|
EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode4, override));
|
|
EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode5, override));
|
|
EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode6, override));
|
|
}
|
|
|
|
#if !SANITIZER_WINDOWS64
|
|
TEST(Interception, PatchableFunctionWithDetour) {
|
|
TestOverrideFunction override = OverrideFunctionWithDetour;
|
|
// Without the prefix, no function can be detoured.
|
|
EXPECT_FALSE(TestFunctionPatching(kPatchableCode1, override));
|
|
EXPECT_FALSE(TestFunctionPatching(kPatchableCode2, override));
|
|
EXPECT_FALSE(TestFunctionPatching(kPatchableCode3, override));
|
|
EXPECT_FALSE(TestFunctionPatching(kPatchableCode4, override));
|
|
EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode1, override));
|
|
EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode2, override));
|
|
EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode3, override));
|
|
EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode4, override));
|
|
EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode5, override));
|
|
EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode6, override));
|
|
|
|
// With the prefix, all functions can be detoured.
|
|
FunctionPrefixKind prefix = FunctionPrefixDetour;
|
|
EXPECT_TRUE(TestFunctionPatching(kPatchableCode1, override, prefix));
|
|
EXPECT_TRUE(TestFunctionPatching(kPatchableCode2, override, prefix));
|
|
EXPECT_TRUE(TestFunctionPatching(kPatchableCode3, override, prefix));
|
|
EXPECT_TRUE(TestFunctionPatching(kPatchableCode4, override, prefix));
|
|
EXPECT_TRUE(TestFunctionPatching(kUnpatchableCode1, override, prefix));
|
|
EXPECT_TRUE(TestFunctionPatching(kUnpatchableCode2, override, prefix));
|
|
EXPECT_TRUE(TestFunctionPatching(kUnpatchableCode3, override, prefix));
|
|
EXPECT_TRUE(TestFunctionPatching(kUnpatchableCode4, override, prefix));
|
|
EXPECT_TRUE(TestFunctionPatching(kUnpatchableCode5, override, prefix));
|
|
EXPECT_TRUE(TestFunctionPatching(kUnpatchableCode6, override, prefix));
|
|
}
|
|
#endif // !SANITIZER_WINDOWS64
|
|
|
|
TEST(Interception, PatchableFunctionWithRedirectJump) {
|
|
TestOverrideFunction override = OverrideFunctionWithRedirectJump;
|
|
EXPECT_FALSE(TestFunctionPatching(kPatchableCode1, override));
|
|
EXPECT_FALSE(TestFunctionPatching(kPatchableCode2, override));
|
|
EXPECT_FALSE(TestFunctionPatching(kPatchableCode3, override));
|
|
EXPECT_TRUE(TestFunctionPatching(kPatchableCode4, override));
|
|
EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode1, override));
|
|
EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode2, override));
|
|
EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode3, override));
|
|
EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode4, override));
|
|
EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode5, override));
|
|
EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode6, override));
|
|
}
|
|
|
|
TEST(Interception, PatchableFunctionWithHotPatch) {
|
|
TestOverrideFunction override = OverrideFunctionWithHotPatch;
|
|
FunctionPrefixKind prefix = FunctionPrefixHotPatch;
|
|
|
|
EXPECT_TRUE(TestFunctionPatching(kPatchableCode1, override, prefix));
|
|
EXPECT_FALSE(TestFunctionPatching(kPatchableCode2, override, prefix));
|
|
EXPECT_FALSE(TestFunctionPatching(kPatchableCode3, override, prefix));
|
|
EXPECT_FALSE(TestFunctionPatching(kPatchableCode4, override, prefix));
|
|
#if SANITIZER_WINDOWS64
|
|
EXPECT_TRUE(TestFunctionPatching(kPatchableCode6, override, prefix));
|
|
EXPECT_TRUE(TestFunctionPatching(kPatchableCode7, override, prefix));
|
|
EXPECT_TRUE(TestFunctionPatching(kPatchableCode8, override, prefix));
|
|
#endif
|
|
EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode1, override, prefix));
|
|
EXPECT_TRUE(TestFunctionPatching(kUnpatchableCode2, override, prefix));
|
|
EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode3, override, prefix));
|
|
EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode4, override, prefix));
|
|
EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode5, override, prefix));
|
|
EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode6, override, prefix));
|
|
}
|
|
|
|
TEST(Interception, PatchableFunctionWithTrampoline) {
|
|
TestOverrideFunction override = OverrideFunctionWithTrampoline;
|
|
FunctionPrefixKind prefix = FunctionPrefixPadding;
|
|
|
|
EXPECT_TRUE(TestFunctionPatching(kPatchableCode1, override, prefix));
|
|
EXPECT_TRUE(TestFunctionPatching(kPatchableCode2, override, prefix));
|
|
#if SANITIZER_WINDOWS64
|
|
EXPECT_FALSE(TestFunctionPatching(kPatchableCode3, override, prefix));
|
|
EXPECT_TRUE(TestFunctionPatching(kPatchableCode9, override, prefix));
|
|
EXPECT_TRUE(TestFunctionPatching(kPatchableCode10, override, prefix));
|
|
EXPECT_TRUE(TestFunctionPatching(kPatchableCode11, override, prefix));
|
|
EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode7, override, prefix));
|
|
EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode8, override, prefix));
|
|
EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode9, override, prefix));
|
|
#else
|
|
EXPECT_TRUE(TestFunctionPatching(kPatchableCode3, override, prefix));
|
|
#endif
|
|
EXPECT_FALSE(TestFunctionPatching(kPatchableCode4, override, prefix));
|
|
|
|
EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode1, override, prefix));
|
|
EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode2, override, prefix));
|
|
EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode3, override, prefix));
|
|
EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode4, override, prefix));
|
|
EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode5, override, prefix));
|
|
EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode6, override, prefix));
|
|
}
|
|
|
|
TEST(Interception, PatchableFunctionPadding) {
|
|
TestOverrideFunction override = OverrideFunction;
|
|
FunctionPrefixKind prefix = FunctionPrefixPadding;
|
|
|
|
EXPECT_TRUE(TestFunctionPatching(kPatchableCode1, override, prefix));
|
|
EXPECT_TRUE(TestFunctionPatching(kPatchableCode2, override, prefix));
|
|
#if SANITIZER_WINDOWS64
|
|
EXPECT_FALSE(TestFunctionPatching(kPatchableCode3, override, prefix));
|
|
#else
|
|
EXPECT_TRUE(TestFunctionPatching(kPatchableCode3, override, prefix));
|
|
#endif
|
|
EXPECT_TRUE(TestFunctionPatching(kPatchableCode4, override, prefix));
|
|
|
|
EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode1, override, prefix));
|
|
EXPECT_TRUE(TestFunctionPatching(kUnpatchableCode2, override, prefix));
|
|
EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode3, override, prefix));
|
|
EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode4, override, prefix));
|
|
EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode5, override, prefix));
|
|
EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode6, override, prefix));
|
|
}
|
|
|
|
TEST(Interception, EmptyExportTable) {
|
|
// We try to get a pointer to a function from an executable that doesn't
|
|
// export any symbol (empty export table).
|
|
uptr FunPtr = InternalGetProcAddress((void *)GetModuleHandleA(0), "example");
|
|
EXPECT_EQ(0U, FunPtr);
|
|
}
|
|
|
|
} // namespace __interception
|
|
|
|
#endif // SANITIZER_WINDOWS
|
|
#endif // #if !SANITIZER_DEBUG
|