| OLD | NEW |
| (Empty) |
| 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@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) | |
| OLD | NEW |