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 |