blob: 7d5f7c922b0dddb6460ae61f27c5941bcff18660 [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 <windows.h>
#include "gtest/gtest.h"
#include "syzygy/agent/asan/asan_heap_checker.h"
#include "syzygy/agent/asan/asan_rtl_impl.h"
#include "syzygy/agent/asan/asan_runtime.h"
#include "syzygy/agent/asan/unittest_util.h"
namespace agent {
namespace asan {
namespace {
using testing::AsanBlockInfoVector;
using testing::MemoryAccessorTester;
using testing::ScopedASanAlloc;
// An arbitrary size for the buffer we allocate in the different unittests.
const size_t kAllocSize = 13;
class AsanRtlTest : public testing::TestAsanRtl {
public:
AsanRtlTest() : memory_src_(NULL), memory_dst_(NULL), memory_length_(0),
memory_size_(0) { }
void SetUp() OVERRIDE {
testing::TestAsanRtl::SetUp();
// Setup the callback to detect invalid accesses.
SetCallBackFunction(&MemoryAccessorTester::AsanErrorCallback);
}
protected:
void AllocMemoryBuffers(int32 length, int32 element_size);
void FreeMemoryBuffers();
// Memory buffers used to test special instructions.
void* memory_src_;
void* memory_dst_;
int32 memory_length_;
int32 memory_size_;
};
void AsanRtlTest::AllocMemoryBuffers(int32 length, int32 element_size) {
ASSERT_EQ(reinterpret_cast<void*>(NULL), memory_src_);
ASSERT_EQ(reinterpret_cast<void*>(NULL), memory_dst_);
ASSERT_EQ(0, memory_length_);
ASSERT_EQ(0, memory_size_);
// Keep track of memory size.
memory_length_ = length;
memory_size_ = length * element_size;
// Allocate memory space.
memory_src_ = HeapAllocFunction(heap_, 0, memory_size_);
ASSERT_TRUE(memory_src_ != NULL);
memory_dst_ = HeapAllocFunction(heap_, 0, memory_size_);
ASSERT_TRUE(memory_dst_ != NULL);
// Initialize memory.
::memset(memory_src_, 0, memory_size_);
::memset(memory_dst_, 0, memory_size_);
}
void AsanRtlTest::FreeMemoryBuffers() {
ASSERT_NE(reinterpret_cast<void*>(NULL), memory_src_);
ASSERT_NE(reinterpret_cast<void*>(NULL), memory_dst_);
ASSERT_TRUE(HeapFreeFunction(heap_, 0, memory_src_));
ASSERT_TRUE(HeapFreeFunction(heap_, 0, memory_dst_));
memory_length_ = 0;
memory_size_ = 0;
memory_src_ = NULL;
memory_dst_ = NULL;
}
} // namespace
TEST_F(AsanRtlTest, GetProcessHeap) {
HANDLE asan_heap_handle = GetProcessHeapFunction();
EXPECT_NE(INVALID_HANDLE_VALUE, asan_heap_handle);
HeapProxy* proxy = HeapProxy::FromHandle(asan_heap_handle);
EXPECT_NE(reinterpret_cast<HeapProxy*>(NULL), proxy);
EXPECT_FALSE(proxy->owns_heap());
EXPECT_EQ(::GetProcessHeap(), proxy->heap());
}
TEST_F(AsanRtlTest, AsanCheckGoodAccess) {
FARPROC check_access_fn =
::GetProcAddress(asan_rtl_, "asan_check_4_byte_read_access");
ASSERT_TRUE(check_access_fn != NULL);
// Run through access checking an allocation that's larger than our
// block size (8), but not a multiple thereof to exercise all paths
// in the access check function (save for the failure path).
ScopedASanAlloc<uint8> mem(this, kAllocSize);
ASSERT_TRUE(mem.get() != NULL);
MemoryAccessorTester tester;
for (size_t i = 0; i < kAllocSize; ++i) {
ASSERT_NO_FATAL_FAILURE(
tester.CheckAccessAndCompareContexts(check_access_fn, mem.get() + i));
}
}
TEST_F(AsanRtlTest, AsanCheckHeapBufferOverflow) {
FARPROC check_access_fn =
::GetProcAddress(asan_rtl_, "asan_check_4_byte_read_access");
ASSERT_TRUE(check_access_fn != NULL);
ScopedASanAlloc<uint8> mem(this, kAllocSize);
ASSERT_TRUE(mem.get() != NULL);
MemoryAccessorTester tester;
tester.AssertMemoryErrorIsDetected(
check_access_fn, mem.get() + kAllocSize, HEAP_BUFFER_OVERFLOW);
EXPECT_TRUE(LogContains("previously allocated here"));
EXPECT_TRUE(LogContains(kHeapBufferOverFlow));
}
TEST_F(AsanRtlTest, AsanCheckHeapBufferUnderflow) {
FARPROC check_access_fn =
::GetProcAddress(asan_rtl_, "asan_check_4_byte_read_access");
ASSERT_TRUE(check_access_fn != NULL);
ScopedASanAlloc<uint8> mem(this, kAllocSize);
ASSERT_TRUE(mem.get() != NULL);
MemoryAccessorTester tester;
tester.AssertMemoryErrorIsDetected(
check_access_fn, mem.get() - 1, HEAP_BUFFER_UNDERFLOW);
EXPECT_TRUE(LogContains("previously allocated here"));
EXPECT_TRUE(LogContains(kHeapBufferUnderFlow));
}
TEST_F(AsanRtlTest, AsanCheckUseAfterFree) {
FARPROC check_access_fn =
::GetProcAddress(asan_rtl_, "asan_check_4_byte_read_access");
ASSERT_TRUE(check_access_fn != NULL);
ScopedASanAlloc<uint8> mem(this, kAllocSize);
ASSERT_TRUE(mem.get() != NULL);
uint8* mem_ptr = mem.get();
mem.reset(NULL);
MemoryAccessorTester tester;
tester.AssertMemoryErrorIsDetected(check_access_fn, mem_ptr, USE_AFTER_FREE);
EXPECT_TRUE(LogContains("previously allocated here"));
EXPECT_TRUE(LogContains("freed here"));
EXPECT_TRUE(LogContains(kHeapUseAfterFree));
}
TEST_F(AsanRtlTest, AsanCheckDoubleFree) {
FARPROC check_access_fn =
::GetProcAddress(asan_rtl_, "asan_check_4_byte_read_access");
ASSERT_TRUE(check_access_fn != NULL);
uint8* mem_ptr = NULL;
{
ScopedASanAlloc<uint8> mem(this, kAllocSize);
ASSERT_TRUE(mem.get() != NULL);
mem_ptr = mem.get();
}
MemoryAccessorTester tester;
tester.set_expected_error_type(DOUBLE_FREE);
EXPECT_FALSE(HeapFreeFunction(heap_, 0, mem_ptr));
EXPECT_TRUE(tester.memory_error_detected());
EXPECT_TRUE(LogContains(kAttemptingDoubleFree));
EXPECT_TRUE(LogContains("previously allocated here"));
EXPECT_TRUE(LogContains("freed here"));
}
TEST_F(AsanRtlTest, AsanCheckWildAccess) {
FARPROC check_access_fn =
::GetProcAddress(asan_rtl_, "asan_check_4_byte_read_access");
ASSERT_TRUE(check_access_fn != NULL);
MemoryAccessorTester tester;
tester.AssertMemoryErrorIsDetected(
check_access_fn, reinterpret_cast<void*>(0x80000000), WILD_ACCESS);
EXPECT_TRUE(LogContains(kWildAccess));
}
TEST_F(AsanRtlTest, AsanCheckInvalidAccess) {
FARPROC check_access_fn =
::GetProcAddress(asan_rtl_, "asan_check_4_byte_read_access");
ASSERT_TRUE(check_access_fn != NULL);
MemoryAccessorTester tester;
tester.AssertMemoryErrorIsDetected(
check_access_fn, reinterpret_cast<void*>(0x00000000), INVALID_ADDRESS);
EXPECT_TRUE(LogContains(kInvalidAddress));
}
TEST_F(AsanRtlTest, AsanCheckCorruptBlock) {
void* mem = HeapAllocFunction(heap_, 0, kAllocSize);
reinterpret_cast<uint8*>(mem)[-1]--;
MemoryAccessorTester tester;
tester.set_expected_error_type(CORRUPT_BLOCK);
EXPECT_TRUE(HeapFreeFunction(heap_, 0, mem));
EXPECT_TRUE(tester.memory_error_detected());
EXPECT_TRUE(LogContains(kHeapCorruptBlock));
EXPECT_TRUE(LogContains("previously allocated here"));
}
TEST_F(AsanRtlTest, AsanCheckCorruptHeap) {
FARPROC check_access_fn =
::GetProcAddress(asan_rtl_, "asan_check_4_byte_read_access");
ASSERT_TRUE(check_access_fn != NULL);
agent::asan::AsanRuntime* runtime = GetActiveRuntimeFunction();
ASSERT_NE(reinterpret_cast<agent::asan::AsanRuntime*>(NULL), runtime);
runtime->params().check_heap_on_failure = true;
ScopedASanAlloc<uint8> mem(this, kAllocSize);
ASSERT_TRUE(mem.get() != NULL);
const size_t kMaxIterations = 10;
// Retrieves the information about this block.
BlockHeader* header = BlockGetHeaderFromBody(mem.get());
BlockInfo block_info = {};
EXPECT_TRUE(BlockInfoFromMemory(header, &block_info));
// We'll update a non essential value of the block trailer to corrupt it.
uint8* mem_in_trailer = reinterpret_cast<uint8*>(
&block_info.trailer->alloc_tid);
// This can fail because of a checksum collision. However, we run it a handful
// of times to keep the chances as small as possible.
for (size_t i = 0; i < kMaxIterations; ++i) {
(*mem_in_trailer)++;
MemoryAccessorTester tester;
tester.AssertMemoryErrorIsDetected(
check_access_fn, mem.get() + kAllocSize, HEAP_BUFFER_OVERFLOW);
EXPECT_TRUE(LogContains("previously allocated here"));
EXPECT_TRUE(LogContains(kHeapBufferOverFlow));
if (!tester.last_error_info().heap_is_corrupt &&
i + 1 < kMaxIterations)
continue;
EXPECT_TRUE(tester.last_error_info().heap_is_corrupt);
EXPECT_EQ(1, tester.last_error_info().corrupt_range_count);
EXPECT_EQ(1, tester.last_corrupt_ranges().size());
const AsanCorruptBlockRange* corrupt_range =
&tester.last_corrupt_ranges()[0].first;
const AsanBlockInfoVector* blocks_info =
&tester.last_corrupt_ranges()[0].second;
EXPECT_EQ(1, blocks_info->size());
EXPECT_TRUE((*blocks_info)[0]->corrupt);
EXPECT_EQ(kAllocSize, (*blocks_info)[0]->user_size);
EXPECT_EQ(block_info.header, (*blocks_info)[0]->header);
EXPECT_NE(0U, (*blocks_info)[0]->alloc_stack_size);
for (size_t j = 0; j < (*blocks_info)[0]->alloc_stack_size; ++j) {
EXPECT_NE(reinterpret_cast<void*>(NULL),
(*blocks_info)[0]->alloc_stack[j]);
}
EXPECT_EQ(0U, (*blocks_info)[0]->free_stack_size);
// An error should be triggered when we free this block.
tester.set_memory_error_detected(false);
tester.set_expected_error_type(CORRUPT_BLOCK);
mem.reset(NULL);
EXPECT_TRUE(tester.memory_error_detected());
break;
}
}
TEST_F(AsanRtlTest, AsanSingleSpecial1byteInstructionCheckGoodAccess) {
static const char* function_names[] = {
"asan_check_1_byte_movs_access",
"asan_check_1_byte_cmps_access",
"asan_check_1_byte_stos_access"
};
// Allocate memory space.
AllocMemoryBuffers(kAllocSize, sizeof(uint8));
uint8* src = reinterpret_cast<uint8*>(memory_src_);
uint8* dst = reinterpret_cast<uint8*>(memory_dst_);
// Validate memory accesses.
for (int32 function = 0; function < arraysize(function_names); ++function) {
FARPROC check_access_fn =
::GetProcAddress(asan_rtl_, function_names[function]);
ASSERT_TRUE(check_access_fn != NULL);
for (int32 i = 0; i < memory_length_; ++i) {
MemoryAccessorTester tester;
tester.ExpectSpecialMemoryErrorIsDetected(
check_access_fn, MemoryAccessorTester::DIRECTION_FORWARD,
false, &dst[i], &src[i], 0xDEADDEAD,
UNKNOWN_BAD_ACCESS);
}
}
FreeMemoryBuffers();
}
TEST_F(AsanRtlTest, AsanSingleSpecial2byteInstructionCheckGoodAccess) {
static const char* function_names[] = {
"asan_check_2_byte_movs_access",
"asan_check_2_byte_cmps_access",
"asan_check_2_byte_stos_access"
};
// Allocate memory space.
AllocMemoryBuffers(kAllocSize, sizeof(uint16));
uint16* src = reinterpret_cast<uint16*>(memory_src_);
uint16* dst = reinterpret_cast<uint16*>(memory_dst_);
// Validate memory accesses.
for (int32 function = 0; function < arraysize(function_names); ++function) {
FARPROC check_access_fn =
::GetProcAddress(asan_rtl_, function_names[function]);
ASSERT_TRUE(check_access_fn != NULL);
for (int32 i = 0; i < memory_length_; ++i) {
MemoryAccessorTester tester;
tester.ExpectSpecialMemoryErrorIsDetected(
check_access_fn, MemoryAccessorTester::DIRECTION_FORWARD,
false, &dst[i], &src[i], 0xDEADDEAD, UNKNOWN_BAD_ACCESS);
}
}
FreeMemoryBuffers();
}
TEST_F(AsanRtlTest, AsanSingleSpecial4byteInstructionCheckGoodAccess) {
static const char* function_names[] = {
"asan_check_4_byte_movs_access",
"asan_check_4_byte_cmps_access",
"asan_check_4_byte_stos_access"
};
// Allocate memory space.
AllocMemoryBuffers(kAllocSize, sizeof(uint32));
uint32* src = reinterpret_cast<uint32*>(memory_src_);
uint32* dst = reinterpret_cast<uint32*>(memory_dst_);
// Validate memory accesses.
for (int32 function = 0; function < arraysize(function_names); ++function) {
FARPROC check_access_fn =
::GetProcAddress(asan_rtl_, function_names[function]);
ASSERT_TRUE(check_access_fn != NULL);
for (int32 i = 0; i < memory_length_; ++i) {
MemoryAccessorTester tester;
tester.ExpectSpecialMemoryErrorIsDetected(
check_access_fn, MemoryAccessorTester::DIRECTION_FORWARD,
false, &dst[i], &src[i], 0xDEADDEAD, UNKNOWN_BAD_ACCESS);
}
}
FreeMemoryBuffers();
}
TEST_F(AsanRtlTest, AsanSingleSpecialInstructionCheckBadAccess) {
static const char* function_names[] = {
"asan_check_1_byte_movs_access",
"asan_check_1_byte_cmps_access",
"asan_check_2_byte_movs_access",
"asan_check_2_byte_cmps_access",
"asan_check_4_byte_movs_access",
"asan_check_4_byte_cmps_access"
};
// Allocate memory space.
AllocMemoryBuffers(kAllocSize, sizeof(uint32));
uint32* src = reinterpret_cast<uint32*>(memory_src_);
uint32* dst = reinterpret_cast<uint32*>(memory_dst_);
// Validate memory accesses.
for (int32 function = 0; function < arraysize(function_names); ++function) {
FARPROC check_access_fn =
::GetProcAddress(asan_rtl_, function_names[function]);
ASSERT_TRUE(check_access_fn != NULL);
MemoryAccessorTester tester;
tester.ExpectSpecialMemoryErrorIsDetected(
check_access_fn, MemoryAccessorTester::DIRECTION_FORWARD,
true, &dst[0], &src[-1], 0xDEADDEAD, HEAP_BUFFER_UNDERFLOW);
tester.ExpectSpecialMemoryErrorIsDetected(
check_access_fn, MemoryAccessorTester::DIRECTION_FORWARD,
true, &dst[-1], &src[0], 0xDEADDEAD, HEAP_BUFFER_UNDERFLOW);
tester.ExpectSpecialMemoryErrorIsDetected(
check_access_fn, MemoryAccessorTester::DIRECTION_FORWARD,
true, &dst[0], &src[memory_length_], 0xDEADDEAD, HEAP_BUFFER_OVERFLOW);
tester.ExpectSpecialMemoryErrorIsDetected(
check_access_fn, MemoryAccessorTester::DIRECTION_FORWARD,
true, &dst[memory_length_], &src[0], 0xDEADDEAD, HEAP_BUFFER_OVERFLOW);
}
FreeMemoryBuffers();
}
TEST_F(AsanRtlTest, AsanSingleStoInstructionCheckBadAccess) {
static const char* function_names[] = {
"asan_check_1_byte_stos_access",
"asan_check_2_byte_stos_access",
"asan_check_4_byte_stos_access"
};
// Allocate memory space.
AllocMemoryBuffers(kAllocSize, sizeof(uint32));
uint32* src = reinterpret_cast<uint32*>(memory_src_);
uint32* dst = reinterpret_cast<uint32*>(memory_dst_);
// Validate memory accesses.
for (int32 function = 0; function < arraysize(function_names); ++function) {
FARPROC check_access_fn =
::GetProcAddress(asan_rtl_, function_names[function]);
ASSERT_TRUE(check_access_fn != NULL);
MemoryAccessorTester tester;
tester.ExpectSpecialMemoryErrorIsDetected(
check_access_fn, MemoryAccessorTester::DIRECTION_FORWARD,
false, &dst[0], &src[-1], 0xDEAD, HEAP_BUFFER_UNDERFLOW);
tester.ExpectSpecialMemoryErrorIsDetected(
check_access_fn, MemoryAccessorTester::DIRECTION_FORWARD,
true, &dst[-1], &src[0], 0xDEAD, HEAP_BUFFER_UNDERFLOW);
tester.ExpectSpecialMemoryErrorIsDetected(
check_access_fn, MemoryAccessorTester::DIRECTION_FORWARD,
false, &dst[0], &src[memory_length_], 0xDEADDEAD, HEAP_BUFFER_OVERFLOW);
tester.ExpectSpecialMemoryErrorIsDetected(
check_access_fn, MemoryAccessorTester::DIRECTION_FORWARD,
true, &dst[memory_length_], &src[0], 0xDEADDEAD, HEAP_BUFFER_OVERFLOW);
}
FreeMemoryBuffers();
}
TEST_F(AsanRtlTest, AsanPrefixedSpecialInstructionCheckGoodAccess) {
static const char* function_names[] = {
"asan_check_repz_4_byte_movs_access",
"asan_check_repz_4_byte_cmps_access",
"asan_check_repz_4_byte_stos_access"
};
// Allocate memory space.
AllocMemoryBuffers(kAllocSize, sizeof(uint32));
uint32* src = reinterpret_cast<uint32*>(memory_src_);
uint32* dst = reinterpret_cast<uint32*>(memory_dst_);
// Validate memory accesses.
for (int32 function = 0; function < arraysize(function_names); ++function) {
FARPROC check_access_fn =
::GetProcAddress(asan_rtl_, function_names[function]);
ASSERT_TRUE(check_access_fn != NULL);
MemoryAccessorTester tester;
tester.ExpectSpecialMemoryErrorIsDetected(
check_access_fn, MemoryAccessorTester::DIRECTION_FORWARD,
false, &dst[0], &src[0], memory_length_, UNKNOWN_BAD_ACCESS);
}
FreeMemoryBuffers();
}
TEST_F(AsanRtlTest, AsanPrefixedSpecialInstructionCheckBadAccess) {
static const char* function_names[] = {
"asan_check_repz_4_byte_movs_access",
"asan_check_repz_4_byte_cmps_access",
"asan_check_repz_4_byte_stos_access"
};
// Allocate memory space.
AllocMemoryBuffers(kAllocSize, sizeof(uint32));
uint32* src = reinterpret_cast<uint32*>(memory_src_);
uint32* dst = reinterpret_cast<uint32*>(memory_dst_);
// Validate memory accesses.
for (int32 function = 0; function < arraysize(function_names); ++function) {
FARPROC check_access_fn =
::GetProcAddress(asan_rtl_, function_names[function]);
ASSERT_TRUE(check_access_fn != NULL);
MemoryAccessorTester tester;
tester.ExpectSpecialMemoryErrorIsDetected(
check_access_fn, MemoryAccessorTester::DIRECTION_FORWARD,
true, &dst[0], &src[0], memory_length_ + 1, HEAP_BUFFER_OVERFLOW);
tester.ExpectSpecialMemoryErrorIsDetected(
check_access_fn, MemoryAccessorTester::DIRECTION_FORWARD,
true, &dst[-1], &src[-1], memory_length_, HEAP_BUFFER_UNDERFLOW);
tester.ExpectSpecialMemoryErrorIsDetected(
check_access_fn, MemoryAccessorTester::DIRECTION_FORWARD,
true, &dst[-1], &src[0], memory_length_, HEAP_BUFFER_UNDERFLOW);
}
FreeMemoryBuffers();
}
TEST_F(AsanRtlTest, AsanDirectionSpecialInstructionCheckGoodAccess) {
static const char* function_names[] = {
"asan_check_repz_4_byte_movs_access",
"asan_check_repz_4_byte_cmps_access",
"asan_check_repz_4_byte_stos_access"
};
// Allocate memory space.
AllocMemoryBuffers(kAllocSize, sizeof(uint32));
uint32* src = reinterpret_cast<uint32*>(memory_src_);
uint32* dst = reinterpret_cast<uint32*>(memory_dst_);
// Validate memory accesses.
for (int32 function = 0; function < arraysize(function_names); ++function) {
FARPROC check_access_fn =
::GetProcAddress(asan_rtl_, function_names[function]);
ASSERT_TRUE(check_access_fn != NULL);
MemoryAccessorTester tester;
tester.ExpectSpecialMemoryErrorIsDetected(
check_access_fn, MemoryAccessorTester::DIRECTION_BACKWARD,
false, &dst[memory_length_ - 1],
&src[memory_length_ - 1], memory_length_,
UNKNOWN_BAD_ACCESS);
}
FreeMemoryBuffers();
}
TEST_F(AsanRtlTest, AsanSpecialInstructionCheckZeroAccess) {
static const char* function_names[] = {
"asan_check_repz_1_byte_movs_access",
"asan_check_repz_1_byte_cmps_access",
"asan_check_repz_1_byte_stos_access",
"asan_check_repz_2_byte_movs_access",
"asan_check_repz_2_byte_cmps_access",
"asan_check_repz_2_byte_stos_access",
"asan_check_repz_4_byte_movs_access",
"asan_check_repz_4_byte_cmps_access",
"asan_check_repz_4_byte_stos_access"
};
// Allocate memory space.
AllocMemoryBuffers(kAllocSize, sizeof(uint32));
uint32* src = reinterpret_cast<uint32*>(memory_src_);
uint32* dst = reinterpret_cast<uint32*>(memory_dst_);
// Validate memory accesses.
for (int32 function = 0; function < arraysize(function_names); ++function) {
FARPROC check_access_fn =
::GetProcAddress(asan_rtl_, function_names[function]);
ASSERT_TRUE(check_access_fn != NULL);
// A prefixed instruction with a count of zero do not have side effects.
MemoryAccessorTester tester;
tester.ExpectSpecialMemoryErrorIsDetected(
check_access_fn, MemoryAccessorTester::DIRECTION_FORWARD,
false, &dst[-1], &src[-1], 0, UNKNOWN_BAD_ACCESS);
}
FreeMemoryBuffers();
}
TEST_F(AsanRtlTest, AsanSpecialInstructionCheckShortcutAccess) {
static const char* function_names[] = {
"asan_check_repz_1_byte_cmps_access",
"asan_check_repz_2_byte_cmps_access",
"asan_check_repz_4_byte_cmps_access",
};
// Allocate memory space.
AllocMemoryBuffers(kAllocSize, sizeof(uint32));
uint32* src = reinterpret_cast<uint32*>(memory_src_);
uint32* dst = reinterpret_cast<uint32*>(memory_dst_);
src[1] = 0x12345667;
// Validate memory accesses.
for (int32 function = 0; function < arraysize(function_names); ++function) {
FARPROC check_access_fn =
::GetProcAddress(asan_rtl_, function_names[function]);
ASSERT_TRUE(check_access_fn != NULL);
// Compare instruction stop their execution when values differ.
MemoryAccessorTester tester;
tester.ExpectSpecialMemoryErrorIsDetected(
check_access_fn, MemoryAccessorTester::DIRECTION_FORWARD,
false, &dst[0], &src[0], memory_length_ + 1, UNKNOWN_BAD_ACCESS);
}
FreeMemoryBuffers();
}
} // namespace asan
} // namespace agent