1 : // Copyright 2012 Google Inc. All Rights Reserved.
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 :
15 : #include "syzygy/agent/asan/asan_rtl_impl.h"
16 :
17 : #include "base/logging.h"
18 : #include "syzygy/agent/asan/asan_heap.h"
19 : #include "syzygy/agent/asan/asan_runtime.h"
20 : #include "syzygy/agent/asan/asan_shadow.h"
21 :
22 : namespace {
23 :
24 : using agent::asan::AsanRuntime;
25 : using agent::asan::HeapProxy;
26 :
27 : HANDLE process_heap = NULL;
28 :
29 : // The asan runtime manager.
30 : AsanRuntime* asan_runtime = NULL;
31 :
32 : } // namespace
33 :
34 : namespace agent {
35 : namespace asan {
36 :
37 E : void SetUpRtl(AsanRuntime* runtime) {
38 E : DCHECK(runtime != NULL);
39 E : asan_runtime = runtime;
40 E : process_heap = GetProcessHeap();
41 E : }
42 :
43 E : void TearDownRtl() {
44 E : process_heap = NULL;
45 E : }
46 :
47 : } // namespace asan
48 : } // namespace agent
49 :
50 : extern "C" {
51 :
52 : HANDLE WINAPI asan_HeapCreate(DWORD options,
53 : SIZE_T initial_size,
54 E : SIZE_T maximum_size) {
55 E : DCHECK(asan_runtime != NULL);
56 : scoped_ptr<HeapProxy> proxy(new HeapProxy(asan_runtime->stack_cache(),
57 E : asan_runtime->logger()));
58 E : if (!proxy->Create(options, initial_size, maximum_size))
59 E : return NULL;
60 :
61 E : asan_runtime->AddHeap(proxy.get());
62 :
63 E : return HeapProxy::ToHandle(proxy.release());
64 E : }
65 :
66 E : BOOL WINAPI asan_HeapDestroy(HANDLE heap) {
67 E : DCHECK(process_heap != NULL);
68 E : if (heap == process_heap)
69 i : return ::HeapDestroy(heap);
70 :
71 E : HeapProxy* proxy = HeapProxy::FromHandle(heap);
72 E : if (!proxy)
73 i : return FALSE;
74 :
75 E : asan_runtime->RemoveHeap(proxy);
76 :
77 E : if (proxy->Destroy()) {
78 E : delete proxy;
79 E : return TRUE;
80 : }
81 :
82 i : return FALSE;
83 E : }
84 :
85 : LPVOID WINAPI asan_HeapAlloc(HANDLE heap,
86 : DWORD flags,
87 E : SIZE_T bytes) {
88 E : DCHECK(process_heap != NULL);
89 E : if (heap == process_heap)
90 i : return ::HeapAlloc(heap, flags, bytes);
91 :
92 E : HeapProxy* proxy = HeapProxy::FromHandle(heap);
93 E : if (!proxy)
94 i : return NULL;
95 :
96 E : return proxy->Alloc(flags, bytes);
97 E : }
98 :
99 : LPVOID WINAPI asan_HeapReAlloc(HANDLE heap,
100 : DWORD flags,
101 : LPVOID mem,
102 E : SIZE_T bytes) {
103 E : DCHECK(process_heap != NULL);
104 E : if (heap == process_heap)
105 i : return ::HeapReAlloc(heap, flags, mem, bytes);
106 :
107 E : HeapProxy* proxy = HeapProxy::FromHandle(heap);
108 E : if (!proxy)
109 i : return NULL;
110 :
111 E : return proxy->ReAlloc(flags, mem, bytes);
112 E : }
113 :
114 : BOOL WINAPI asan_HeapFree(HANDLE heap,
115 : DWORD flags,
116 E : LPVOID mem) {
117 E : DCHECK(process_heap != NULL);
118 E : if (heap == process_heap)
119 i : return ::HeapFree(heap, flags, mem);
120 :
121 E : HeapProxy* proxy = HeapProxy::FromHandle(heap);
122 E : if (!proxy)
123 i : return FALSE;
124 :
125 E : if (!proxy->Free(flags, mem)) {
126 : CONTEXT context;
127 i : ::RtlCaptureContext(&context);
128 i : asan_runtime->OnError(&context);
129 i : return false;
130 : }
131 :
132 E : return true;
133 E : }
134 :
135 : SIZE_T WINAPI asan_HeapSize(HANDLE heap,
136 : DWORD flags,
137 E : LPCVOID mem) {
138 E : DCHECK(process_heap != NULL);
139 E : if (heap == process_heap)
140 i : return ::HeapSize(heap, flags, mem);
141 :
142 E : HeapProxy* proxy = HeapProxy::FromHandle(heap);
143 E : if (!proxy)
144 i : return -1;
145 :
146 E : return proxy->Size(flags, mem);
147 E : }
148 :
149 : BOOL WINAPI asan_HeapValidate(HANDLE heap,
150 : DWORD flags,
151 E : LPCVOID mem) {
152 E : DCHECK(process_heap != NULL);
153 E : if (heap == process_heap)
154 i : return ::HeapValidate(heap, flags, mem);
155 :
156 E : HeapProxy* proxy = HeapProxy::FromHandle(heap);
157 E : if (!proxy)
158 i : return FALSE;
159 :
160 E : return proxy->Validate(flags, mem);
161 E : }
162 :
163 : SIZE_T WINAPI asan_HeapCompact(HANDLE heap,
164 E : DWORD flags) {
165 E : DCHECK(process_heap != NULL);
166 E : if (heap == process_heap)
167 i : return ::HeapCompact(heap, flags);
168 :
169 E : HeapProxy* proxy = HeapProxy::FromHandle(heap);
170 E : if (!proxy)
171 i : return 0;
172 :
173 E : return proxy->Compact(flags);
174 E : }
175 :
176 E : BOOL WINAPI asan_HeapLock(HANDLE heap) {
177 E : DCHECK(process_heap != NULL);
178 E : if (heap == process_heap)
179 i : return ::HeapLock(heap);
180 :
181 E : HeapProxy* proxy = HeapProxy::FromHandle(heap);
182 E : if (!proxy)
183 i : return FALSE;
184 :
185 E : return proxy->Lock();
186 E : }
187 :
188 E : BOOL WINAPI asan_HeapUnlock(HANDLE heap) {
189 E : DCHECK(process_heap != NULL);
190 E : if (heap == process_heap)
191 i : return ::HeapUnlock(heap);
192 :
193 E : HeapProxy* proxy = HeapProxy::FromHandle(heap);
194 E : if (!proxy)
195 i : return FALSE;
196 :
197 E : return proxy->Unlock();
198 E : }
199 :
200 : BOOL WINAPI asan_HeapWalk(HANDLE heap,
201 E : LPPROCESS_HEAP_ENTRY entry) {
202 E : DCHECK(process_heap != NULL);
203 E : if (heap == process_heap)
204 i : return ::HeapWalk(heap, entry);
205 :
206 E : HeapProxy* proxy = HeapProxy::FromHandle(heap);
207 E : if (!proxy)
208 i : return FALSE;
209 :
210 E : return proxy->Walk(entry);
211 E : }
212 :
213 : BOOL WINAPI asan_HeapSetInformation(
214 : HANDLE heap, HEAP_INFORMATION_CLASS info_class,
215 E : PVOID info, SIZE_T info_length) {
216 E : DCHECK(process_heap != NULL);
217 E : if (heap == NULL || heap == process_heap)
218 E : return ::HeapSetInformation(heap, info_class, info, info_length);
219 :
220 E : HeapProxy* proxy = HeapProxy::FromHandle(heap);
221 E : if (!proxy)
222 i : return FALSE;
223 :
224 E : return proxy->SetInformation(info_class, info, info_length);
225 E : }
226 :
227 : BOOL WINAPI asan_HeapQueryInformation(
228 : HANDLE heap, HEAP_INFORMATION_CLASS info_class,
229 E : PVOID info, SIZE_T info_length, PSIZE_T return_length) {
230 E : DCHECK(process_heap != NULL);
231 E : if (heap == NULL || heap == process_heap) {
232 : return ::HeapQueryInformation(heap,
233 : info_class,
234 : info,
235 : info_length,
236 i : return_length);
237 : }
238 :
239 E : HeapProxy* proxy = HeapProxy::FromHandle(heap);
240 E : if (!proxy)
241 i : return FALSE;
242 :
243 : bool ret = proxy->QueryInformation(info_class,
244 : info,
245 : info_length,
246 E : return_length);
247 E : return ret == true;
248 E : }
249 :
250 E : void WINAPI asan_SetCallBack(void (*callback)(CONTEXT*)) {
251 E : DCHECK(asan_runtime != NULL);
252 E : asan_runtime->SetErrorCallBack(callback);
253 E : }
254 :
255 : } // extern "C"
256 :
257 : namespace agent {
258 : namespace asan {
259 :
260 : // Represent the content of the stack before calling the error function.
261 : // The original_* fields store the value of the registers as they were before
262 : // calling the asan hook, the do_not_use_* values are some stack variables used
263 : // by asan but shouldn't be used to produce the stack trace.
264 : // NOTE: As this structure describes the state of the stack at the time
265 : // ReportBadMemoryAccess is called. It is intimately tied to the implementation
266 : // of the asan_check functions.
267 : #pragma pack(push, 1)
268 : struct AsanContext {
269 : DWORD original_edi;
270 : DWORD original_esi;
271 : DWORD original_ebp;
272 : DWORD do_not_use_esp;
273 : DWORD original_ebx;
274 : DWORD do_not_use_edx;
275 : DWORD original_ecx;
276 : DWORD do_not_use_eax;
277 : DWORD original_eflags;
278 : // This is the location of the bad access for this context.
279 : DWORD do_not_use_flags;
280 : void* location;
281 : DWORD original_eax;
282 : DWORD original_eip;
283 : DWORD original_edx;
284 : };
285 : #pragma pack(pop)
286 :
287 : // Report a bad access to the memory.
288 : // @param access_mode The mode of the access.
289 : // @param access_size The size of the access.
290 : // @param asan_context The context of the access.
291 : void __stdcall ReportBadMemoryAccess(HeapProxy::AccessMode access_mode,
292 : size_t access_size,
293 E : struct AsanContext* asan_context) {
294 : // Capture the context and restore the value of the register as before calling
295 : // the asan hook.
296 :
297 : // Capture the current context.
298 E : CONTEXT context = {};
299 :
300 : // We need to call ::RtlCaptureContext if we want SegSS and SegCS to be
301 : // properly set.
302 E : ::RtlCaptureContext(&context);
303 E : context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
304 :
305 : // Restore the original value of the registers.
306 E : context.Eip = asan_context->original_eip;
307 E : context.Eax = asan_context->original_eax;
308 E : context.Ecx = asan_context->original_ecx;
309 E : context.Edx = asan_context->original_edx;
310 E : context.Ebx = asan_context->original_ebx;
311 E : context.Ebp = asan_context->original_ebp;
312 E : context.Esi = asan_context->original_esi;
313 E : context.Edi = asan_context->original_edi;
314 E : context.EFlags = asan_context->original_eflags;
315 :
316 : // Our AsanContext structure is in fact the whole stack frame for the asan
317 : // hook, to compute the value of esp before the call to the hook we can just
318 : // calculate the top address of this structure.
319 E : context.Esp = reinterpret_cast<DWORD>(asan_context) + sizeof(AsanContext);
320 :
321 E : StackCapture stack;
322 E : stack.InitFromStack();
323 : // We need to compute a relative stack id so that for the same stack trace
324 : // we'll get the same value every time even if the modules are loaded at a
325 : // different base address.
326 E : stack.set_stack_id(stack.ComputeRelativeStackId());
327 :
328 : // Check if we can ignore this error.
329 E : if (asan_runtime->ShouldIgnoreError(stack.stack_id()))
330 i : return;
331 :
332 : asan_runtime->ReportAsanErrorDetails(asan_context->location,
333 : context,
334 : stack,
335 : access_mode,
336 E : access_size);
337 :
338 : // Call the callback to handle this error.
339 E : asan_runtime->OnError(&context);
340 E : }
341 :
342 : } // namespace asan
343 : } // namespace agent
344 :
345 : // Generates the asan check access functions. The name of the generated method
346 : // will be asan_check_(@p access_size)_byte_(@p access_mode_str)().
347 : // @param access_size The size of the access (in byte).
348 : // @param access_mode_str The string representing the access mode (read_access
349 : // or write_access).
350 : // @param access_mode_value The internal value representing this kind of access.
351 : #define ASAN_CHECK_FUNCTION(access_size, access_mode_str, access_mode_value) \
352 : extern "C" __declspec(naked) \
353 : void asan_check_ ## access_size ## _byte_ ## access_mode_str ## () { \
354 : __asm { \
355 : /* Save eax as we'll use it to save the value of the flags. */ \
356 : __asm push eax \
357 : /* Save the low byte of the flags into ah. We use this instruction */ \
358 : /* instead of pushfd/popfd because it's much faster. We also save */ \
359 : /* the overflow flag into al. We can do this because our hooks are */ \
360 : /* simple and don't touch the other flags. */ \
361 : __asm lahf \
362 : __asm seto al \
363 : /* Save edx for the slow path. */ \
364 : __asm push edx \
365 : /* Check for zero shadow - fast case. */ \
366 : __asm shr edx, 3 \
367 : __asm movzx edx, BYTE ptr[edx + agent::asan::Shadow::shadow_] \
368 : __asm test dl, dl \
369 : __asm jnz check_access_slow \
370 : /* Restore original edx. */ \
371 : __asm add esp, 4 \
372 : __asm mov edx, DWORD PTR[esp + 8] \
373 : /* al is set to 1 if the overflow flag was set before the call to */ \
374 : /* our hook, 0 otherwise. We add 0x7f to it so it'll restore the */ \
375 : /* flag. */ \
376 : __asm add al, 0x7f \
377 : /* Restore the low byte of the flags. */ \
378 : __asm sahf \
379 : /* Restore original eax */ \
380 : __asm pop eax \
381 : __asm ret 4 \
382 : __asm check_access_slow: \
383 : /* Save flags on stack (keep in eax in the fastpath) */ \
384 : __asm push eax \
385 : /* Uh-oh - non-zero shadow byte means we go to the slow case. */ \
386 : /* Save ecx, it's caller-save (eax, ecx and edx are caller-save). */ \
387 : __asm push ecx \
388 : /* Push the address to check. */ \
389 : __asm push DWORD ptr[esp + 8] \
390 : __asm call agent::asan::Shadow::IsAccessible \
391 : __asm test al, al \
392 : __asm pop ecx \
393 : /* We've found a bad access, report this failure. */ \
394 : __asm jz report_failure \
395 : /* Same code as in the fast path, we could jump there but it'll add */ \
396 : /* an instruction. */ \
397 : __asm epilogue: \
398 : __asm pop eax \
399 : __asm add esp, 4 \
400 : __asm add al, 0x7f \
401 : __asm sahf \
402 : __asm mov edx, DWORD PTR[esp + 8] \
403 : __asm pop eax \
404 : __asm ret 4 \
405 : __asm report_failure: \
406 : /* As we give the user the ability to change the error handler, we */ \
407 : /* need to save the flags with pushfd because now we can't guarantee */ \
408 : /* that the direction flag won't be touched. */ \
409 : /* We start by restoring the flags. */ \
410 : __asm mov eax, DWORD PTR[esp] \
411 : __asm add al, 0x7f \
412 : __asm sahf \
413 : /* Then we push them. */ \
414 : __asm pushfd \
415 : /* Push all the register to have the full asan context on the stack.*/ \
416 : __asm pushad \
417 : /* Push a pointer to this context. */ \
418 : __asm push esp \
419 : /* Push the access size. */ \
420 : __asm push access_size \
421 : /* Push the access type. */ \
422 : __asm push access_mode_value \
423 : /* Call the error handler. */ \
424 : __asm call agent::asan::ReportBadMemoryAccess \
425 : __asm popad \
426 : __asm popfd \
427 : __asm jmp epilogue \
428 : } \
429 : }
430 :
431 : enum AccessMode {
432 : AsanReadAccess = HeapProxy::ASAN_READ_ACCESS,
433 : AsanWriteAccess = HeapProxy::ASAN_WRITE_ACCESS,
434 : };
435 :
436 i : ASAN_CHECK_FUNCTION(1, read_access, AsanReadAccess)
437 i : ASAN_CHECK_FUNCTION(2, read_access, AsanReadAccess)
438 i : ASAN_CHECK_FUNCTION(4, read_access, AsanReadAccess)
439 i : ASAN_CHECK_FUNCTION(8, read_access, AsanReadAccess)
440 i : ASAN_CHECK_FUNCTION(10, read_access, AsanReadAccess)
441 i : ASAN_CHECK_FUNCTION(16, read_access, AsanReadAccess)
442 i : ASAN_CHECK_FUNCTION(32, read_access, AsanReadAccess)
443 i : ASAN_CHECK_FUNCTION(1, write_access, AsanWriteAccess)
444 i : ASAN_CHECK_FUNCTION(2, write_access, AsanWriteAccess)
445 i : ASAN_CHECK_FUNCTION(4, write_access, AsanWriteAccess)
446 i : ASAN_CHECK_FUNCTION(8, write_access, AsanWriteAccess)
447 i : ASAN_CHECK_FUNCTION(10, write_access, AsanWriteAccess)
448 i : ASAN_CHECK_FUNCTION(16, write_access, AsanWriteAccess)
449 i : ASAN_CHECK_FUNCTION(32, write_access, AsanWriteAccess)
450 :
451 : #undef ASAN_CHECK_FUNCTION
452 :
453 : // Generates the asan check access functions for the string instruction 'cmps'.
454 : // The name of the generated method will be
455 : // asan_check_(@p prefix)(@p access_size)_byte_cmps_access().
456 : // @param prefix The prefix of the instruction (repz or nothing).
457 : // @param counter The number of times the instruction must be executed.
458 : // It may be a register or a constant.
459 : // @param access_size The size of the access (in byte).
460 : // @param inst The instruction used to move esi/edi (note: direction flag).
461 : #define ASAN_CHECK_CMPS_FUNCTION(prefix, counter, access_size, inst) \
462 : extern "C" __declspec(naked) \
463 : void asan_check ## prefix ## access_size ## _byte_cmps_access() { \
464 : /* TODO(etienneb) : implement this function. */ \
465 : __asm ret \
466 : }
467 :
468 i : ASAN_CHECK_CMPS_FUNCTION(_repz_, ecx, 4, cmps)
469 i : ASAN_CHECK_CMPS_FUNCTION(_repz_, ecx, 2, cmpsw)
470 i : ASAN_CHECK_CMPS_FUNCTION(_repz_, ecx, 1, cmpsb)
471 i : ASAN_CHECK_CMPS_FUNCTION(_, 1, 4, cmps)
472 i : ASAN_CHECK_CMPS_FUNCTION(_, 1, 2, cmpsw)
473 i : ASAN_CHECK_CMPS_FUNCTION(_, 1, 1, cmpsb)
474 :
475 : #undef ASAN_CHECK_CMPS_FUNCTION
476 :
477 : // Generates the asan check access functions for the string instruction 'movs'.
478 : // The name of the generated method will be
479 : // asan_check_(@p prefix)(@p access_size)_byte_movs_access().
480 : // @param prefix The prefix of the instruction (repz or nothing).
481 : // @param counter The number of times the instruction must be executed.
482 : // It may be a register or a constant.
483 : // @param access_size The size of the access (in byte).
484 : // @param inst The instruction used to move esi/edi (note: direction flag).
485 : #define ASAN_CHECK_MOVS_FUNCTION(prefix, counter, access_size, inst) \
486 : extern "C" __declspec(naked) \
487 : void asan_check ## prefix ## access_size ## _byte_movs_access() { \
488 : /* TODO(etienneb) : implement this function. */ \
489 : __asm ret \
490 : }
491 :
492 i : ASAN_CHECK_MOVS_FUNCTION(_repz_, ecx, 4, cmps)
493 i : ASAN_CHECK_MOVS_FUNCTION(_repz_, ecx, 2, cmpsw)
494 i : ASAN_CHECK_MOVS_FUNCTION(_repz_, ecx, 1, cmpsb)
495 i : ASAN_CHECK_MOVS_FUNCTION(_, 1, 4, cmps);
496 i : ASAN_CHECK_MOVS_FUNCTION(_, 1, 2, cmpsw);
497 i : ASAN_CHECK_MOVS_FUNCTION(_, 1, 1, cmpsb);
498 :
499 : #undef ASAN_CHECK_MOVS_FUNCTION
500 :
501 : // Generates the asan check access functions for the string instruction 'stos'.
502 : // The name of the generated method will be
503 : // asan_check_(@p prefix)(@p access_size)_byte_movs_access().
504 : // @param prefix The prefix of the instruction (repz or nothing).
505 : // @param counter The number of times the instruction must be executed.
506 : // It may be a register or a constant.
507 : // @param access_size The size of the access (in byte).
508 : // @param inst The instruction used to move esi/edi (note: direction flag).
509 : #define ASAN_CHECK_STOS_FUNCTION(prefix, counter, access_size, inst) \
510 : extern "C" __declspec(naked) \
511 : void asan_check ## prefix ## access_size ## _byte_stos_access() { \
512 : /* TODO(etienneb) : implement this function. */ \
513 : __asm ret \
514 : }
515 :
516 i : ASAN_CHECK_STOS_FUNCTION(_repz_, ecx, 4, cmps)
517 i : ASAN_CHECK_STOS_FUNCTION(_repz_, ecx, 2, cmpsw)
518 i : ASAN_CHECK_STOS_FUNCTION(_repz_, ecx, 1, cmpsb)
519 i : ASAN_CHECK_STOS_FUNCTION(_, 1, 4, cmps);
520 i : ASAN_CHECK_STOS_FUNCTION(_, 1, 2, cmpsw);
521 i : ASAN_CHECK_STOS_FUNCTION(_, 1, 1, cmpsb);
522 :
523 : #undef ASAN_CHECK_STOS_FUNCTION
|