Home | History | Annotate | Download | only in windows
      1 /* Copyright (c) 2011, Google Inc.
      2  * All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Redistributions in binary form must reproduce the above
     11  * copyright notice, this list of conditions and the following disclaimer
     12  * in the documentation and/or other materials provided with the
     13  * distribution.
     14  *     * Neither the name of Google Inc. nor the names of its
     15  * contributors may be used to endorse or promote products derived from
     16  * this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  *
     30  * ---
     31  * Author: Joi Sigurdsson
     32  * Author: Scott Francis
     33  *
     34  * Unit tests for PreamblePatcher
     35  */
     36 
     37 #include "config_for_unittests.h"
     38 #include "preamble_patcher.h"
     39 #include "mini_disassembler.h"
     40 #pragma warning(push)
     41 #pragma warning(disable:4553)
     42 #include "auto_testing_hook.h"
     43 #pragma warning(pop)
     44 
     45 #define WIN32_LEAN_AND_MEAN
     46 #include <windows.h>
     47 #include <tchar.h>
     48 
     49 // Turning off all optimizations for this file, since the official build's
     50 // "Whole program optimization" seems to cause the TestPatchUsingDynamicStub
     51 // test to crash with an access violation.  We debugged this and found
     52 // that the optimized access a register that is changed by a call to the hook
     53 // function.
     54 #pragma optimize("", off)
     55 
     56 // A convenience macro to avoid a lot of casting in the tests.
     57 // I tried to make this a templated function, but windows complained:
     58 //     error C2782: 'sidestep::SideStepError `anonymous-namespace'::Unpatch(T,T,T *)' : template parameter 'T' is ambiguous
     59 //        could be 'int (int)'
     60 //        or       'int (__cdecl *)(int)'
     61 // My life isn't long enough to try to figure out how to fix this.
     62 #define UNPATCH(target_function, replacement_function, original_function_stub) \
     63   sidestep::PreamblePatcher::Unpatch((void*)(target_function),          \
     64                                      (void*)(replacement_function),     \
     65                                      (void*)(original_function))
     66 
     67 namespace {
     68 
     69 // Function for testing - this is what we patch
     70 //
     71 // NOTE:  Because of the way the compiler optimizes this function in
     72 // release builds, we need to use a different input value every time we
     73 // call it within a function, otherwise the compiler will just reuse the
     74 // last calculated incremented value.
     75 int __declspec(noinline) IncrementNumber(int i) {
     76 #ifdef _M_X64
     77   __int64 i2 = i + 1;
     78   return (int) i2;
     79 #else
     80    return i + 1;
     81 #endif
     82 }
     83 
     84 extern "C" int TooShortFunction(int);
     85 
     86 extern "C" int JumpShortCondFunction(int);
     87 
     88 extern "C" int JumpNearCondFunction(int);
     89 
     90 extern "C" int JumpAbsoluteFunction(int);
     91 
     92 extern "C" int CallNearRelativeFunction(int);
     93 
     94 typedef int (*IncrementingFunc)(int);
     95 IncrementingFunc original_function = NULL;
     96 
     97 int HookIncrementNumber(int i) {
     98   SIDESTEP_ASSERT(original_function != NULL);
     99   int incremented_once = original_function(i);
    100   return incremented_once + 1;
    101 }
    102 
    103 // For the AutoTestingHook test, we can't use original_function, because
    104 // all that is encapsulated.
    105 // This function "increments" by 10, just to set it apart from the other
    106 // functions.
    107 int __declspec(noinline) AutoHookIncrementNumber(int i) {
    108   return i + 10;
    109 }
    110 
    111 };  // namespace
    112 
    113 namespace sidestep {
    114 
    115 bool TestDisassembler() {
    116    unsigned int instruction_size = 0;
    117    sidestep::MiniDisassembler disassembler;
    118    void * target = reinterpret_cast<unsigned char *>(IncrementNumber);
    119    void * new_target = PreamblePatcher::ResolveTarget(target);
    120    if (target != new_target)
    121       target = new_target;
    122 
    123    while (1) {
    124       sidestep::InstructionType instructionType = disassembler.Disassemble(
    125          reinterpret_cast<unsigned char *>(target) + instruction_size,
    126          instruction_size);
    127       if (sidestep::IT_RETURN == instructionType) {
    128          return true;
    129       }
    130    }
    131 }
    132 
    133 bool TestPatchWithLongJump() {
    134   original_function = NULL;
    135   void *p = ::VirtualAlloc(reinterpret_cast<void *>(0x0000020000000000), 4096,
    136                            MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    137   SIDESTEP_EXPECT_TRUE(p != NULL);
    138   memset(p, 0xcc, 4096);
    139   SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS ==
    140                        sidestep::PreamblePatcher::Patch(IncrementNumber,
    141                                                         (IncrementingFunc) p,
    142                                                         &original_function));
    143   SIDESTEP_ASSERT((*original_function)(1) == 2);
    144   SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS ==
    145                        UNPATCH(IncrementNumber,
    146                                (IncrementingFunc)p,
    147                                original_function));
    148   ::VirtualFree(p, 0, MEM_RELEASE);
    149   return true;
    150 }
    151 
    152 bool TestPatchWithPreambleShortCondJump() {
    153   original_function = NULL;
    154   SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS ==
    155                        sidestep::PreamblePatcher::Patch(JumpShortCondFunction,
    156                                                         HookIncrementNumber,
    157                                                         &original_function));
    158   (*original_function)(1);
    159   SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS ==
    160                        UNPATCH(JumpShortCondFunction,
    161                                (void*)HookIncrementNumber,
    162                                original_function));
    163   return true;
    164 }
    165 
    166 bool TestPatchWithPreambleNearRelativeCondJump() {
    167   original_function = NULL;
    168   SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS ==
    169                        sidestep::PreamblePatcher::Patch(JumpNearCondFunction,
    170                                                         HookIncrementNumber,
    171                                                         &original_function));
    172   (*original_function)(0);
    173   (*original_function)(1);
    174   SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS ==
    175                        UNPATCH(JumpNearCondFunction,
    176                                HookIncrementNumber,
    177                                original_function));
    178   return true;
    179 }
    180 
    181 bool TestPatchWithPreambleAbsoluteJump() {
    182   original_function = NULL;
    183   SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS ==
    184                        sidestep::PreamblePatcher::Patch(JumpAbsoluteFunction,
    185                                                         HookIncrementNumber,
    186                                                         &original_function));
    187   (*original_function)(0);
    188   (*original_function)(1);
    189   SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS ==
    190                        UNPATCH(JumpAbsoluteFunction,
    191                                HookIncrementNumber,
    192                                original_function));
    193   return true;
    194 }
    195 
    196 bool TestPatchWithPreambleNearRelativeCall() {
    197   original_function = NULL;
    198   SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS ==
    199                        sidestep::PreamblePatcher::Patch(
    200                                                     CallNearRelativeFunction,
    201                                                     HookIncrementNumber,
    202                                                     &original_function));
    203   (*original_function)(0);
    204   (*original_function)(1);
    205   SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS ==
    206                        UNPATCH(CallNearRelativeFunction,
    207                                HookIncrementNumber,
    208                                original_function));
    209   return true;
    210 }
    211 
    212 bool TestPatchUsingDynamicStub() {
    213   original_function = NULL;
    214   SIDESTEP_EXPECT_TRUE(IncrementNumber(1) == 2);
    215   SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS ==
    216                        sidestep::PreamblePatcher::Patch(IncrementNumber,
    217                                                         HookIncrementNumber,
    218                                                         &original_function));
    219   SIDESTEP_EXPECT_TRUE(original_function);
    220   SIDESTEP_EXPECT_TRUE(IncrementNumber(2) == 4);
    221   SIDESTEP_EXPECT_TRUE(original_function(3) == 4);
    222 
    223   // Clearbox test to see that the function has been patched.
    224   sidestep::MiniDisassembler disassembler;
    225   unsigned int instruction_size = 0;
    226   SIDESTEP_EXPECT_TRUE(sidestep::IT_JUMP == disassembler.Disassemble(
    227                            reinterpret_cast<unsigned char*>(IncrementNumber),
    228                            instruction_size));
    229 
    230   // Since we patched IncrementNumber, its first statement is a
    231   // jmp to the hook function.  So verify that we now can not patch
    232   // IncrementNumber because it starts with a jump.
    233 #if 0
    234   IncrementingFunc dummy = NULL;
    235   // TODO(joi (at) chromium.org): restore this test once flag is added to
    236   // disable JMP following
    237   SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_JUMP_INSTRUCTION ==
    238                        sidestep::PreamblePatcher::Patch(IncrementNumber,
    239                                                         HookIncrementNumber,
    240                                                         &dummy));
    241 
    242   // This test disabled because code in preamble_patcher_with_stub.cc
    243   // asserts before returning the error code -- so there is no way
    244   // to get an error code here, in debug build.
    245   dummy = NULL;
    246   SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_FUNCTION_TOO_SMALL ==
    247                        sidestep::PreamblePatcher::Patch(TooShortFunction,
    248                                                         HookIncrementNumber,
    249                                                         &dummy));
    250 #endif
    251 
    252   SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS ==
    253                        UNPATCH(IncrementNumber,
    254                                HookIncrementNumber,
    255                                original_function));
    256   return true;
    257 }
    258 
    259 bool PatchThenUnpatch() {
    260   original_function = NULL;
    261   SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS ==
    262                        sidestep::PreamblePatcher::Patch(IncrementNumber,
    263                                                         HookIncrementNumber,
    264                                                         &original_function));
    265   SIDESTEP_EXPECT_TRUE(original_function);
    266   SIDESTEP_EXPECT_TRUE(IncrementNumber(1) == 3);
    267   SIDESTEP_EXPECT_TRUE(original_function(2) == 3);
    268 
    269   SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS ==
    270                        UNPATCH(IncrementNumber,
    271                                HookIncrementNumber,
    272                                original_function));
    273   original_function = NULL;
    274   SIDESTEP_EXPECT_TRUE(IncrementNumber(3) == 4);
    275 
    276   return true;
    277 }
    278 
    279 bool AutoTestingHookTest() {
    280   SIDESTEP_EXPECT_TRUE(IncrementNumber(1) == 2);
    281 
    282   // Inner scope, so we can test what happens when the AutoTestingHook
    283   // goes out of scope
    284   {
    285     AutoTestingHook hook = MakeTestingHook(IncrementNumber,
    286                                            AutoHookIncrementNumber);
    287     (void) hook;
    288     SIDESTEP_EXPECT_TRUE(IncrementNumber(2) == 12);
    289   }
    290   SIDESTEP_EXPECT_TRUE(IncrementNumber(3) == 4);
    291 
    292   return true;
    293 }
    294 
    295 bool AutoTestingHookInContainerTest() {
    296   SIDESTEP_EXPECT_TRUE(IncrementNumber(1) == 2);
    297 
    298   // Inner scope, so we can test what happens when the AutoTestingHook
    299   // goes out of scope
    300   {
    301     AutoTestingHookHolder hook(MakeTestingHookHolder(IncrementNumber,
    302                                                      AutoHookIncrementNumber));
    303     (void) hook;
    304     SIDESTEP_EXPECT_TRUE(IncrementNumber(2) == 12);
    305   }
    306   SIDESTEP_EXPECT_TRUE(IncrementNumber(3) == 4);
    307 
    308   return true;
    309 }
    310 
    311 bool TestPreambleAllocation() {
    312   __int64 diff = 0;
    313   void* p1 = reinterpret_cast<void*>(0x110000000);
    314   void* p2 = reinterpret_cast<void*>(0x810000000);
    315   unsigned char* b1 = PreamblePatcher::AllocPreambleBlockNear(p1);
    316   SIDESTEP_EXPECT_TRUE(b1 != NULL);
    317   diff = reinterpret_cast<__int64>(p1) - reinterpret_cast<__int64>(b1);
    318   // Ensure blocks are within 2GB
    319   SIDESTEP_EXPECT_TRUE(diff <= INT_MAX && diff >= INT_MIN);
    320   unsigned char* b2 = PreamblePatcher::AllocPreambleBlockNear(p2);
    321   SIDESTEP_EXPECT_TRUE(b2 != NULL);
    322   diff = reinterpret_cast<__int64>(p2) - reinterpret_cast<__int64>(b2);
    323   SIDESTEP_EXPECT_TRUE(diff <= INT_MAX && diff >= INT_MIN);
    324 
    325   // Ensure we're reusing free blocks
    326   unsigned char* b3 = b1;
    327   unsigned char* b4 = b2;
    328   PreamblePatcher::FreePreambleBlock(b1);
    329   PreamblePatcher::FreePreambleBlock(b2);
    330   b1 = PreamblePatcher::AllocPreambleBlockNear(p1);
    331   SIDESTEP_EXPECT_TRUE(b1 == b3);
    332   b2 = PreamblePatcher::AllocPreambleBlockNear(p2);
    333   SIDESTEP_EXPECT_TRUE(b2 == b4);
    334   PreamblePatcher::FreePreambleBlock(b1);
    335   PreamblePatcher::FreePreambleBlock(b2);
    336 
    337   return true;
    338 }
    339 
    340 bool UnitTests() {
    341   return TestPatchWithPreambleNearRelativeCall() &&
    342       TestPatchWithPreambleAbsoluteJump() &&
    343       TestPatchWithPreambleNearRelativeCondJump() &&
    344       TestPatchWithPreambleShortCondJump() &&
    345       TestDisassembler() && TestPatchWithLongJump() &&
    346       TestPatchUsingDynamicStub() && PatchThenUnpatch() &&
    347       AutoTestingHookTest() && AutoTestingHookInContainerTest() &&
    348       TestPreambleAllocation();
    349 }
    350 
    351 };  // namespace sidestep
    352 
    353 int safe_vsnprintf(char *str, size_t size, const char *format, va_list ap) {
    354   if (size == 0)        // not even room for a \0?
    355     return -1;          // not what C99 says to do, but what windows does
    356   str[size-1] = '\0';
    357   return _vsnprintf(str, size-1, format, ap);
    358 }
    359 
    360 int _tmain(int argc, _TCHAR* argv[])
    361 {
    362   bool ret = sidestep::UnitTests();
    363   printf("%s\n", ret ? "PASS" : "FAIL");
    364   return ret ? 0 : -1;
    365 }
    366 
    367 #pragma optimize("", on)
    368