blob: 4ac1224732a815f25d69d84d676a8c8bb88db882 [file] [log] [blame]
// Copyright 2012 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "syzygy/agent/asan/asan_rtl_impl.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/logging.h"
#include "base/debug/alias.h"
#include "base/memory/scoped_ptr.h"
#include "syzygy/agent/asan/asan_heap.h"
#include "syzygy/agent/asan/asan_rtl_utils.h"
#include "syzygy/agent/asan/asan_runtime.h"
#include "syzygy/agent/asan/shadow.h"
#include "syzygy/agent/asan/stack_capture.h"
#include "syzygy/agent/common/scoped_last_error_keeper.h"
namespace {
using agent::asan::AsanErrorInfo;
using agent::asan::AsanRuntime;
using agent::asan::HeapProxy;
using agent::asan::Shadow;
using agent::asan::TestStructure;
HANDLE process_heap = NULL;
scoped_ptr<HeapProxy> asan_process_heap;
// The asan runtime manager.
AsanRuntime* asan_runtime = NULL;
} // namespace
namespace agent {
namespace asan {
void SetUpRtl(AsanRuntime* runtime) {
DCHECK(runtime != NULL);
asan_runtime = runtime;
process_heap = ::GetProcessHeap();
asan_process_heap.reset(new HeapProxy());
asan_process_heap->UseHeap(process_heap);
asan_runtime->AddHeap(asan_process_heap.get());
// Set the instance used by the helper functions.
SetAsanRuntimeInstance(runtime);
}
void TearDownRtl() {
DCHECK_NE(reinterpret_cast<HANDLE>(NULL), process_heap);
DCHECK_NE(reinterpret_cast<HeapProxy*>(NULL), asan_process_heap);
if (!asan_process_heap->Destroy()) {
LOG(ERROR) << "Unable to destroy the process heap.";
return;
}
// This needs to happen after the heap is destroyed so that the error handling
// callback is still available to report any errors encountered while cleaning
// up the quarantine.
asan_runtime->RemoveHeap(asan_process_heap.get());
asan_process_heap.reset(NULL);
process_heap = NULL;
}
// Check if the memory location is accessible and report an error on bad memory
// accesses.
// @param location The memory address of the access.
// @param access_mode The mode of the access.
// @param access_size The size of the access.
// @param context The registers context of the access.
void CheckMemoryAccess(void* location,
HeapProxy::AccessMode access_mode,
size_t access_size,
const AsanContext& context) {
if (!Shadow::IsAccessible(location))
ReportBadMemoryAccess(location, access_mode, access_size, context);
}
// Check if the memory accesses done by a string instructions are valid.
// @param dst The destination memory address of the access.
// @param dst_access_mode The destination mode of the access.
// @param src The source memory address of the access.
// @param src_access_mode The source mode of the access.
// @param length The number of memory accesses.
// @param access_size The size of each the access in byte.
// @param increment The increment to move dst/src after each access.
// @param compare Flag to activate shortcut of the execution on difference.
// @param context The registers context of the access.
void CheckStringsMemoryAccesses(
uint8* dst, HeapProxy::AccessMode dst_access_mode,
uint8* src, HeapProxy::AccessMode src_access_mode,
uint32 length, size_t access_size, int32 increment, bool compare,
const AsanContext& context) {
int32 offset = 0;
for (uint32 i = 0; i < length; ++i) {
// Check next memory location at src[offset].
if (src_access_mode != HeapProxy::ASAN_UNKNOWN_ACCESS)
CheckMemoryAccess(&src[offset], src_access_mode, access_size, context);
// Check next memory location at dst[offset].
if (dst_access_mode != HeapProxy::ASAN_UNKNOWN_ACCESS)
CheckMemoryAccess(&dst[offset], dst_access_mode, access_size, context);
// For CMPS instructions, we shortcut the execution of prefix REPZ when
// memory contents differ.
if (compare) {
uint32 src_content = 0;
uint32 dst_content = 0;
switch (access_size) {
case 4:
src_content = *reinterpret_cast<uint32*>(&src[offset]);
dst_content = *reinterpret_cast<uint32*>(&dst[offset]);
break;
case 2:
src_content = *reinterpret_cast<uint16*>(&src[offset]);
dst_content = *reinterpret_cast<uint16*>(&dst[offset]);
break;
case 1:
src_content = *reinterpret_cast<uint8*>(&src[offset]);
dst_content = *reinterpret_cast<uint8*>(&dst[offset]);
break;
default:
NOTREACHED() << "Unexpected access_size.";
break;
}
if (src_content != dst_content)
return;
}
// Increments offset of dst/src to the next memory location.
offset += increment;
}
}
} // namespace asan
} // namespace agent
// This is a trick for efficient saving/restoring part of the flags register.
// see http://blog.freearrow.com/archives/396
// Flags (bits 16-31) probably need a pipeline flush on update (POPFD). Thus,
// using LAHF/SAHF instead gives better performance.
// PUSHFD/POPFD: 23.314684 ticks
// LAHF/SAHF: 8.838665 ticks
// This macro starts by saving EAX onto the stack and then loads the value of
// the flags into it.
#define ASAN_SAVE_EFLAGS \
__asm push eax \
__asm lahf \
__asm seto al
// This macro restores the flags, their previous value is assumed to be in EAX
// and we expect to have the previous value of EAX on the top of the stack.
// AL is set to 1 if the overflow flag was set before the call to our hook, 0
// otherwise. We add 0x7f to it so it'll restore the flag. Then we restore the
// low bytes of the flags and EAX.
#define ASAN_RESTORE_EFLAGS \
__asm add al, 0x7f \
__asm sahf \
__asm pop eax
// This is the common part of the fast path shared between the different
// implementations of the hooks, this does the following:
// - Saves the memory location in EDX for the slow path.
// - Checks if the address we're trying to access is signed, if so this mean
// that this is an access to the upper region of the memory (over the
// 2GB limit) and we should report this as an invalid wild access.
// - Checks for zero shadow for this memory location. We use the cmp
// instruction so it'll set the sign flag if the upper bit of the shadow
// value of this memory location is set to 1.
// - If the shadow byte is not equal to zero then it jumps to the slow path.
// - Otherwise it removes the memory location from the top of the stack.
#define ASAN_FAST_PATH \
__asm push edx \
__asm sar edx, 3 \
__asm js report_failure \
__asm movzx edx, BYTE PTR[edx + Shadow::shadow_] \
__asm cmp dl, 0 \
__asm jnz check_access_slow \
__asm add esp, 4
// This is the common part of the slow path shared between the different
// implementations of the hooks. The memory location is expected to be on top of
// the stack and the shadow value for it is assumed to be in DL at this point.
// This also relies on the fact that the shadow non accessible byte mask has its
// upper bit set to 1 and that we jump to this macro after doing a
// "cmp shadow_byte, 0", so the sign flag would be set to 1 if the value isn't
// accessible.
// We inline the Shadow::IsAccessible function for performance reasons.
// This function does the following:
// - Checks if this byte is accessible and jump to the error path if it's
// not.
// - Removes the memory location from the top of the stack.
#define ASAN_SLOW_PATH \
__asm js report_failure \
__asm mov dh, BYTE PTR[esp] \
__asm and dh, 7 \
__asm cmp dh, dl \
__asm jae report_failure \
__asm add esp, 4
// This is the error path. It expects to have the previous value of EDX at
// [ESP + 4] and the address of the faulty instruction at [ESP].
// This macro take cares of saving and restoring the flags.
#define ASAN_ERROR_PATH(access_size, access_mode_value) \
/* Restore original value of EDX, and put memory location on stack. */ \
__asm xchg edx, DWORD PTR[esp + 4] \
/* Create an ASAN registers context on the stack. */ \
__asm pushfd \
__asm pushad \
/* Fix the original value of ESP in the ASAN registers context. */ \
/* Removing 12 bytes (e.g. EFLAGS / EIP / Original EDX). */ \
__asm add DWORD PTR[esp + 12], 12 \
/* Push ARG4: the address of ASAN context on stack. */ \
__asm push esp \
/* Push ARG3: the access size. */ \
__asm push access_size \
/* Push ARG2: the access type. */ \
__asm push access_mode_value \
/* Push ARG1: the memory location. */ \
__asm push DWORD PTR[esp + 52] \
__asm call agent::asan::ReportBadMemoryAccess \
/* Remove 4 x ARG on stack. */ \
__asm add esp, 16 \
/* Restore original registers. */ \
__asm popad \
__asm popfd \
/* Return and remove memory location on stack. */ \
__asm ret 4
// Generates the asan check access functions. The name of the generated method
// will be asan_check_(@p access_size)_byte_(@p access_mode_str)().
// @param access_size The size of the access (in byte).
// @param access_mode_str The string representing the access mode (read_access
// or write_access).
// @param access_mode_value The internal value representing this kind of access.
// @note Calling this function doesn't alter any register.
#define ASAN_CHECK_FUNCTION(access_size, access_mode_str, access_mode_value) \
extern "C" __declspec(naked) \
void asan_check_ ## access_size ## _byte_ ## access_mode_str ## () { \
__asm { \
/* Save the EFLAGS. */ \
ASAN_SAVE_EFLAGS \
ASAN_FAST_PATH \
/* Restore original EDX. */ \
__asm mov edx, DWORD PTR[esp + 8] \
/* Restore the EFLAGS. */ \
ASAN_RESTORE_EFLAGS \
__asm ret 4 \
__asm check_access_slow: \
ASAN_SLOW_PATH \
/* Restore original EDX. */ \
__asm mov edx, DWORD PTR[esp + 8] \
/* Restore the EFLAGS. */ \
ASAN_RESTORE_EFLAGS \
__asm ret 4 \
__asm report_failure: \
/* Restore memory location in EDX. */ \
__asm pop edx \
/* Restore the EFLAGS. */ \
ASAN_RESTORE_EFLAGS \
ASAN_ERROR_PATH(access_size, access_mode_value) \
} \
}
// Generates a variant of the asan check access functions that don't save the
// flags. The name of the generated method will be
// asan_check_(@p access_size)_byte_(@p access_mode_str)_no_flags().
// @param access_size The size of the access (in byte).
// @param access_mode_str The string representing the access mode (read_access
// or write_access).
// @param access_mode_value The internal value representing this kind of access.
// @note Calling this function may alter the EFLAGS register only.
#define ASAN_CHECK_FUNCTION_NO_FLAGS(access_size, \
access_mode_str, \
access_mode_value) \
extern "C" __declspec(naked) \
void asan_check_ ## access_size ## _byte_ ## access_mode_str ## \
_no_flags() { \
__asm { \
ASAN_FAST_PATH \
/* Restore original EDX. */ \
__asm mov edx, DWORD PTR[esp + 4] \
__asm ret 4 \
__asm check_access_slow: \
ASAN_SLOW_PATH \
/* Restore original EDX. */ \
__asm mov edx, DWORD PTR[esp + 4] \
__asm ret 4 \
__asm report_failure: \
/* Restore memory location in EDX. */ \
__asm pop edx \
ASAN_ERROR_PATH(access_size, access_mode_value) \
} \
}
// Redefine some enums to make them accessible in the inlined assembly.
// @{
enum AccessMode {
AsanReadAccess = HeapProxy::ASAN_READ_ACCESS,
AsanWriteAccess = HeapProxy::ASAN_WRITE_ACCESS,
AsanUnknownAccess = HeapProxy::ASAN_UNKNOWN_ACCESS,
};
// @}
// The slow path relies on the fact that the shadow memory non accessible byte
// mask has its upper bit set to 1.
COMPILE_ASSERT(
(Shadow::kHeapNonAccessibleByteMask & (1 << 7)) != 0,
asan_shadow_mask_upper_bit_is_0);
ASAN_CHECK_FUNCTION(1, read_access, AsanReadAccess)
ASAN_CHECK_FUNCTION(2, read_access, AsanReadAccess)
ASAN_CHECK_FUNCTION(4, read_access, AsanReadAccess)
ASAN_CHECK_FUNCTION(8, read_access, AsanReadAccess)
ASAN_CHECK_FUNCTION(10, read_access, AsanReadAccess)
ASAN_CHECK_FUNCTION(16, read_access, AsanReadAccess)
ASAN_CHECK_FUNCTION(32, read_access, AsanReadAccess)
ASAN_CHECK_FUNCTION(1, write_access, AsanWriteAccess)
ASAN_CHECK_FUNCTION(2, write_access, AsanWriteAccess)
ASAN_CHECK_FUNCTION(4, write_access, AsanWriteAccess)
ASAN_CHECK_FUNCTION(8, write_access, AsanWriteAccess)
ASAN_CHECK_FUNCTION(10, write_access, AsanWriteAccess)
ASAN_CHECK_FUNCTION(16, write_access, AsanWriteAccess)
ASAN_CHECK_FUNCTION(32, write_access, AsanWriteAccess)
#undef ASAN_CHECK_FUNCTION
ASAN_CHECK_FUNCTION_NO_FLAGS(1, read_access, AsanReadAccess)
ASAN_CHECK_FUNCTION_NO_FLAGS(2, read_access, AsanReadAccess)
ASAN_CHECK_FUNCTION_NO_FLAGS(4, read_access, AsanReadAccess)
ASAN_CHECK_FUNCTION_NO_FLAGS(8, read_access, AsanReadAccess)
ASAN_CHECK_FUNCTION_NO_FLAGS(10, read_access, AsanReadAccess)
ASAN_CHECK_FUNCTION_NO_FLAGS(16, read_access, AsanReadAccess)
ASAN_CHECK_FUNCTION_NO_FLAGS(32, read_access, AsanReadAccess)
ASAN_CHECK_FUNCTION_NO_FLAGS(1, write_access, AsanWriteAccess)
ASAN_CHECK_FUNCTION_NO_FLAGS(2, write_access, AsanWriteAccess)
ASAN_CHECK_FUNCTION_NO_FLAGS(4, write_access, AsanWriteAccess)
ASAN_CHECK_FUNCTION_NO_FLAGS(8, write_access, AsanWriteAccess)
ASAN_CHECK_FUNCTION_NO_FLAGS(10, write_access, AsanWriteAccess)
ASAN_CHECK_FUNCTION_NO_FLAGS(16, write_access, AsanWriteAccess)
ASAN_CHECK_FUNCTION_NO_FLAGS(32, write_access, AsanWriteAccess)
#undef ASAN_CHECK_FUNCTION_NO_FLAGS
#undef ASAN_SAVE_EFLAGS
#undef ASAN_RESTORE_EFLAGS
#undef ASAN_FAST_PATH
#undef ASAN_SLOW_PATH
#undef ASAN_ERROR_PATH
// Generates the asan check access functions for a string instruction.
// The name of the generated method will be
// asan_check_(@p prefix)(@p access_size)_byte_(@p inst)_access().
// @param inst The instruction mnemonic.
// @param prefix The prefix of the instruction (repz or nothing).
// @param counter The number of times the instruction must be executed (ECX). It
// may be a register or a constant.
// @param dst_mode The memory access mode for destination (EDI).
// @param src_mode The memory access mode for destination (ESI).
// @param access_size The size of the access (in byte).
// @param compare A flag to enable shortcut execution by comparing memory
// contents.
#define ASAN_CHECK_STRINGS(func, prefix, counter, dst_mode, src_mode, \
access_size, compare) \
extern "C" __declspec(naked) \
void asan_check ## prefix ## access_size ## _byte_ ## func ## _access() { \
__asm { \
/* Prologue, save context. */ \
__asm pushfd \
__asm pushad \
/* Fix the original value of ESP in the ASAN registers context. */ \
/* Removing 8 bytes (e.g.EFLAGS / EIP was on stack). */ \
__asm add DWORD PTR[esp + 12], 8 \
/* Setup increment in EBX (depends on direction flag in EFLAGS). */ \
__asm mov ebx, access_size \
__asm pushfd \
__asm pop eax \
__asm test eax, 0x400 \
__asm jz skip_neg_direction \
__asm neg ebx \
__asm skip_neg_direction: \
/* By standard calling convention, direction flag must be forward. */ \
__asm cld \
/* Push ARG(context), the ASAN registers context. */ \
__asm push esp \
/* Push ARG(compare), shortcut when memory contents differ. */ \
__asm push compare \
/* Push ARG(increment), increment for EDI/EDI. */ \
__asm push ebx \
/* Push ARG(access_size), the access size. */ \
__asm push access_size \
/* Push ARG(length), the number of memory accesses. */ \
__asm push counter \
/* Push ARG(src_access_mode), source access type. */ \
__asm push src_mode \
/* Push ARG(src), the source pointer. */ \
__asm push esi \
/* Push ARG(dst_access_mode), destination access type. */ \
__asm push dst_mode \
/* Push ARG(dst), the destination pointer. */ \
__asm push edi \
/* Call the generic check strings function. */ \
__asm call agent::asan::CheckStringsMemoryAccesses \
__asm add esp, 36 \
/* Epilogue, restore context. */ \
__asm popad \
__asm popfd \
__asm ret \
} \
}
ASAN_CHECK_STRINGS(cmps, _repz_, ecx, AsanReadAccess, AsanReadAccess, 4, 1)
ASAN_CHECK_STRINGS(cmps, _repz_, ecx, AsanReadAccess, AsanReadAccess, 2, 1)
ASAN_CHECK_STRINGS(cmps, _repz_, ecx, AsanReadAccess, AsanReadAccess, 1, 1)
ASAN_CHECK_STRINGS(cmps, _, 1, AsanReadAccess, AsanReadAccess, 4, 1)
ASAN_CHECK_STRINGS(cmps, _, 1, AsanReadAccess, AsanReadAccess, 2, 1)
ASAN_CHECK_STRINGS(cmps, _, 1, AsanReadAccess, AsanReadAccess, 1, 1)
ASAN_CHECK_STRINGS(movs, _repz_, ecx, AsanWriteAccess, AsanReadAccess, 4, 0)
ASAN_CHECK_STRINGS(movs, _repz_, ecx, AsanWriteAccess, AsanReadAccess, 2, 0)
ASAN_CHECK_STRINGS(movs, _repz_, ecx, AsanWriteAccess, AsanReadAccess, 1, 0)
ASAN_CHECK_STRINGS(movs, _, 1, AsanWriteAccess, AsanReadAccess, 4, 0)
ASAN_CHECK_STRINGS(movs, _, 1, AsanWriteAccess, AsanReadAccess, 2, 0)
ASAN_CHECK_STRINGS(movs, _, 1, AsanWriteAccess, AsanReadAccess, 1, 0)
ASAN_CHECK_STRINGS(stos, _repz_, ecx, AsanWriteAccess, AsanUnknownAccess, 4, 0)
ASAN_CHECK_STRINGS(stos, _repz_, ecx, AsanWriteAccess, AsanUnknownAccess, 2, 0)
ASAN_CHECK_STRINGS(stos, _repz_, ecx, AsanWriteAccess, AsanUnknownAccess, 1, 0)
ASAN_CHECK_STRINGS(stos, _, 1, AsanWriteAccess, AsanUnknownAccess, 4, 0)
ASAN_CHECK_STRINGS(stos, _, 1, AsanWriteAccess, AsanUnknownAccess, 2, 0)
ASAN_CHECK_STRINGS(stos, _, 1, AsanWriteAccess, AsanUnknownAccess, 1, 0)
#undef ASAN_CHECK_STRINGS
extern "C" {
HANDLE WINAPI asan_GetProcessHeap() {
DCHECK_NE(reinterpret_cast<HeapProxy*>(NULL), asan_process_heap.get());
DCHECK_NE(reinterpret_cast<HANDLE>(NULL), asan_process_heap->heap());
DCHECK_EQ(process_heap, asan_process_heap->heap());
return HeapProxy::ToHandle(asan_process_heap.get());
}
HANDLE WINAPI asan_HeapCreate(DWORD options,
SIZE_T initial_size,
SIZE_T maximum_size) {
DCHECK(asan_runtime != NULL);
scoped_ptr<HeapProxy> proxy(new HeapProxy());
if (!proxy->Create(options, initial_size, maximum_size))
return NULL;
asan_runtime->AddHeap(proxy.get());
return HeapProxy::ToHandle(proxy.release());
}
BOOL WINAPI asan_HeapDestroy(HANDLE heap) {
DCHECK(process_heap != NULL);
if (heap == process_heap)
return ::HeapDestroy(heap);
HeapProxy* proxy = HeapProxy::FromHandle(heap);
if (!proxy)
return FALSE;
// Clean up the heap before removing it, so that it remains attached to our
// callback in the event of any heap errors.
bool success = proxy->Destroy();
asan_runtime->RemoveHeap(proxy);
delete proxy;
if (success)
return TRUE;
return FALSE;
}
LPVOID WINAPI asan_HeapAlloc(HANDLE heap,
DWORD flags,
SIZE_T bytes) {
DCHECK(process_heap != NULL);
if (heap == process_heap)
return ::HeapAlloc(heap, flags, bytes);
HeapProxy* proxy = HeapProxy::FromHandle(heap);
if (!proxy)
return NULL;
return proxy->Alloc(flags, bytes);
}
LPVOID WINAPI asan_HeapReAlloc(HANDLE heap,
DWORD flags,
LPVOID mem,
SIZE_T bytes) {
DCHECK(process_heap != NULL);
if (heap == process_heap)
return ::HeapReAlloc(heap, flags, mem, bytes);
HeapProxy* proxy = HeapProxy::FromHandle(heap);
if (!proxy)
return NULL;
return proxy->ReAlloc(flags, mem, bytes);
}
BOOL WINAPI asan_HeapFree(HANDLE heap,
DWORD flags,
LPVOID mem) {
DCHECK(process_heap != NULL);
if (heap == process_heap)
return ::HeapFree(heap, flags, mem);
HeapProxy* proxy = HeapProxy::FromHandle(heap);
if (!proxy)
return FALSE;
if (!proxy->Free(flags, mem))
return false;
return true;
}
SIZE_T WINAPI asan_HeapSize(HANDLE heap,
DWORD flags,
LPCVOID mem) {
DCHECK(process_heap != NULL);
if (heap == process_heap)
return ::HeapSize(heap, flags, mem);
HeapProxy* proxy = HeapProxy::FromHandle(heap);
if (!proxy)
return static_cast<SIZE_T>(-1);
return proxy->Size(flags, mem);
}
BOOL WINAPI asan_HeapValidate(HANDLE heap,
DWORD flags,
LPCVOID mem) {
DCHECK(process_heap != NULL);
if (heap == process_heap)
return ::HeapValidate(heap, flags, mem);
HeapProxy* proxy = HeapProxy::FromHandle(heap);
if (!proxy)
return FALSE;
return proxy->Validate(flags, mem);
}
SIZE_T WINAPI asan_HeapCompact(HANDLE heap,
DWORD flags) {
DCHECK(process_heap != NULL);
if (heap == process_heap)
return ::HeapCompact(heap, flags);
HeapProxy* proxy = HeapProxy::FromHandle(heap);
if (!proxy)
return 0;
return proxy->Compact(flags);
}
BOOL WINAPI asan_HeapLock(HANDLE heap) {
DCHECK(process_heap != NULL);
if (heap == process_heap)
return ::HeapLock(heap);
HeapProxy* proxy = HeapProxy::FromHandle(heap);
if (!proxy)
return FALSE;
return proxy->Lock();
}
BOOL WINAPI asan_HeapUnlock(HANDLE heap) {
DCHECK(process_heap != NULL);
if (heap == process_heap)
return ::HeapUnlock(heap);
HeapProxy* proxy = HeapProxy::FromHandle(heap);
if (!proxy)
return FALSE;
return proxy->Unlock();
}
BOOL WINAPI asan_HeapWalk(HANDLE heap,
LPPROCESS_HEAP_ENTRY entry) {
DCHECK(process_heap != NULL);
if (heap == process_heap)
return ::HeapWalk(heap, entry);
HeapProxy* proxy = HeapProxy::FromHandle(heap);
if (!proxy)
return FALSE;
return proxy->Walk(entry);
}
BOOL WINAPI asan_HeapSetInformation(
HANDLE heap, HEAP_INFORMATION_CLASS info_class,
PVOID info, SIZE_T info_length) {
DCHECK(process_heap != NULL);
if (heap == NULL || heap == process_heap)
return ::HeapSetInformation(heap, info_class, info, info_length);
HeapProxy* proxy = HeapProxy::FromHandle(heap);
if (!proxy)
return FALSE;
return proxy->SetInformation(info_class, info, info_length);
}
BOOL WINAPI asan_HeapQueryInformation(
HANDLE heap, HEAP_INFORMATION_CLASS info_class,
PVOID info, SIZE_T info_length, PSIZE_T return_length) {
DCHECK(process_heap != NULL);
if (heap == NULL || heap == process_heap) {
return ::HeapQueryInformation(heap,
info_class,
info,
info_length,
return_length);
}
HeapProxy* proxy = HeapProxy::FromHandle(heap);
if (!proxy)
return FALSE;
bool ret = proxy->QueryInformation(info_class,
info,
info_length,
return_length);
return ret == true;
}
void WINAPI asan_SetCallBack(AsanErrorCallBack callback) {
DCHECK(asan_runtime != NULL);
asan_runtime->SetErrorCallBack(base::Bind(callback));
}
// Unittesting seam.
AsanRuntime* WINAPI asan_GetActiveRuntime() {
return asan_runtime;
}
} // extern "C"