Index: third_party/tcmalloc/chromium/src/windows/preamble_patcher_test.cc |
diff --git a/third_party/tcmalloc/chromium/src/windows/preamble_patcher_test.cc b/third_party/tcmalloc/chromium/src/windows/preamble_patcher_test.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..41ab551cc99774ed4c30c4bc722b248367c34548 |
--- /dev/null |
+++ b/third_party/tcmalloc/chromium/src/windows/preamble_patcher_test.cc |
@@ -0,0 +1,367 @@ |
+/* 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) |