preamble_patcher_test.cc 14 KB

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