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
67namespace {
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.
75int __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
84extern "C" int TooShortFunction(int);
85
86extern "C" int JumpShortCondFunction(int);
87
88extern "C" int JumpNearCondFunction(int);
89
90extern "C" int JumpAbsoluteFunction(int);
91
92extern "C" int CallNearRelativeFunction(int);
93
94typedef int (*IncrementingFunc)(int);
95IncrementingFunc original_function = NULL;
96
97int 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.
107int __declspec(noinline) AutoHookIncrementNumber(int i) {
108  return i + 10;
109}
110
111};  // namespace
112
113namespace sidestep {
114
115bool 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
133bool 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
152bool 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
166bool 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
181bool 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
196bool 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
212bool 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
259bool 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
279bool 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
295bool 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
311bool 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
340bool 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
353int 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
360int _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