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 |