123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368 |
- // -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
- /* Copyright (c) 2011, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * ---
- * Author: Joi Sigurdsson
- * Author: Scott Francis
- *
- * Unit tests for PreamblePatcher
- */
- #include "config_for_unittests.h"
- #include "preamble_patcher.h"
- #include "mini_disassembler.h"
- #pragma warning(push)
- #pragma warning(disable:4553)
- #include "auto_testing_hook.h"
- #pragma warning(pop)
- #define WIN32_LEAN_AND_MEAN
- #include <windows.h>
- #include <tchar.h>
- // Turning off all optimizations for this file, since the official build's
- // "Whole program optimization" seems to cause the TestPatchUsingDynamicStub
- // test to crash with an access violation. We debugged this and found
- // that the optimized access a register that is changed by a call to the hook
- // function.
- #pragma optimize("", off)
- // A convenience macro to avoid a lot of casting in the tests.
- // I tried to make this a templated function, but windows complained:
- // error C2782: 'sidestep::SideStepError `anonymous-namespace'::Unpatch(T,T,T *)' : template parameter 'T' is ambiguous
- // could be 'int (int)'
- // or 'int (__cdecl *)(int)'
- // My life isn't long enough to try to figure out how to fix this.
- #define UNPATCH(target_function, replacement_function, original_function_stub) \
- sidestep::PreamblePatcher::Unpatch((void*)(target_function), \
- (void*)(replacement_function), \
- (void*)(original_function))
- namespace {
- // Function for testing - this is what we patch
- //
- // NOTE: Because of the way the compiler optimizes this function in
- // release builds, we need to use a different input value every time we
- // call it within a function, otherwise the compiler will just reuse the
- // last calculated incremented value.
- int __declspec(noinline) IncrementNumber(int i) {
- #ifdef _M_X64
- __int64 i2 = i + 1;
- return (int) i2;
- #else
- return i + 1;
- #endif
- }
- extern "C" int TooShortFunction(int);
- extern "C" int JumpShortCondFunction(int);
- extern "C" int JumpNearCondFunction(int);
- extern "C" int JumpAbsoluteFunction(int);
- extern "C" int CallNearRelativeFunction(int);
- typedef int (*IncrementingFunc)(int);
- IncrementingFunc original_function = NULL;
- int HookIncrementNumber(int i) {
- SIDESTEP_ASSERT(original_function != NULL);
- int incremented_once = original_function(i);
- return incremented_once + 1;
- }
- // For the AutoTestingHook test, we can't use original_function, because
- // all that is encapsulated.
- // This function "increments" by 10, just to set it apart from the other
- // functions.
- int __declspec(noinline) AutoHookIncrementNumber(int i) {
- return i + 10;
- }
- }; // namespace
- namespace sidestep {
- bool TestDisassembler() {
- unsigned int instruction_size = 0;
- sidestep::MiniDisassembler disassembler;
- void * target = reinterpret_cast<unsigned char *>(IncrementNumber);
- void * new_target = PreamblePatcher::ResolveTarget(target);
- if (target != new_target)
- target = new_target;
- while (1) {
- sidestep::InstructionType instructionType = disassembler.Disassemble(
- reinterpret_cast<unsigned char *>(target) + instruction_size,
- instruction_size);
- if (sidestep::IT_RETURN == instructionType) {
- return true;
- }
- }
- }
- bool TestPatchWithLongJump() {
- original_function = NULL;
- void *p = ::VirtualAlloc(reinterpret_cast<void *>(0x0000020000000000), 4096,
- MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
- SIDESTEP_EXPECT_TRUE(p != NULL);
- memset(p, 0xcc, 4096);
- SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS ==
- sidestep::PreamblePatcher::Patch(IncrementNumber,
- (IncrementingFunc) p,
- &original_function));
- SIDESTEP_ASSERT((*original_function)(1) == 2);
- SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS ==
- UNPATCH(IncrementNumber,
- (IncrementingFunc)p,
- original_function));
- ::VirtualFree(p, 0, MEM_RELEASE);
- return true;
- }
- bool TestPatchWithPreambleShortCondJump() {
- original_function = NULL;
- SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS ==
- sidestep::PreamblePatcher::Patch(JumpShortCondFunction,
- HookIncrementNumber,
- &original_function));
- (*original_function)(1);
- SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS ==
- UNPATCH(JumpShortCondFunction,
- (void*)HookIncrementNumber,
- original_function));
- return true;
- }
- bool TestPatchWithPreambleNearRelativeCondJump() {
- original_function = NULL;
- SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS ==
- sidestep::PreamblePatcher::Patch(JumpNearCondFunction,
- HookIncrementNumber,
- &original_function));
- (*original_function)(0);
- (*original_function)(1);
- SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS ==
- UNPATCH(JumpNearCondFunction,
- HookIncrementNumber,
- original_function));
- return true;
- }
- bool TestPatchWithPreambleAbsoluteJump() {
- original_function = NULL;
- SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS ==
- sidestep::PreamblePatcher::Patch(JumpAbsoluteFunction,
- HookIncrementNumber,
- &original_function));
- (*original_function)(0);
- (*original_function)(1);
- SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS ==
- UNPATCH(JumpAbsoluteFunction,
- HookIncrementNumber,
- original_function));
- return true;
- }
- bool TestPatchWithPreambleNearRelativeCall() {
- original_function = NULL;
- SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS ==
- sidestep::PreamblePatcher::Patch(
- CallNearRelativeFunction,
- HookIncrementNumber,
- &original_function));
- (*original_function)(0);
- (*original_function)(1);
- SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS ==
- UNPATCH(CallNearRelativeFunction,
- HookIncrementNumber,
- original_function));
- return true;
- }
- bool TestPatchUsingDynamicStub() {
- original_function = NULL;
- SIDESTEP_EXPECT_TRUE(IncrementNumber(1) == 2);
- SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS ==
- sidestep::PreamblePatcher::Patch(IncrementNumber,
- HookIncrementNumber,
- &original_function));
- SIDESTEP_EXPECT_TRUE(original_function);
- SIDESTEP_EXPECT_TRUE(IncrementNumber(2) == 4);
- SIDESTEP_EXPECT_TRUE(original_function(3) == 4);
- // Clearbox test to see that the function has been patched.
- sidestep::MiniDisassembler disassembler;
- unsigned int instruction_size = 0;
- SIDESTEP_EXPECT_TRUE(sidestep::IT_JUMP == disassembler.Disassemble(
- reinterpret_cast<unsigned char*>(IncrementNumber),
- instruction_size));
- // Since we patched IncrementNumber, its first statement is a
- // jmp to the hook function. So verify that we now can not patch
- // IncrementNumber because it starts with a jump.
- #if 0
- IncrementingFunc dummy = NULL;
- // TODO(joi@chromium.org): restore this test once flag is added to
- // disable JMP following
- SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_JUMP_INSTRUCTION ==
- sidestep::PreamblePatcher::Patch(IncrementNumber,
- HookIncrementNumber,
- &dummy));
- // This test disabled because code in preamble_patcher_with_stub.cc
- // asserts before returning the error code -- so there is no way
- // to get an error code here, in debug build.
- dummy = NULL;
- SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_FUNCTION_TOO_SMALL ==
- sidestep::PreamblePatcher::Patch(TooShortFunction,
- HookIncrementNumber,
- &dummy));
- #endif
- SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS ==
- UNPATCH(IncrementNumber,
- HookIncrementNumber,
- original_function));
- return true;
- }
- bool PatchThenUnpatch() {
- original_function = NULL;
- SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS ==
- sidestep::PreamblePatcher::Patch(IncrementNumber,
- HookIncrementNumber,
- &original_function));
- SIDESTEP_EXPECT_TRUE(original_function);
- SIDESTEP_EXPECT_TRUE(IncrementNumber(1) == 3);
- SIDESTEP_EXPECT_TRUE(original_function(2) == 3);
- SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS ==
- UNPATCH(IncrementNumber,
- HookIncrementNumber,
- original_function));
- original_function = NULL;
- SIDESTEP_EXPECT_TRUE(IncrementNumber(3) == 4);
- return true;
- }
- bool AutoTestingHookTest() {
- SIDESTEP_EXPECT_TRUE(IncrementNumber(1) == 2);
- // Inner scope, so we can test what happens when the AutoTestingHook
- // goes out of scope
- {
- AutoTestingHook hook = MakeTestingHook(IncrementNumber,
- AutoHookIncrementNumber);
- (void) hook;
- SIDESTEP_EXPECT_TRUE(IncrementNumber(2) == 12);
- }
- SIDESTEP_EXPECT_TRUE(IncrementNumber(3) == 4);
- return true;
- }
- bool AutoTestingHookInContainerTest() {
- SIDESTEP_EXPECT_TRUE(IncrementNumber(1) == 2);
- // Inner scope, so we can test what happens when the AutoTestingHook
- // goes out of scope
- {
- AutoTestingHookHolder hook(MakeTestingHookHolder(IncrementNumber,
- AutoHookIncrementNumber));
- (void) hook;
- SIDESTEP_EXPECT_TRUE(IncrementNumber(2) == 12);
- }
- SIDESTEP_EXPECT_TRUE(IncrementNumber(3) == 4);
- return true;
- }
- bool TestPreambleAllocation() {
- __int64 diff = 0;
- void* p1 = reinterpret_cast<void*>(0x110000000);
- void* p2 = reinterpret_cast<void*>(0x810000000);
- unsigned char* b1 = PreamblePatcher::AllocPreambleBlockNear(p1);
- SIDESTEP_EXPECT_TRUE(b1 != NULL);
- diff = reinterpret_cast<__int64>(p1) - reinterpret_cast<__int64>(b1);
- // Ensure blocks are within 2GB
- SIDESTEP_EXPECT_TRUE(diff <= INT_MAX && diff >= INT_MIN);
- unsigned char* b2 = PreamblePatcher::AllocPreambleBlockNear(p2);
- SIDESTEP_EXPECT_TRUE(b2 != NULL);
- diff = reinterpret_cast<__int64>(p2) - reinterpret_cast<__int64>(b2);
- SIDESTEP_EXPECT_TRUE(diff <= INT_MAX && diff >= INT_MIN);
- // Ensure we're reusing free blocks
- unsigned char* b3 = b1;
- unsigned char* b4 = b2;
- PreamblePatcher::FreePreambleBlock(b1);
- PreamblePatcher::FreePreambleBlock(b2);
- b1 = PreamblePatcher::AllocPreambleBlockNear(p1);
- SIDESTEP_EXPECT_TRUE(b1 == b3);
- b2 = PreamblePatcher::AllocPreambleBlockNear(p2);
- SIDESTEP_EXPECT_TRUE(b2 == b4);
- PreamblePatcher::FreePreambleBlock(b1);
- PreamblePatcher::FreePreambleBlock(b2);
- return true;
- }
- bool UnitTests() {
- return TestPatchWithPreambleNearRelativeCall() &&
- TestPatchWithPreambleAbsoluteJump() &&
- TestPatchWithPreambleNearRelativeCondJump() &&
- TestPatchWithPreambleShortCondJump() &&
- TestDisassembler() && TestPatchWithLongJump() &&
- TestPatchUsingDynamicStub() && PatchThenUnpatch() &&
- AutoTestingHookTest() && AutoTestingHookInContainerTest() &&
- TestPreambleAllocation();
- }
- }; // namespace sidestep
- int safe_vsnprintf(char *str, size_t size, const char *format, va_list ap) {
- if (size == 0) // not even room for a \0?
- return -1; // not what C99 says to do, but what windows does
- str[size-1] = '\0';
- return _vsnprintf(str, size-1, format, ap);
- }
- int _tmain(int argc, _TCHAR* argv[])
- {
- bool ret = sidestep::UnitTests();
- printf("%s\n", ret ? "PASS" : "FAIL");
- return ret ? 0 : -1;
- }
- #pragma optimize("", on)
|