Add the Clang-Asan probes.
The 2G/4G | 8TB/128TB redirectors are still missing, I'll add them in a further CL.
Review-Url: https://chromiumcodereview.appspot.com/2424073002
diff --git a/syzygy/agent/asan/asan.gyp b/syzygy/agent/asan/asan.gyp
index 85acfc0..4f35d25 100644
--- a/syzygy/agent/asan/asan.gyp
+++ b/syzygy/agent/asan/asan.gyp
@@ -63,6 +63,7 @@
'logger.h',
'memory_interceptors.cc',
'memory_interceptors.h',
+ 'memory_interceptors_impl.h',
'memory_interceptors_patcher.cc',
'memory_interceptors_patcher.h',
'memory_notifier.h',
@@ -128,11 +129,6 @@
'gen/memory_interceptors_impl.asm',
'gen/memory_redirectors.asm',
],
- }, {
- 'sources': [
- 'memory_interceptors_impl_x64.cc',
- 'memory_interceptors_impl_x64.h',
- ]
}],
],
'export_dependent_settings': [
diff --git a/syzygy/agent/asan/gen/system_interceptors.def b/syzygy/agent/asan/gen/system_interceptors.def
index 3267b02..ac33bd7 100644
--- a/syzygy/agent/asan/gen/system_interceptors.def
+++ b/syzygy/agent/asan/gen/system_interceptors.def
@@ -139,6 +139,21 @@
; Exposed to allow the user to enumerate runtime experiments.
asan_EnumExperiments
+ ; Clang-Asan access checking functions.
+ ; TODO(sebmarchand): Use the redirectors once they're available.
+ __asan_load1=asan_load1_2gb
+ __asan_load2=asan_load2_2gb
+ __asan_load4=asan_load4_2gb
+ __asan_load8=asan_load8_2gb
+ __asan_load16=asan_load16_2gb
+ __asan_load32=asan_load32_2gb
+ __asan_store1=asan_store1_2gb
+ __asan_store2=asan_store2_2gb
+ __asan_store4=asan_store4_2gb
+ __asan_store8=asan_store8_2gb
+ __asan_store16=asan_store16_2gb
+ __asan_store32=asan_store32_2gb
+
; Generated system intercepts
asan_ReadFile
asan_ReadFileEx
diff --git a/syzygy/agent/asan/memory_interceptors.cc b/syzygy/agent/asan/memory_interceptors.cc
index 34dcdd4..f26bdbf 100644
--- a/syzygy/agent/asan/memory_interceptors.cc
+++ b/syzygy/agent/asan/memory_interceptors.cc
@@ -17,6 +17,7 @@
#include "base/logging.h"
#include "base/macros.h"
+#include "syzygy/agent/asan/memory_interceptors_impl.h"
#include "syzygy/agent/asan/rtl_utils.h"
#include "syzygy/agent/asan/shadow.h"
@@ -42,8 +43,8 @@
return old_shadow;
}
-const MemoryAccessorVariants kMemoryAccessorVariants[] = {
#ifndef _WIN64
+const MemoryAccessorVariants kMemoryAccessorVariants[] = {
#define ENUM_MEM_INTERCEPT_FUNCTION_VARIANTS(access_size, access_mode_str, \
access_mode_value) \
{ "asan_check_" #access_size "_byte_" #access_mode_str, \
@@ -56,19 +57,11 @@
asan_no_check, \
asan_check_##access_size##_byte_##access_mode_str##_no_flags_2gb, \
asan_check_##access_size##_byte_##access_mode_str##_no_flags_4gb},
-#else
-#define ENUM_MEM_INTERCEPT_FUNCTION_VARIANTS(access_size, access_mode_str, \
- access_mode_value) \
- { "asan_check_" #access_size "_byte_" #access_mode_str}, \
- { "asan_check_" #access_size "_byte_" #access_mode_str "_no_flags", \
- asan_no_check},
-#endif
- ASAN_MEM_INTERCEPT_FUNCTIONS(ENUM_MEM_INTERCEPT_FUNCTION_VARIANTS)
+ ASAN_MEM_INTERCEPT_FUNCTIONS(ENUM_MEM_INTERCEPT_FUNCTION_VARIANTS)
#undef ENUM_MEM_INTERCEPT_FUNCTION_VARIANTS
-#ifndef _WIN64
#define ENUM_STRING_INTERCEPT_FUNCTION_VARIANTS( \
func, prefix, counter, dst_mode, src_mode, access_size, compare) \
{ "asan_check" #prefix #access_size "_byte_" #func "_access", \
@@ -78,13 +71,13 @@
asan_check ## prefix ## access_size ## _byte_ ## func ## _access, \
},
- ASAN_STRING_INTERCEPT_FUNCTIONS(ENUM_STRING_INTERCEPT_FUNCTION_VARIANTS)
+ ASAN_STRING_INTERCEPT_FUNCTIONS(ENUM_STRING_INTERCEPT_FUNCTION_VARIANTS)
#undef ENUM_STRING_INTERCEPT_FUNCTION_VARIANTS
-#endif
};
const size_t kNumMemoryAccessorVariants = arraysize(kMemoryAccessorVariants);
+#endif
void SetRedirectEntryCallback(const RedirectEntryCallback& callback) {
redirect_entry_callback = callback;
@@ -113,6 +106,7 @@
extern "C" {
+#ifndef _WIN64
// 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.
@@ -195,6 +189,7 @@
NOTREACHED();
return NULL;
}
+#endif
// A simple wrapper to agent::asan::ReportBadMemoryAccess that has C linkage
// so it can be referred to in memory_interceptors.asm.
diff --git a/syzygy/agent/asan/memory_interceptors.h b/syzygy/agent/asan/memory_interceptors.h
index 2176d47..7a0bcd4 100644
--- a/syzygy/agent/asan/memory_interceptors.h
+++ b/syzygy/agent/asan/memory_interceptors.h
@@ -56,6 +56,7 @@
// Sets the callback invoked on entry to a redirect stub.
void SetRedirectEntryCallback(const RedirectEntryCallback& callback);
+#ifndef _WIN64
// This type is not accurate, as the memory accessors have a custom calling
// convention, but it's nice to have a type for them.
typedef void (*MemoryAccessorFunction)();
@@ -123,14 +124,36 @@
F(stos, _, 1, AsanWriteAccess, AsanUnknownAccess, 4, 0) \
F(stos, _, 1, AsanWriteAccess, AsanUnknownAccess, 2, 0) \
F(stos, _, 1, AsanWriteAccess, AsanUnknownAccess, 1, 0)
+#endif
+
+// List of the Asan-Clang memory accessor functions.
+#define CLANG_ASAN_MEM_INTERCEPT_FUNCTIONS(F) \
+ F(1, load, AsanReadAccess) \
+ F(2, load, AsanReadAccess) \
+ F(4, load, AsanReadAccess) \
+ F(8, load, AsanReadAccess) \
+ F(10, load, AsanReadAccess) \
+ F(16, load, AsanReadAccess) \
+ F(32, load, AsanReadAccess) \
+ F(1, store, AsanWriteAccess) \
+ F(2, store, AsanWriteAccess) \
+ F(4, store, AsanWriteAccess) \
+ F(8, store, AsanWriteAccess) \
+ F(10, store, AsanWriteAccess) \
+ F(16, store, AsanWriteAccess)
} // namespace asan
} // namespace agent
extern "C" {
+#ifndef _WIN64
// The no-op memory access checker.
void asan_no_check();
+#endif
+
+// The Clang no-op memory access checker.
+void asan_clang_no_check(const void*);
// The no-op string instruction memory access checker.
void asan_string_no_check();
@@ -168,6 +191,17 @@
#undef DECLARE_STRING_INTERCEPT_FUNCTIONS
#endif
+#define DECLARE_MEM_CLANG_INTERCEPT_FUNCTIONS(access_size, access_mode_str, \
+ access_mode_value) \
+ void asan_redirect_##access_mode_str##access_size##(const void*); \
+ void asan_##access_mode_str##access_size##_2gb(const void*); \
+ void asan_##access_mode_str##access_size##_4gb(const void*);
+
+// Declare all the Clang-Asan memory interceptor functions.
+CLANG_ASAN_MEM_INTERCEPT_FUNCTIONS(DECLARE_MEM_CLANG_INTERCEPT_FUNCTIONS)
+
+#undef DECLARE_MEM_CLANG_INTERCEPT_FUNCTIONS
+
} // extern "C"
#endif // SYZYGY_AGENT_ASAN_MEMORY_INTERCEPTORS_H_
diff --git a/syzygy/agent/asan/memory_interceptors_impl.h b/syzygy/agent/asan/memory_interceptors_impl.h
new file mode 100644
index 0000000..cb2b133
--- /dev/null
+++ b/syzygy/agent/asan/memory_interceptors_impl.h
@@ -0,0 +1,99 @@
+// Copyright 2016 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.
+
+#ifndef SYZYGY_AGENT_ASAN_MEMORY_INTERCEPTORS_IMPL_H_
+#define SYZYGY_AGENT_ASAN_MEMORY_INTERCEPTORS_IMPL_H_
+
+// The Clang-asan compatible implementation of the Asan probes.
+
+#include "syzygy/agent/asan/error_info.h"
+#include "syzygy/agent/asan/rtl_utils.h"
+#include "syzygy/agent/asan/runtime.h"
+#include "syzygy/agent/asan/shadow.h"
+
+using agent::asan::AccessMode;
+using agent::asan::AsanContext;
+using agent::asan::AsanRuntime;
+
+// The template function that performs the checks.
+// @tparam access_size Access size in bytes.
+// @tparam address_space_size The virtual address space size limit in bytes.
+// It's 8 TB for Win7 and Win8 and 128 TB for Win8.1+.
+// @tparam access_mode The access mode, which can be any of AccessMode values,
+// allthough this file only exports the probes for read and write accesses.
+// @param addr The address being accessed.
+template <size_t access_size, size_t address_space_size, AccessMode access_mode>
+void asan_check(const void* addr) {
+ if (reinterpret_cast<uintptr_t>(addr) > address_space_size ||
+ !AsanRuntime::runtime()->shadow()->IsAccessible(addr)) {
+ CONTEXT ctx = {};
+ ::RtlCaptureContext(&ctx);
+ AsanContext asan_ctx = {};
+ ContextToAsanContext(ctx, &asan_ctx);
+ ReportBadMemoryAccess(addr, access_mode, access_size, asan_ctx);
+ }
+}
+
+// A few macros to instantiate 'asan_check' and export the instantiations
+// with appropriate names.
+
+#define EXPORT_INTERCEPTOR_READ(access_size, suffix, address_space_size) \
+ void asan_load##access_size##_##suffix(const void* addr) { \
+ return asan_check<access_size, address_space_size, \
+ agent::asan::ASAN_READ_ACCESS>(addr); \
+ }
+
+#define EXPORT_INTERCEPTOR_WRITE(access_size, suffix, address_space_size) \
+ void asan_store##access_size##_##suffix(const void* addr) { \
+ return asan_check<access_size, address_space_size, \
+ agent::asan::ASAN_WRITE_ACCESS>(addr); \
+ }
+
+#define EXPORT_INTERCEPTOR(access_size, suffix, address_space_size) \
+ EXPORT_INTERCEPTOR_READ(access_size, suffix, address_space_size) \
+ EXPORT_INTERCEPTOR_WRITE(access_size, suffix, address_space_size)
+
+#define EXPORT_INTERCEPTORS_ALL_SIZES(suffix, address_space_size) \
+ EXPORT_INTERCEPTOR(1, suffix, address_space_size) \
+ EXPORT_INTERCEPTOR(2, suffix, address_space_size) \
+ EXPORT_INTERCEPTOR(4, suffix, address_space_size) \
+ EXPORT_INTERCEPTOR(8, suffix, address_space_size) \
+ EXPORT_INTERCEPTOR(10, suffix, address_space_size) \
+ EXPORT_INTERCEPTOR(16, suffix, address_space_size) \
+ EXPORT_INTERCEPTOR(32, suffix, address_space_size)
+
+#ifdef _WIN64
+namespace {
+const size_t ONE_TB = static_cast<size_t>(1) << 40;
+}
+#endif
+
+extern "C" {
+void asan_clang_no_check(const void*) {
+ return;
+}
+#ifdef _WIN64
+void asan_string_no_check() {
+ return;
+}
+const void* asan_shadow_references[] = {nullptr};
+EXPORT_INTERCEPTORS_ALL_SIZES(8tb, 8 * ONE_TB - 1)
+EXPORT_INTERCEPTORS_ALL_SIZES(128tb, 128 * ONE_TB - 1)
+#else
+EXPORT_INTERCEPTORS_ALL_SIZES(2gb, 0x7FFFFFFF)
+EXPORT_INTERCEPTORS_ALL_SIZES(4gb, 0xFFFFFFFF)
+#endif
+}
+
+#endif // SYZYGY_AGENT_ASAN_MEMORY_INTERCEPTORS_IMPL_H_
diff --git a/syzygy/agent/asan/memory_interceptors_impl_x64.cc b/syzygy/agent/asan/memory_interceptors_impl_x64.cc
deleted file mode 100644
index dc1c54a..0000000
--- a/syzygy/agent/asan/memory_interceptors_impl_x64.cc
+++ /dev/null
@@ -1,94 +0,0 @@
-// Copyright 2016 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.
-
-// The C++ implementation of the memory interceptors, intended to work on win64,
-// unlike the win32 implementation, written (generated) in pure assembly.
-// This implementation provides less functions than the original win32 one,
-// as it's intended to be used outside of Syzygy.
-
-#include "syzygy/agent/asan/error_info.h"
-#include "syzygy/agent/asan/rtl_utils.h"
-#include "syzygy/agent/asan/runtime.h"
-#include "syzygy/agent/asan/shadow.h"
-
-using namespace agent::asan;
-
-// The template function that performs the checks.
-// @tparam access_size Access size in bytes.
-// @tparam address_space_size The virtual address space size limit in bytes.
-// It's 8 TB for Win7 and Win8 and 128 TB for Win8.1+.
-// @tparam access_mode The access mode, which can be any of AccessMode values,
-// allthough this file only exports the probes for read and write accesses.
-// @param addr The address being accessed.
-template<size_t access_size, size_t address_space_size, AccessMode access_mode>
-void asan_check(const void* addr) {
- if (reinterpret_cast<uintptr_t>(addr) >= address_space_size ||
- !AsanRuntime::runtime()->shadow()->IsRangeAccessible(addr, access_size)) {
- CONTEXT ctx = {};
- ::RtlCaptureContext(&ctx);
- AsanContext asan_ctx = {};
- ContextToAsanContext(ctx, &asan_ctx);
- ReportBadMemoryAccess(addr, access_mode, access_size, asan_ctx);
- }
-}
-
-// A few macros to instantiate 'asan_check' and export the instantiations
-// with appropriate names.
-
-#define EXPORT_INTERCEPTOR_READ(access_size, suffix, address_space_size) \
-void asan_check_##access_size##_byte_read_access_no_flags_##suffix( \
- const void* addr) { \
- return asan_check<access_size, address_space_size, \
- agent::asan::ASAN_READ_ACCESS>(addr); \
-} \
-void asan_check_##access_size##_byte_read_access_##suffix(const void* addr) { \
- return asan_check<access_size, address_space_size, \
- agent::asan::ASAN_READ_ACCESS>(addr); \
-}
-
-#define EXPORT_INTERCEPTOR_WRITE(access_size, suffix, address_space_size) \
-void asan_check_##access_size##_byte_write_access_no_flags_##suffix( \
- const void* addr) { \
- return asan_check<access_size, address_space_size, \
- agent::asan::ASAN_WRITE_ACCESS>(addr); \
-} \
-void asan_check_##access_size##_byte_write_access_##suffix(const void* addr) { \
- return asan_check<access_size, address_space_size, \
- agent::asan::ASAN_WRITE_ACCESS>(addr); \
-}
-
-#define EXPORT_INTERCEPTOR(access_size, suffix, address_space_size) \
- EXPORT_INTERCEPTOR_READ(access_size, suffix, address_space_size) \
- EXPORT_INTERCEPTOR_WRITE(access_size, suffix, address_space_size)
-
-#define EXPORT_INTERCEPTORS_ALL_SIZES(suffix, address_space_size) \
- EXPORT_INTERCEPTOR(1, suffix, address_space_size) \
- EXPORT_INTERCEPTOR(2, suffix, address_space_size) \
- EXPORT_INTERCEPTOR(4, suffix, address_space_size) \
- EXPORT_INTERCEPTOR(8, suffix, address_space_size) \
- EXPORT_INTERCEPTOR(10, suffix, address_space_size) \
- EXPORT_INTERCEPTOR(16, suffix, address_space_size) \
- EXPORT_INTERCEPTOR(32, suffix, address_space_size)
-
-namespace {
-const size_t ONE_TB = static_cast<size_t>(1) << 40;
-}
-
-extern "C" {
- void asan_no_check() { return; }
- void asan_string_no_check() { return; }
- void* asan_shadow_references[] = { nullptr };
- EXPORT_INTERCEPTORS_ALL_SIZES(8tb, 8 * ONE_TB)
- EXPORT_INTERCEPTORS_ALL_SIZES(128tb, 128 * ONE_TB)
-}
diff --git a/syzygy/agent/asan/memory_interceptors_unittest.cc b/syzygy/agent/asan/memory_interceptors_unittest.cc
index ee2c556..f2cc5d8 100644
--- a/syzygy/agent/asan/memory_interceptors_unittest.cc
+++ b/syzygy/agent/asan/memory_interceptors_unittest.cc
@@ -40,6 +40,18 @@
#undef DEFINE_INTERCEPT_FUNCTION_TABLE
};
+static const TestMemoryInterceptors::ClangInterceptFunction
+ clang_intercept_functions[] = {
+#define DEFINE_CLANG_INTERCEPT_FUNCTION_TABLE(access_size, access_mode_str, \
+ access_mode) \
+ { asan_##access_mode_str##access_size##_2gb, access_size } \
+ , {asan_##access_mode_str##access_size##_4gb, access_size},
+ CLANG_ASAN_MEM_INTERCEPT_FUNCTIONS(
+ DEFINE_CLANG_INTERCEPT_FUNCTION_TABLE)
+
+#undef DEFINE_CLANG_INTERCEPT_FUNCTION_TABLE
+};
+
static const TestMemoryInterceptors::InterceptFunction
intercept_functions_no_flags[] = {
#define DEFINE_INTERCEPT_FUNCTION_TABLE_NO_FLAGS(access_size, access_mode_str, \
@@ -56,14 +68,13 @@
#undef DEFINE_INTERCEPT_FUNCTION_TABLE_NO_FLAGS
};
-static const TestMemoryInterceptors::InterceptFunction
- redirect_functions[] = {
-#define DEFINE_REDIRECT_FUNCTION_TABLE(access_size, access_mode_str, \
- access_mode) \
- { asan_redirect_ ## access_size ## _byte_ ## access_mode_str, \
- access_size }, \
+static const TestMemoryInterceptors::InterceptFunction redirect_functions[] = {
+#define DEFINE_REDIRECT_FUNCTION_TABLE(access_size, access_mode_str, \
+ access_mode) \
+ { asan_redirect_##access_size##_byte_##access_mode_str, access_size } \
+ ,
-ASAN_MEM_INTERCEPT_FUNCTIONS(DEFINE_REDIRECT_FUNCTION_TABLE)
+ ASAN_MEM_INTERCEPT_FUNCTIONS(DEFINE_REDIRECT_FUNCTION_TABLE)
#undef DEFINE_REDIRECT_FUNCTION_TABLE
};
@@ -75,7 +86,7 @@
{ asan_redirect_ ## access_size ## _byte_ ## access_mode_str ## _no_flags, \
access_size },
-ASAN_MEM_INTERCEPT_FUNCTIONS(DEFINE_REDIRECT_FUNCTION_TABLE_NO_FLAGS)
+ ASAN_MEM_INTERCEPT_FUNCTIONS(DEFINE_REDIRECT_FUNCTION_TABLE_NO_FLAGS)
#undef DEFINE_REDIRECT_FUNCTION_TABLE_NO_FLAGS
};
@@ -132,17 +143,17 @@
} // namespace
TEST_F(MemoryInterceptorsTest, TestValidAccess) {
- TestValidAccess(intercept_functions);
+ TestValidAccess(intercept_functions, clang_intercept_functions);
TestValidAccessIgnoreFlags(intercept_functions_no_flags);
}
TEST_F(MemoryInterceptorsTest, TestOverrunAccess) {
- TestOverrunAccess(intercept_functions);
+ TestOverrunAccess(intercept_functions, clang_intercept_functions);
TestOverrunAccessIgnoreFlags(intercept_functions_no_flags);
}
TEST_F(MemoryInterceptorsTest, TestUnderrunAccess) {
- TestUnderrunAccess(intercept_functions);
+ TestUnderrunAccess(intercept_functions, clang_intercept_functions);
TestUnderrunAccessIgnoreFlags(intercept_functions_no_flags);
}
@@ -151,12 +162,13 @@
EXPECT_CALL(*this, OnRedirectorInvocation(_))
.Times(arraysize(redirect_functions))
.WillRepeatedly(Return(MEMORY_ACCESSOR_MODE_NOOP));
- TestValidAccess(redirect_functions);
+ TestValidAccess(redirect_functions, arraysize(redirect_functions));
EXPECT_CALL(*this, OnRedirectorInvocation(_))
.Times(arraysize(redirect_functions_no_flags))
.WillRepeatedly(Return(MEMORY_ACCESSOR_MODE_NOOP));
- TestValidAccessIgnoreFlags(redirect_functions_no_flags);
+ TestValidAccessIgnoreFlags(redirect_functions_no_flags,
+ arraysize(redirect_functions_no_flags));
}
TEST_F(MemoryInterceptorsTest, TestRedirectors2G) {
@@ -165,9 +177,9 @@
.WillRepeatedly(Return(MEMORY_ACCESSOR_MODE_2G));
// Test valid, underrun and overrun.
- TestValidAccess(redirect_functions);
- TestUnderrunAccess(redirect_functions);
- TestOverrunAccess(redirect_functions);
+ TestValidAccess(redirect_functions, arraysize(redirect_functions));
+ TestUnderrunAccess(redirect_functions, arraysize(redirect_functions));
+ TestOverrunAccess(redirect_functions, arraysize(redirect_functions));
EXPECT_CALL(*this, OnRedirectorInvocation(_))
.Times(3 * arraysize(redirect_functions_no_flags))
diff --git a/syzygy/agent/asan/rtl_unittest.cc b/syzygy/agent/asan/rtl_unittest.cc
index 291a1d0..cf7f506 100644
--- a/syzygy/agent/asan/rtl_unittest.cc
+++ b/syzygy/agent/asan/rtl_unittest.cc
@@ -26,8 +26,70 @@
namespace {
using testing::AsanBlockInfoVector;
+using testing::ClangMemoryAccessorTester;
using testing::MemoryAccessorTester;
using testing::ScopedAsanAlloc;
+#ifndef _WIN64
+using testing::SyzyAsanMemoryAccessorTester;
+#endif
+
+// Helper class to check an Asan function. This allows to test different probes
+// with different calling conventions.
+class AsanFunctionCheck {
+ public:
+ virtual ~AsanFunctionCheck() {}
+
+ // Memory accessor tested that should be used.
+ virtual MemoryAccessorTester* tester() = 0;
+
+ // Name of the function that should be tested.
+ virtual const char* function_name() = 0;
+
+ // Try to access |ptr| via |access_fn|.
+ virtual void CheckAccess(FARPROC access_fn, void* ptr) = 0;
+};
+
+#ifndef _WIN64
+// Specialization of the AsanFunctionCheck class to test the probes with the
+// SyzyAsan custom calling convention (value to check in EDX).
+class SyzyAsanFunctionCheck : public AsanFunctionCheck {
+ public:
+ MemoryAccessorTester* tester() override { return &tester_; }
+
+ // Name of one of the SyzyAsan probes.
+ const char* function_name() override {
+ return "asan_check_1_byte_read_access";
+ }
+
+ // Check access and ensure that the context hasn't been altered.
+ void CheckAccess(FARPROC access_fn, void* ptr) override {
+ tester_.CheckAccessAndCompareContexts(access_fn, ptr);
+ }
+
+ private:
+ // The tester used by this function checker.
+ SyzyAsanMemoryAccessorTester tester_;
+};
+#endif
+
+// Specialization of the AsanFunctionCheck class to test the probes with the
+// cdecl calling convention (value to check on the stack).
+class ClangAsanFunctionCheck : public AsanFunctionCheck {
+ public:
+ MemoryAccessorTester* tester() override { return &tester_; }
+
+ // Name of one of the Clang-Asan probes.
+ const char* function_name() override { return "__asan_load1"; }
+
+ // Check the access
+ void CheckAccess(FARPROC access_fn, void* ptr) override {
+ tester_.CheckAccess(access_fn, ptr);
+ }
+
+ private:
+ // The tester used by this function checker.
+ ClangMemoryAccessorTester tester_;
+};
// An arbitrary size for the buffer we allocate in the different unittests.
const size_t kAllocSize = 13;
@@ -37,6 +99,8 @@
AsanRtlTest() : memory_src_(NULL), memory_dst_(NULL), memory_length_(0),
memory_size_(0) { }
+ virtual ~AsanRtlTest() {}
+
void SetUp() override {
testing::TestAsanRtl::SetUp();
@@ -55,6 +119,28 @@
int32_t memory_size_;
};
+// Specialization of the AsanRtlTest for the test that should be done with
+// different sets of probes.
+template <class T>
+class AsanRtlTypedTest : public AsanRtlTest {
+ public:
+ ~AsanRtlTypedTest() override {}
+
+ protected:
+ T tester_;
+};
+
+// Access functions checker that should be used in the typed tests. On 32-bit
+// we need to test both the SyzyAsan and the Clang-Asan probes and in 64-bit
+// we only test the Clang-Asan ones.
+#ifndef _WIN64
+typedef ::testing::Types<SyzyAsanFunctionCheck, ClangAsanFunctionCheck>
+ CheckAccessTypes;
+#else
+typedef ::testing::Types<ClangAsanFunctionCheck> CheckAccessTypes;
+#endif
+TYPED_TEST_CASE(AsanRtlTypedTest, CheckAccessTypes);
+
void AsanRtlTest::AllocMemoryBuffers(int32_t length, int32_t element_size) {
ASSERT_EQ(reinterpret_cast<void*>(NULL), memory_src_);
ASSERT_EQ(reinterpret_cast<void*>(NULL), memory_dst_);
@@ -100,10 +186,9 @@
asan_heap_handle);
}
-#ifndef _WIN64
-TEST_F(AsanRtlTest, AsanCheckGoodAccess) {
+TYPED_TEST(AsanRtlTypedTest, AsanCheckGoodAccess) {
FARPROC check_access_fn =
- ::GetProcAddress(asan_rtl_, "asan_check_4_byte_read_access");
+ ::GetProcAddress(asan_rtl_, tester_.function_name());
ASSERT_TRUE(check_access_fn != NULL);
// Run through access checking an allocation that's larger than our
@@ -112,47 +197,43 @@
ScopedAsanAlloc<uint8_t> 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));
+ tester_.CheckAccess(check_access_fn, mem.get() + i));
}
}
-#endif
-TEST_F(AsanRtlTest, AsanCheckHeapBufferOverflow) {
+TYPED_TEST(AsanRtlTypedTest, AsanCheckHeapBufferOverflow) {
FARPROC check_access_fn =
- ::GetProcAddress(asan_rtl_, "asan_check_4_byte_read_access");
+ ::GetProcAddress(asan_rtl_, tester_.function_name());
ASSERT_TRUE(check_access_fn != NULL);
ScopedAsanAlloc<uint8_t> mem(this, kAllocSize);
ASSERT_TRUE(mem.get() != NULL);
- MemoryAccessorTester tester;
- tester.AssertMemoryErrorIsDetected(
+ 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) {
+TYPED_TEST(AsanRtlTypedTest, AsanCheckHeapBufferUnderflow) {
FARPROC check_access_fn =
- ::GetProcAddress(asan_rtl_, "asan_check_4_byte_read_access");
+ ::GetProcAddress(asan_rtl_, tester_.function_name());
ASSERT_TRUE(check_access_fn != NULL);
ScopedAsanAlloc<uint8_t> mem(this, kAllocSize);
ASSERT_TRUE(mem.get() != NULL);
- MemoryAccessorTester tester;
- tester.AssertMemoryErrorIsDetected(
- check_access_fn, mem.get() - 1, HEAP_BUFFER_UNDERFLOW);
+ 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) {
+TYPED_TEST(AsanRtlTypedTest, AsanCheckUseAfterFree) {
FARPROC check_access_fn =
- ::GetProcAddress(asan_rtl_, "asan_check_4_byte_read_access");
+ ::GetProcAddress(asan_rtl_, tester_.function_name());
ASSERT_TRUE(check_access_fn != NULL);
ScopedAsanAlloc<uint8_t> mem(this, kAllocSize);
@@ -161,16 +242,16 @@
uint8_t* mem_ptr = mem.get();
mem.reset(NULL);
- MemoryAccessorTester tester;
- tester.AssertMemoryErrorIsDetected(check_access_fn, mem_ptr, USE_AFTER_FREE);
+ 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) {
+TYPED_TEST(AsanRtlTypedTest, AsanCheckDoubleFree) {
FARPROC check_access_fn =
- ::GetProcAddress(asan_rtl_, "asan_check_4_byte_read_access");
+ ::GetProcAddress(asan_rtl_, tester_.function_name());
ASSERT_TRUE(check_access_fn != NULL);
uint8_t* mem_ptr = NULL;
@@ -180,18 +261,17 @@
mem_ptr = mem.get();
}
- MemoryAccessorTester tester;
- tester.set_expected_error_type(DOUBLE_FREE);
+ tester_.tester()->set_expected_error_type(DOUBLE_FREE);
EXPECT_FALSE(HeapFreeFunction(heap_, 0, mem_ptr));
- EXPECT_TRUE(tester.memory_error_detected());
+ EXPECT_TRUE(tester_.tester()->memory_error_detected());
EXPECT_TRUE(LogContains(kAttemptingDoubleFree));
EXPECT_TRUE(LogContains("previously allocated here"));
EXPECT_TRUE(LogContains("freed here"));
}
-TEST_F(AsanRtlTest, AsanCheckWildAccess) {
+TYPED_TEST(AsanRtlTypedTest, AsanCheckWildAccess) {
FARPROC check_access_fn =
- ::GetProcAddress(asan_rtl_, "asan_check_4_byte_read_access");
+ ::GetProcAddress(asan_rtl_, tester_.function_name());
ASSERT_TRUE(check_access_fn != NULL);
#ifndef _WIN64
@@ -200,9 +280,8 @@
void* addr = reinterpret_cast<void*>(1ULL << 63);
#endif
- MemoryAccessorTester tester;
- tester.AssertMemoryErrorIsDetected(
- check_access_fn, addr, WILD_ACCESS);
+ tester_.tester()->AssertMemoryErrorIsDetected(check_access_fn, addr,
+ WILD_ACCESS);
EXPECT_TRUE(LogContains(kWildAccess));
}
@@ -210,46 +289,42 @@
// It is not possible to test the near-nullptr access with heap corruption
// execution path since it depends on the unhandled exception filter which is
// not installed in the rtl library.
-TEST_F(AsanRtlTest, AsanIgnoreInvalidAccess) {
+TYPED_TEST(AsanRtlTypedTest, AsanIgnoreInvalidAccess) {
FARPROC check_access_fn =
- ::GetProcAddress(asan_rtl_, "asan_check_4_byte_read_access");
+ ::GetProcAddress(asan_rtl_, tester_.function_name());
ASSERT_TRUE(check_access_fn != NULL);
// A near-nullptr access should not be reported by SyzyASAN.
- MemoryAccessorTester tester;
- tester.CheckAccessAndCompareContexts(check_access_fn, nullptr);
+ tester_.CheckAccess(check_access_fn, nullptr);
EXPECT_FALSE(LogContains(kInvalidAddress));
}
#endif
-TEST_F(AsanRtlTest, AsanReportInvalidAccess) {
+TYPED_TEST(AsanRtlTypedTest, AsanReportInvalidAccess) {
FARPROC check_access_fn =
- ::GetProcAddress(asan_rtl_, "asan_check_4_byte_read_access");
+ ::GetProcAddress(asan_rtl_, tester_.function_name());
ASSERT_NE(static_cast<FARPROC>(nullptr), check_access_fn);
-
- MemoryAccessorTester tester;
agent::asan::AsanRuntime* runtime = GetActiveRuntimeFunction();
ASSERT_NE(static_cast<agent::asan::AsanRuntime*>(nullptr), runtime);
runtime->params().report_invalid_accesses = true;
- tester.AssertMemoryErrorIsDetected(
+ tester_.tester()->AssertMemoryErrorIsDetected(
check_access_fn, static_cast<void*>(nullptr), INVALID_ADDRESS);
EXPECT_TRUE(LogContains(kInvalidAddress));
}
-TEST_F(AsanRtlTest, AsanCheckCorruptBlock) {
+TYPED_TEST(AsanRtlTypedTest, AsanCheckCorruptBlock) {
void* mem = HeapAllocFunction(heap_, 0, kAllocSize);
reinterpret_cast<uint8_t*>(mem)[-1]--;
- MemoryAccessorTester tester;
- tester.set_expected_error_type(CORRUPT_BLOCK);
+ tester_.tester()->set_expected_error_type(CORRUPT_BLOCK);
EXPECT_TRUE(HeapFreeFunction(heap_, 0, mem));
- EXPECT_TRUE(tester.memory_error_detected());
+ EXPECT_TRUE(tester_.tester()->memory_error_detected());
EXPECT_TRUE(LogContains(kHeapCorruptBlock));
EXPECT_TRUE(LogContains("previously allocated here"));
}
-TEST_F(AsanRtlTest, AsanCheckCorruptHeap) {
+TYPED_TEST(AsanRtlTypedTest, AsanCheckCorruptHeap) {
FARPROC check_access_fn =
- ::GetProcAddress(asan_rtl_, "asan_check_4_byte_read_access");
+ ::GetProcAddress(asan_rtl_, tester_.function_name());
ASSERT_TRUE(check_access_fn != nullptr);
agent::asan::AsanRuntime* runtime = GetActiveRuntimeFunction();
@@ -262,8 +337,8 @@
const size_t kMaxIterations = 10;
// Retrieves the information about this block.
- BlockHeader* header = BlockGetHeaderFromBody(
- reinterpret_cast<BlockBody*>(mem.get()));
+ BlockHeader* header =
+ BlockGetHeaderFromBody(reinterpret_cast<BlockBody*>(mem.get()));
BlockInfo block_info = {};
EXPECT_TRUE(BlockInfoFromMemory(header, &block_info));
@@ -275,21 +350,21 @@
// 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(
+ 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)
+ if (!tester_.tester()->last_error_info().heap_is_corrupt &&
+ i + 1 < kMaxIterations)
continue;
- EXPECT_TRUE(tester.last_error_info().heap_is_corrupt);
+ EXPECT_TRUE(tester_.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());
- AsanBlockInfoVector blocks_info = tester.last_corrupt_ranges()[0].second;
+ EXPECT_EQ(1, tester_.tester()->last_error_info().corrupt_range_count);
+ EXPECT_EQ(1, tester_.tester()->last_corrupt_ranges().size());
+ AsanBlockInfoVector blocks_info =
+ tester_.tester()->last_corrupt_ranges()[0].second;
EXPECT_EQ(1, blocks_info.size());
EXPECT_EQ(kDataIsCorrupt, blocks_info[0].analysis.block_state);
@@ -301,10 +376,10 @@
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);
+ tester_.tester()->set_memory_error_detected(false);
+ tester_.tester()->set_expected_error_type(CORRUPT_BLOCK);
mem.reset(NULL);
- EXPECT_TRUE(tester.memory_error_detected());
+ EXPECT_TRUE(tester_.tester()->memory_error_detected());
break;
}
@@ -312,11 +387,9 @@
#ifndef _WIN64
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"
- };
+ 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_t));
@@ -330,11 +403,10 @@
ASSERT_TRUE(check_access_fn != NULL);
for (int32_t i = 0; i < memory_length_; ++i) {
- MemoryAccessorTester tester;
+ SyzyAsanMemoryAccessorTester tester;
tester.ExpectSpecialMemoryErrorIsDetected(
- check_access_fn, MemoryAccessorTester::DIRECTION_FORWARD,
- false, &dst[i], &src[i], 0xDEADDEAD,
- UNKNOWN_BAD_ACCESS);
+ check_access_fn, SyzyAsanMemoryAccessorTester::DIRECTION_FORWARD,
+ false, &dst[i], &src[i], 0xDEADDEAD, UNKNOWN_BAD_ACCESS);
}
}
@@ -342,11 +414,9 @@
}
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"
- };
+ 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_t));
@@ -360,9 +430,9 @@
ASSERT_TRUE(check_access_fn != NULL);
for (int32_t i = 0; i < memory_length_; ++i) {
- MemoryAccessorTester tester;
+ SyzyAsanMemoryAccessorTester tester;
tester.ExpectSpecialMemoryErrorIsDetected(
- check_access_fn, MemoryAccessorTester::DIRECTION_FORWARD,
+ check_access_fn, SyzyAsanMemoryAccessorTester::DIRECTION_FORWARD,
false, &dst[i], &src[i], 0xDEADDEAD, UNKNOWN_BAD_ACCESS);
}
}
@@ -371,11 +441,9 @@
}
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"
- };
+ 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_t));
@@ -389,9 +457,9 @@
ASSERT_TRUE(check_access_fn != NULL);
for (int32_t i = 0; i < memory_length_; ++i) {
- MemoryAccessorTester tester;
+ SyzyAsanMemoryAccessorTester tester;
tester.ExpectSpecialMemoryErrorIsDetected(
- check_access_fn, MemoryAccessorTester::DIRECTION_FORWARD,
+ check_access_fn, SyzyAsanMemoryAccessorTester::DIRECTION_FORWARD,
false, &dst[i], &src[i], 0xDEADDEAD, UNKNOWN_BAD_ACCESS);
}
}
@@ -400,14 +468,12 @@
}
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"
- };
+ 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_t));
@@ -420,31 +486,29 @@
::GetProcAddress(asan_rtl_, function_names[function]);
ASSERT_TRUE(check_access_fn != NULL);
- MemoryAccessorTester tester;
+ SyzyAsanMemoryAccessorTester tester;
tester.ExpectSpecialMemoryErrorIsDetected(
- check_access_fn, MemoryAccessorTester::DIRECTION_FORWARD,
- true, &dst[0], &src[-1], 0xDEADDEAD, HEAP_BUFFER_UNDERFLOW);
+ check_access_fn, SyzyAsanMemoryAccessorTester::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);
+ check_access_fn, SyzyAsanMemoryAccessorTester::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);
+ check_access_fn, SyzyAsanMemoryAccessorTester::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);
+ check_access_fn, SyzyAsanMemoryAccessorTester::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"
- };
+ 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_t));
@@ -457,20 +521,20 @@
::GetProcAddress(asan_rtl_, function_names[function]);
ASSERT_TRUE(check_access_fn != NULL);
- MemoryAccessorTester tester;
+ SyzyAsanMemoryAccessorTester tester;
tester.ExpectSpecialMemoryErrorIsDetected(
- check_access_fn, MemoryAccessorTester::DIRECTION_FORWARD,
- false, &dst[0], &src[-1], 0xDEAD, HEAP_BUFFER_UNDERFLOW);
+ check_access_fn, SyzyAsanMemoryAccessorTester::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);
+ check_access_fn, SyzyAsanMemoryAccessorTester::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);
+ check_access_fn, SyzyAsanMemoryAccessorTester::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);
+ check_access_fn, SyzyAsanMemoryAccessorTester::DIRECTION_FORWARD, true,
+ &dst[memory_length_], &src[0], 0xDEADDEAD, HEAP_BUFFER_OVERFLOW);
}
FreeMemoryBuffers();
@@ -493,10 +557,10 @@
::GetProcAddress(asan_rtl_, function_names[function]);
ASSERT_TRUE(check_access_fn != NULL);
- MemoryAccessorTester tester;
+ SyzyAsanMemoryAccessorTester tester;
tester.ExpectSpecialMemoryErrorIsDetected(
- check_access_fn, MemoryAccessorTester::DIRECTION_FORWARD,
- false, &dst[0], &src[0], memory_length_, UNKNOWN_BAD_ACCESS);
+ check_access_fn, SyzyAsanMemoryAccessorTester::DIRECTION_FORWARD, false,
+ &dst[0], &src[0], memory_length_, UNKNOWN_BAD_ACCESS);
}
FreeMemoryBuffers();
@@ -519,16 +583,16 @@
::GetProcAddress(asan_rtl_, function_names[function]);
ASSERT_TRUE(check_access_fn != NULL);
- MemoryAccessorTester tester;
+ SyzyAsanMemoryAccessorTester tester;
tester.ExpectSpecialMemoryErrorIsDetected(
- check_access_fn, MemoryAccessorTester::DIRECTION_FORWARD,
- true, &dst[0], &src[0], memory_length_ + 1, HEAP_BUFFER_OVERFLOW);
+ check_access_fn, SyzyAsanMemoryAccessorTester::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);
+ check_access_fn, SyzyAsanMemoryAccessorTester::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);
+ check_access_fn, SyzyAsanMemoryAccessorTester::DIRECTION_FORWARD, true,
+ &dst[-1], &src[0], memory_length_, HEAP_BUFFER_UNDERFLOW);
}
FreeMemoryBuffers();
@@ -551,12 +615,11 @@
::GetProcAddress(asan_rtl_, function_names[function]);
ASSERT_TRUE(check_access_fn != NULL);
- MemoryAccessorTester tester;
+ SyzyAsanMemoryAccessorTester tester;
tester.ExpectSpecialMemoryErrorIsDetected(
- check_access_fn, MemoryAccessorTester::DIRECTION_BACKWARD,
- false, &dst[memory_length_ - 1],
- &src[memory_length_ - 1], memory_length_,
- UNKNOWN_BAD_ACCESS);
+ check_access_fn, SyzyAsanMemoryAccessorTester::DIRECTION_BACKWARD,
+ false, &dst[memory_length_ - 1], &src[memory_length_ - 1],
+ memory_length_, UNKNOWN_BAD_ACCESS);
}
FreeMemoryBuffers();
@@ -588,10 +651,10 @@
ASSERT_TRUE(check_access_fn != NULL);
// A prefixed instruction with a count of zero do not have side effects.
- MemoryAccessorTester tester;
+ SyzyAsanMemoryAccessorTester tester;
tester.ExpectSpecialMemoryErrorIsDetected(
- check_access_fn, MemoryAccessorTester::DIRECTION_FORWARD,
- false, &dst[-1], &src[-1], 0, UNKNOWN_BAD_ACCESS);
+ check_access_fn, SyzyAsanMemoryAccessorTester::DIRECTION_FORWARD, false,
+ &dst[-1], &src[-1], 0, UNKNOWN_BAD_ACCESS);
}
FreeMemoryBuffers();
@@ -619,10 +682,10 @@
ASSERT_TRUE(check_access_fn != NULL);
// Compare instruction stop their execution when values differ.
- MemoryAccessorTester tester;
+ SyzyAsanMemoryAccessorTester tester;
tester.ExpectSpecialMemoryErrorIsDetected(
- check_access_fn, MemoryAccessorTester::DIRECTION_FORWARD,
- false, &dst[0], &src[0], memory_length_ + 1, UNKNOWN_BAD_ACCESS);
+ check_access_fn, SyzyAsanMemoryAccessorTester::DIRECTION_FORWARD, false,
+ &dst[0], &src[0], memory_length_ + 1, UNKNOWN_BAD_ACCESS);
}
FreeMemoryBuffers();
diff --git a/syzygy/agent/asan/system_interceptors_x64.def b/syzygy/agent/asan/system_interceptors_x64.def
index f16e7b4..68c77f2 100644
--- a/syzygy/agent/asan/system_interceptors_x64.def
+++ b/syzygy/agent/asan/system_interceptors_x64.def
@@ -18,25 +18,19 @@
EXPORTS
; Access checking functions.
-
- ; TODO: add redirectors and export them by default
-
- asan_check_1_byte_read_access=asan_check_1_byte_read_access_128tb
- asan_check_2_byte_read_access=asan_check_2_byte_read_access_128tb
- asan_check_4_byte_read_access=asan_check_4_byte_read_access_128tb
- asan_check_8_byte_read_access=asan_check_8_byte_read_access_128tb
- asan_check_10_byte_read_access=asan_check_10_byte_read_access_128tb
- asan_check_16_byte_read_access=asan_check_16_byte_read_access_128tb
- asan_check_32_byte_read_access=asan_check_32_byte_read_access_128tb
-
- asan_check_1_byte_write_access=asan_check_1_byte_write_access_128tb
- asan_check_2_byte_write_access=asan_check_2_byte_write_access_128tb
- asan_check_4_byte_write_access=asan_check_4_byte_write_access_128tb
- asan_check_8_byte_write_access=asan_check_8_byte_write_access_128tb
- asan_check_10_byte_write_access=asan_check_10_byte_write_access_128tb
- asan_check_16_byte_write_access=asan_check_16_byte_write_access_128tb
- asan_check_32_byte_write_access=asan_check_32_byte_write_access_128tb
-
+ ; TODO(sebmarchand): Use the redirectors once they're available.
+ __asan_load1=asan_load1_128tb
+ __asan_load2=asan_load2_128tb
+ __asan_load4=asan_load4_128tb
+ __asan_load8=asan_load8_128tb
+ __asan_load16=asan_load16_128tb
+ __asan_load32=asan_load32_128tb
+ __asan_store1=asan_store1_128tb
+ __asan_store2=asan_store2_128tb
+ __asan_store4=asan_store4_128tb
+ __asan_store8=asan_store8_128tb
+ __asan_store16=asan_store16_128tb
+ __asan_store32=asan_store32_128tb
; Heap-replacement functions.
asan_GetProcessHeap
diff --git a/syzygy/agent/asan/syzyasan_rtl.cc b/syzygy/agent/asan/syzyasan_rtl.cc
index c571eab..b6e7e85 100644
--- a/syzygy/agent/asan/syzyasan_rtl.cc
+++ b/syzygy/agent/asan/syzyasan_rtl.cc
@@ -120,6 +120,7 @@
if (mode == MEMORY_ACCESSOR_MODE_NOOP && asan_runtime != nullptr)
TearDownAsanRuntime(&asan_runtime);
+#ifndef _WIN64
// Build the IAT patch map.
IATPatchMap patch_map;
for (size_t i = 0; i < kNumMemoryAccessorVariants; ++i) {
@@ -146,6 +147,7 @@
patch_complete = true;
}
}
+#endif
return mode;
}
diff --git a/syzygy/agent/asan/syzyasan_rtl.def.template b/syzygy/agent/asan/syzyasan_rtl.def.template
index 0bcf43f..6cb4085 100644
--- a/syzygy/agent/asan/syzyasan_rtl.def.template
+++ b/syzygy/agent/asan/syzyasan_rtl.def.template
@@ -135,3 +135,18 @@
; Exposed to allow the user to enumerate runtime experiments.
asan_EnumExperiments
+
+ ; Clang-Asan access checking functions.
+ ; TODO(sebmarchand): Use the redirectors once they're available.
+ __asan_load1=asan_load1_2gb
+ __asan_load2=asan_load2_2gb
+ __asan_load4=asan_load4_2gb
+ __asan_load8=asan_load8_2gb
+ __asan_load16=asan_load16_2gb
+ __asan_load32=asan_load32_2gb
+ __asan_store1=asan_store1_2gb
+ __asan_store2=asan_store2_2gb
+ __asan_store4=asan_store4_2gb
+ __asan_store8=asan_store8_2gb
+ __asan_store16=asan_store16_2gb
+ __asan_store32=asan_store32_2gb
diff --git a/syzygy/agent/asan/unittest_util.cc b/syzygy/agent/asan/unittest_util.cc
index 79e4378..8b2bfda 100644
--- a/syzygy/agent/asan/unittest_util.cc
+++ b/syzygy/agent/asan/unittest_util.cc
@@ -135,8 +135,6 @@
} // namespace
-MemoryAccessorTester* MemoryAccessorTester::instance_ = NULL;
-
// Define the function pointers.
#define DEFINE_FUNCTION_PTR_VARIABLE(convention, ret, name, args, argnames) \
name##FunctionPtr TestAsanRtl::name##Function;
@@ -531,39 +529,111 @@
} // namespace
+MemoryAccessorTester* MemoryAccessorTester::instance_ = nullptr;
+
MemoryAccessorTester::MemoryAccessorTester()
: expected_error_type_(agent::asan::UNKNOWN_BAD_ACCESS),
- memory_error_detected_(false),
- ignore_flags_(false) {
- EXPECT_EQ(static_cast<MemoryAccessorTester*>(NULL), instance_);
-
+ memory_error_detected_(false) {
Initialize();
- instance_ = this;
-}
-
-MemoryAccessorTester::MemoryAccessorTester(IgnoreFlags /* ignore_flags */)
- : expected_error_type_(agent::asan::UNKNOWN_BAD_ACCESS),
- memory_error_detected_(false),
- ignore_flags_(true) {
- EXPECT_EQ(static_cast<MemoryAccessorTester*>(NULL), instance_);
-
- Initialize();
- instance_ = this;
}
MemoryAccessorTester::~MemoryAccessorTester() {
- EXPECT_EQ(this, instance_);
- instance_ = NULL;
+ DCHECK_NE(static_cast<MemoryAccessorTester*>(nullptr), instance_);
+ instance_ = nullptr;
}
void MemoryAccessorTester::Initialize() {
- ::memset(&context_before_hook_, 0xCD, sizeof(context_before_hook_));
- ::memset(&context_after_hook_, 0xCE, sizeof(context_after_hook_));
+ DCHECK_EQ(static_cast<MemoryAccessorTester*>(nullptr), instance_);
+ instance_ = this;
::memset(&error_context_, 0xCF, sizeof(error_context_));
::memset(&last_error_info_, 0, sizeof(last_error_info_));
}
+void MemoryAccessorTester::AsanErrorCallbackImpl(AsanErrorInfo* error_info) {
+ EXPECT_NE(reinterpret_cast<AsanErrorInfo*>(NULL), error_info);
+ EXPECT_NE(agent::asan::UNKNOWN_BAD_ACCESS, error_info->error_type);
+
+ EXPECT_EQ(expected_error_type_, error_info->error_type);
+ if (error_info->error_type >= agent::asan::USE_AFTER_FREE) {
+ // We should at least have the stack trace of the allocation of this block.
+ EXPECT_GT(error_info->block_info.alloc_stack_size, 0U);
+ EXPECT_NE(0U, error_info->block_info.alloc_tid);
+ if (error_info->error_type == agent::asan::USE_AFTER_FREE ||
+ error_info->error_type == agent::asan::DOUBLE_FREE) {
+ EXPECT_GT(error_info->block_info.free_stack_size, 0U);
+ EXPECT_NE(0U, error_info->block_info.free_tid);
+ } else {
+ EXPECT_EQ(error_info->block_info.free_stack_size, 0U);
+ EXPECT_EQ(0U, error_info->block_info.free_tid);
+ }
+ }
+
+ if (error_info->error_type == agent::asan::HEAP_BUFFER_OVERFLOW) {
+ EXPECT_TRUE(strstr(error_info->shadow_info, "beyond") != NULL);
+ } else if (error_info->error_type == agent::asan::HEAP_BUFFER_UNDERFLOW) {
+ EXPECT_TRUE(strstr(error_info->shadow_info, "before") != NULL);
+ }
+
+ memory_error_detected_ = true;
+ last_error_info_ = *error_info;
+
+ // Copy the corrupt range's information.
+ if (error_info->heap_is_corrupt) {
+ EXPECT_GE(1U, error_info->corrupt_range_count);
+ for (size_t i = 0; i < error_info->corrupt_range_count; ++i) {
+ last_corrupt_ranges_.push_back(CorruptRangeInfo());
+ CorruptRangeInfo* range_info = &last_corrupt_ranges_.back();
+ range_info->first = error_info->corrupt_ranges[i];
+ AsanBlockInfoVector* block_infos = &range_info->second;
+ for (size_t j = 0; j < range_info->first.block_info_count; ++j) {
+ agent::asan::AsanBlockInfo block_info = range_info->first.block_info[j];
+ for (size_t k = 0; k < range_info->first.block_info[j].alloc_stack_size;
+ ++k) {
+ block_info.alloc_stack[k] =
+ range_info->first.block_info[j].alloc_stack[k];
+ }
+ for (size_t k = 0; k < range_info->first.block_info[j].free_stack_size;
+ ++k) {
+ block_info.free_stack[k] =
+ range_info->first.block_info[j].free_stack[k];
+ }
+ block_infos->push_back(block_info);
+ }
+ }
+ }
+
+ error_context_ = error_info->context;
+}
+
+void MemoryAccessorTester::AsanErrorCallback(AsanErrorInfo* error_info) {
+ DCHECK_NE(static_cast<MemoryAccessorTester*>(nullptr), instance_);
+ instance_->AsanErrorCallbackImpl(error_info);
+}
+
#ifndef _WIN64
+SyzyAsanMemoryAccessorTester::SyzyAsanMemoryAccessorTester()
+ : ignore_flags_(false) {
+}
+SyzyAsanMemoryAccessorTester::SyzyAsanMemoryAccessorTester(
+ IgnoreFlags /* ignore_flags */)
+ : ignore_flags_(true) {
+}
+
+void SyzyAsanMemoryAccessorTester::Initialize() {
+ ::memset(&context_before_hook_, 0xCD, sizeof(context_before_hook_));
+ ::memset(&context_after_hook_, 0xCE, sizeof(context_after_hook_));
+ MemoryAccessorTester::Initialize();
+}
+
+void SyzyAsanMemoryAccessorTester::AssertMemoryErrorIsDetected(
+ FARPROC access_fn,
+ void* ptr,
+ BadAccessKind bad_access_type) {
+ expected_error_type_ = bad_access_type;
+ CheckAccessAndCompareContexts(access_fn, ptr);
+ ASSERT_TRUE(memory_error_detected_);
+}
+
namespace {
void CheckAccessAndCaptureContexts(
@@ -599,8 +669,9 @@
} // namespace
-void MemoryAccessorTester::CheckAccessAndCompareContexts(
- FARPROC access_fn, void* ptr) {
+void SyzyAsanMemoryAccessorTester::CheckAccessAndCompareContexts(
+ FARPROC access_fn,
+ void* ptr) {
memory_error_detected_ = false;
check_access_fn = access_fn;
@@ -608,13 +679,9 @@
CheckAccessAndCaptureContexts(
&context_before_hook_, &context_after_hook_, ptr);
- ExpectEqualContexts(context_before_hook_,
- context_after_hook_,
- ignore_flags_);
+ ExpectEqualContexts(context_before_hook_, context_after_hook_, ignore_flags_);
if (memory_error_detected_) {
- ExpectEqualContexts(context_before_hook_,
- error_context_,
- ignore_flags_);
+ ExpectEqualContexts(context_before_hook_, error_context_, ignore_flags_);
}
check_access_fn = NULL;
@@ -660,9 +727,12 @@
} // namespace
-void MemoryAccessorTester::CheckSpecialAccessAndCompareContexts(
- FARPROC access_fn, StringOperationDirection direction,
- void* dst, void* src, int len) {
+void SyzyAsanMemoryAccessorTester::CheckSpecialAccessAndCompareContexts(
+ FARPROC access_fn,
+ StringOperationDirection direction,
+ void* dst,
+ void* src,
+ int len) {
memory_error_detected_ = false;
direction_flag_forward = (direction == DIRECTION_FORWARD);
@@ -671,97 +741,15 @@
CheckSpecialAccess(
&context_before_hook_, &context_after_hook_, dst, src, len);
- ExpectEqualContexts(context_before_hook_,
- context_after_hook_,
- ignore_flags_);
+ ExpectEqualContexts(context_before_hook_, context_after_hook_, ignore_flags_);
if (memory_error_detected_) {
- ExpectEqualContexts(context_before_hook_,
- error_context_,
- ignore_flags_);
+ ExpectEqualContexts(context_before_hook_, error_context_, ignore_flags_);
}
check_access_fn = NULL;
}
-#endif
-void MemoryAccessorTester::AsanErrorCallbackImpl(AsanErrorInfo* error_info) {
- // TODO(sebmarchand): Stash the error info in a fixture-static variable and
- // assert on specific conditions after the fact.
- EXPECT_NE(reinterpret_cast<AsanErrorInfo*>(NULL), error_info);
- EXPECT_NE(agent::asan::UNKNOWN_BAD_ACCESS, error_info->error_type);
-
- EXPECT_EQ(expected_error_type_, error_info->error_type);
- if (error_info->error_type >= agent::asan::USE_AFTER_FREE) {
- // We should at least have the stack trace of the allocation of this block.
- EXPECT_GT(error_info->block_info.alloc_stack_size, 0U);
- EXPECT_NE(0U, error_info->block_info.alloc_tid);
- if (error_info->error_type == agent::asan::USE_AFTER_FREE ||
- error_info->error_type == agent::asan::DOUBLE_FREE) {
- EXPECT_GT(error_info->block_info.free_stack_size, 0U);
- EXPECT_NE(0U, error_info->block_info.free_tid);
- } else {
- EXPECT_EQ(error_info->block_info.free_stack_size, 0U);
- EXPECT_EQ(0U, error_info->block_info.free_tid);
- }
- }
-
- if (error_info->error_type == agent::asan::HEAP_BUFFER_OVERFLOW) {
- EXPECT_TRUE(strstr(error_info->shadow_info, "beyond") != NULL);
- } else if (error_info->error_type == agent::asan::HEAP_BUFFER_UNDERFLOW) {
- EXPECT_TRUE(strstr(error_info->shadow_info, "before") != NULL);
- }
-
- memory_error_detected_ = true;
- last_error_info_ = *error_info;
-
- // Copy the corrupt range's information.
- if (error_info->heap_is_corrupt) {
- EXPECT_GE(1U, error_info->corrupt_range_count);
- for (size_t i = 0; i < error_info->corrupt_range_count; ++i) {
- last_corrupt_ranges_.push_back(CorruptRangeInfo());
- CorruptRangeInfo* range_info = &last_corrupt_ranges_.back();
- range_info->first = error_info->corrupt_ranges[i];
- AsanBlockInfoVector* block_infos = &range_info->second;
- for (size_t j = 0; j < range_info->first.block_info_count; ++j) {
- agent::asan::AsanBlockInfo block_info = range_info->first.block_info[j];
- for (size_t k = 0;
- k < range_info->first.block_info[j].alloc_stack_size;
- ++k) {
- block_info.alloc_stack[k] =
- range_info->first.block_info[j].alloc_stack[k];
- }
- for (size_t k = 0;
- k < range_info->first.block_info[j].free_stack_size;
- ++k) {
- block_info.free_stack[k] =
- range_info->first.block_info[j].free_stack[k];
- }
- block_infos->push_back(block_info);
- }
- }
- }
-
- error_context_ = error_info->context;
-}
-
-void MemoryAccessorTester::AsanErrorCallback(AsanErrorInfo* error_info) {
- ASSERT_NE(reinterpret_cast<MemoryAccessorTester*>(NULL), instance_);
-
- instance_->AsanErrorCallbackImpl(error_info);
-}
-
-void MemoryAccessorTester::AssertMemoryErrorIsDetected(
- FARPROC access_fn, void* ptr, BadAccessKind bad_access_type) {
- expected_error_type_ = bad_access_type;
-#ifndef _WIN64
- CheckAccessAndCompareContexts(access_fn, ptr);
-#else
- reinterpret_cast<void(*)(const void*)>(access_fn)(ptr);
-#endif
- ASSERT_TRUE(memory_error_detected_);
-}
-
-void MemoryAccessorTester::ExpectSpecialMemoryErrorIsDetected(
+void SyzyAsanMemoryAccessorTester::ExpectSpecialMemoryErrorIsDetected(
FARPROC access_fn,
StringOperationDirection direction,
bool expect_error,
@@ -775,16 +763,31 @@
expected_error_type_ = bad_access_type;
-#ifndef _WIN64
// Perform memory accesses inside the range.
ASSERT_NO_FATAL_FAILURE(
CheckSpecialAccessAndCompareContexts(
access_fn, direction, dst, src, length));
- #endif
EXPECT_EQ(expect_error, memory_error_detected_);
check_access_fn = NULL;
}
+#endif
+
+void ClangMemoryAccessorTester::CheckAccess(FARPROC access_fn, void* ptr) {
+ memory_error_detected_ = false;
+ check_access_fn = access_fn;
+ reinterpret_cast<void (*)(const void*)>(access_fn)(ptr);
+ check_access_fn = NULL;
+}
+
+void ClangMemoryAccessorTester::AssertMemoryErrorIsDetected(
+ FARPROC access_fn,
+ void* ptr,
+ BadAccessKind bad_access_type) {
+ expected_error_type_ = bad_access_type;
+ reinterpret_cast<void (*)(const void*)>(access_fn)(ptr);
+ ASSERT_TRUE(memory_error_detected_);
+}
TestMemoryInterceptors::TestMemoryInterceptors()
: heap_(NULL), src_(NULL), dst_(NULL) {
@@ -839,7 +842,7 @@
for (size_t i = 0; i < num_fns; ++i) {
const InterceptFunction& fn = fns[i];
- MemoryAccessorTester tester;
+ SyzyAsanMemoryAccessorTester tester;
tester.CheckAccessAndCompareContexts(
reinterpret_cast<FARPROC>(fn.function), src_);
@@ -852,25 +855,24 @@
for (size_t i = 0; i < num_fns; ++i) {
const InterceptFunction& fn = fns[i];
- MemoryAccessorTester tester(MemoryAccessorTester::IGNORE_FLAGS);
+ SyzyAsanMemoryAccessorTester tester(
+ SyzyAsanMemoryAccessorTester::IGNORE_FLAGS);
tester.CheckAccessAndCompareContexts(
reinterpret_cast<FARPROC>(fn.function), src_);
ASSERT_FALSE(tester.memory_error_detected());
}
}
-#endif
void TestMemoryInterceptors::TestOverrunAccess(
const InterceptFunction* fns, size_t num_fns) {
for (size_t i = 0; i < num_fns; ++i) {
const InterceptFunction& fn = fns[i];
- MemoryAccessorTester tester;
+ SyzyAsanMemoryAccessorTester tester;
tester.AssertMemoryErrorIsDetected(
- reinterpret_cast<FARPROC>(fn.function),
- src_ + kAllocSize,
- MemoryAccessorTester::BadAccessKind::HEAP_BUFFER_OVERFLOW);
+ reinterpret_cast<FARPROC>(fn.function), src_ + kAllocSize,
+ SyzyAsanMemoryAccessorTester::BadAccessKind::HEAP_BUFFER_OVERFLOW);
ASSERT_TRUE(tester.memory_error_detected());
}
@@ -881,7 +883,8 @@
for (size_t i = 0; i < num_fns; ++i) {
const InterceptFunction& fn = fns[i];
- MemoryAccessorTester tester(MemoryAccessorTester::IGNORE_FLAGS);
+ SyzyAsanMemoryAccessorTester tester(
+ SyzyAsanMemoryAccessorTester::IGNORE_FLAGS);
tester.AssertMemoryErrorIsDetected(
reinterpret_cast<FARPROC>(fn.function),
src_ + kAllocSize,
@@ -900,7 +903,7 @@
// underrun. I guess the checkers test a single shadow byte at most
// whereas it'd be more correct for access checkers to test as many
// shadow bytes as is appropriate for the range of memory they touch.
- MemoryAccessorTester tester;
+ SyzyAsanMemoryAccessorTester tester;
tester.AssertMemoryErrorIsDetected(
reinterpret_cast<FARPROC>(fn.function),
src_ - 8,
@@ -919,7 +922,8 @@
// underrun. I guess the checkers test a single shadow byte at most
// whereas it'd be more correct for access checkers to test as many
// shadow bytes as is appropriate for the range of memory they touch.
- MemoryAccessorTester tester(MemoryAccessorTester::IGNORE_FLAGS);
+ SyzyAsanMemoryAccessorTester tester(
+ SyzyAsanMemoryAccessorTester::IGNORE_FLAGS);
tester.AssertMemoryErrorIsDetected(
reinterpret_cast<FARPROC>(fn.function),
src_ - 8,
@@ -929,36 +933,34 @@
}
}
-#ifndef _WIN64
void TestMemoryInterceptors::TestStringValidAccess(
const StringInterceptFunction* fns, size_t num_fns) {
for (size_t i = 0; i < num_fns; ++i) {
const StringInterceptFunction& fn = fns[i];
- MemoryAccessorTester tester;
+ SyzyAsanMemoryAccessorTester tester;
tester.CheckSpecialAccessAndCompareContexts(
reinterpret_cast<FARPROC>(fn.function),
- MemoryAccessorTester::DIRECTION_FORWARD,
- dst_, src_, static_cast<int>(kAllocSize / fn.size));
+ SyzyAsanMemoryAccessorTester::DIRECTION_FORWARD, dst_, src_,
+ static_cast<int>(kAllocSize / fn.size));
ASSERT_FALSE(tester.memory_error_detected());
tester.CheckSpecialAccessAndCompareContexts(
reinterpret_cast<FARPROC>(fn.function),
- MemoryAccessorTester::DIRECTION_BACKWARD,
+ SyzyAsanMemoryAccessorTester::DIRECTION_BACKWARD,
dst_ + kAllocSize - fn.size, src_ + kAllocSize - fn.size,
static_cast<int>(kAllocSize / fn.size));
ASSERT_FALSE(tester.memory_error_detected());
}
}
-#endif
void TestMemoryInterceptors::TestStringOverrunAccess(
const StringInterceptFunction* fns, size_t num_fns) {
for (size_t i = 0; i < num_fns; ++i) {
const StringInterceptFunction& fn = fns[i];
- MemoryAccessorTester tester;
+ SyzyAsanMemoryAccessorTester tester;
size_t oob_len = 0;
byte* oob_dst = NULL;
byte* oob_src = NULL;
@@ -981,16 +983,16 @@
// Overflow on dst forwards.
tester.ExpectSpecialMemoryErrorIsDetected(
reinterpret_cast<FARPROC>(fn.function),
- MemoryAccessorTester::DIRECTION_FORWARD, true,
- oob_dst, src_, static_cast<int>(oob_len),
+ SyzyAsanMemoryAccessorTester::DIRECTION_FORWARD, true, oob_dst, src_,
+ static_cast<int>(oob_len),
MemoryAccessorTester::BadAccessKind::HEAP_BUFFER_OVERFLOW);
if (fn.src_access_mode != agent::asan::ASAN_UNKNOWN_ACCESS) {
// Overflow on src forwards.
tester.ExpectSpecialMemoryErrorIsDetected(
reinterpret_cast<FARPROC>(fn.function),
- MemoryAccessorTester::DIRECTION_FORWARD, true,
- dst_, oob_src, static_cast<int>(oob_len),
+ SyzyAsanMemoryAccessorTester::DIRECTION_FORWARD, true, dst_, oob_src,
+ static_cast<int>(oob_len),
MemoryAccessorTester::BadAccessKind::HEAP_BUFFER_OVERFLOW);
}
@@ -1008,20 +1010,63 @@
// Overflow on dst backwards.
tester.ExpectSpecialMemoryErrorIsDetected(
reinterpret_cast<FARPROC>(fn.function),
- MemoryAccessorTester::DIRECTION_BACKWARD, true,
- oob_dst, src_ + kAllocSize - fn.size, static_cast<int>(oob_len),
+ SyzyAsanMemoryAccessorTester::DIRECTION_BACKWARD, true, oob_dst,
+ src_ + kAllocSize - fn.size, static_cast<int>(oob_len),
MemoryAccessorTester::BadAccessKind::HEAP_BUFFER_OVERFLOW);
if (fn.src_access_mode != agent::asan::ASAN_UNKNOWN_ACCESS) {
// Overflow on src backwards.
tester.ExpectSpecialMemoryErrorIsDetected(
reinterpret_cast<FARPROC>(fn.function),
- MemoryAccessorTester::DIRECTION_BACKWARD, true,
+ SyzyAsanMemoryAccessorTester::DIRECTION_BACKWARD, true,
dst_ + kAllocSize - fn.size, oob_dst, static_cast<int>(oob_len),
MemoryAccessorTester::BadAccessKind::HEAP_BUFFER_OVERFLOW);
}
}
}
+#endif
+
+void TestMemoryInterceptors::TestClangValidAccess(
+ const ClangInterceptFunction* fns,
+ size_t num_fns) {
+ for (size_t i = 0; i < num_fns; ++i) {
+ const ClangInterceptFunction& fn = fns[i];
+
+ ClangMemoryAccessorTester tester;
+ tester.CheckAccess(reinterpret_cast<FARPROC>(fn.function), src_);
+
+ ASSERT_FALSE(tester.memory_error_detected());
+ }
+}
+
+void TestMemoryInterceptors::TestClangOverrunAccess(
+ const ClangInterceptFunction* fns,
+ size_t num_fns) {
+ for (size_t i = 0; i < num_fns; ++i) {
+ const ClangInterceptFunction& fn = fns[i];
+
+ ClangMemoryAccessorTester tester;
+ tester.AssertMemoryErrorIsDetected(
+ reinterpret_cast<FARPROC>(fn.function), src_ + kAllocSize,
+ MemoryAccessorTester::BadAccessKind::HEAP_BUFFER_OVERFLOW);
+
+ ASSERT_TRUE(tester.memory_error_detected());
+ }
+}
+
+void TestMemoryInterceptors::TestClangUnderrunAccess(
+ const ClangInterceptFunction* fns,
+ size_t num_fns) {
+ for (size_t i = 0; i < num_fns; ++i) {
+ const ClangInterceptFunction& fn = fns[i];
+ ClangMemoryAccessorTester tester;
+ tester.AssertMemoryErrorIsDetected(
+ reinterpret_cast<FARPROC>(fn.function), src_ - 8,
+ MemoryAccessorTester::BadAccessKind::HEAP_BUFFER_UNDERFLOW);
+
+ ASSERT_TRUE(tester.memory_error_detected());
+ }
+}
bool IsAccessible(void* address) {
return testing::TestAccess(address, false);
diff --git a/syzygy/agent/asan/unittest_util.h b/syzygy/agent/asan/unittest_util.h
index 4348724..e7bf862 100644
--- a/syzygy/agent/asan/unittest_util.h
+++ b/syzygy/agent/asan/unittest_util.h
@@ -595,51 +595,24 @@
CorruptRangeInfo;
typedef std::vector<CorruptRangeInfo> CorruptRangeVector;
-// A helper for testing SyzyAsan memory accessor instrumentation functions.
+// A helper for testing the memory accessor instrumentation functions.
+//
+// This is an abstract class that should be overridden for each types
+// of probes (with a different calling convention).
class MemoryAccessorTester {
public:
typedef agent::asan::BadAccessKind BadAccessKind;
- enum IgnoreFlags {
- IGNORE_FLAGS
- };
MemoryAccessorTester();
- explicit MemoryAccessorTester(IgnoreFlags ignore_flags);
- ~MemoryAccessorTester();
+ virtual ~MemoryAccessorTester();
-#ifndef _WIN64
- // Checks that @p access_fn doesn't raise exceptions on access checking
- // @p ptr, and that @p access_fn doesn't modify any registers or flags
- // when executed.
- void CheckAccessAndCompareContexts(FARPROC access_fn, void* ptr);
-#endif
+ // Call |access_fn| to test an access on |ptr| and make sure that an invalid
+ // access of type |bad_access_kind| is detected.
+ virtual void AssertMemoryErrorIsDetected(FARPROC access_fn,
+ void* ptr,
+ BadAccessKind bad_access_type) = 0;
- // Checks that @p access_fn generates @p bad_access_type on checking @p ptr.
- void AssertMemoryErrorIsDetected(
- FARPROC access_fn, void* ptr, BadAccessKind bad_access_type);
-
- enum StringOperationDirection {
- DIRECTION_FORWARD,
- DIRECTION_BACKWARD
- };
-#ifndef _WIN64
- // Checks that @p access_fn doesn't raise exceptions on access checking
- // for a given @p direction, @p src, @p dst and @p len.
- void CheckSpecialAccessAndCompareContexts(
- FARPROC access_fn, StringOperationDirection direction,
- void* dst, void* src, int len);
-#endif
-
- // Checks that @p access_fn generates @p bad_access_type on access checking
- // for a given @p direction, @p src, @p dst and @p len.
- void ExpectSpecialMemoryErrorIsDetected(FARPROC access_fn,
- StringOperationDirection direction,
- bool expect_error,
- void* dst,
- void* src,
- int32_t length,
- BadAccessKind bad_access_type);
-
+ // The callback used to report the errors.
static void AsanErrorCallback(AsanErrorInfo* error_info);
void set_expected_error_type(BadAccessKind expected) {
@@ -655,8 +628,8 @@
return last_corrupt_ranges_;
}
- private:
- void Initialize();
+ protected:
+ virtual void Initialize();
void AsanErrorCallbackImpl(AsanErrorInfo* error_info);
// This will be used in the asan callback to ensure that we detect the right
@@ -666,12 +639,6 @@
// detected.
bool memory_error_detected_;
- // Indicates whether to ignore changes to the flags register.
- bool ignore_flags_;
-
- // The pre- and post-invocation contexts.
- CONTEXT context_before_hook_;
- CONTEXT context_after_hook_;
// Context captured on error.
CONTEXT error_context_;
@@ -679,10 +646,81 @@
AsanErrorInfo last_error_info_;
CorruptRangeVector last_corrupt_ranges_;
- // There shall be only one!
+ // Prevent from instantiating several instances of this class at the same
+ // time as the instance gets used as a callback by the runtime.
static MemoryAccessorTester* instance_;
};
+#ifndef _WIN64
+// Specialization of a |MemoryAccessorTester| for the probes with the SyzyAsan
+// custom calling convention.
+class SyzyAsanMemoryAccessorTester : public MemoryAccessorTester {
+ public:
+ enum IgnoreFlags {
+ IGNORE_FLAGS
+ };
+
+ SyzyAsanMemoryAccessorTester();
+ explicit SyzyAsanMemoryAccessorTester(IgnoreFlags ignore_flags);
+ virtual ~SyzyAsanMemoryAccessorTester() {}
+
+ // Checks that @p access_fn doesn't raise exceptions on access checking
+ // @p ptr, and that @p access_fn doesn't modify any registers or flags
+ // when executed.
+ void CheckAccessAndCompareContexts(FARPROC access_fn, void* ptr);
+
+ // Checks that @p access_fn generates @p bad_access_type on checking @p ptr.
+ void AssertMemoryErrorIsDetected(FARPROC access_fn,
+ void* ptr,
+ BadAccessKind bad_access_type) override;
+
+ enum StringOperationDirection {
+ DIRECTION_FORWARD,
+ DIRECTION_BACKWARD
+ };
+
+ // Checks that @p access_fn doesn't raise exceptions on access checking
+ // for a given @p direction, @p src, @p dst and @p len.
+ void CheckSpecialAccessAndCompareContexts(
+ FARPROC access_fn, StringOperationDirection direction,
+ void* dst, void* src, int len);
+
+ // Checks that @p access_fn generates @p bad_access_type on access checking
+ // for a given @p direction, @p src, @p dst and @p len.
+ void ExpectSpecialMemoryErrorIsDetected(FARPROC access_fn,
+ StringOperationDirection direction,
+ bool expect_error,
+ void* dst,
+ void* src,
+ int32_t length,
+ BadAccessKind bad_access_type);
+
+ protected:
+ void Initialize() override;
+
+ // Indicates whether to ignore changes to the flags register.
+ bool ignore_flags_;
+
+ // The pre- and post-invocation contexts.
+ CONTEXT context_before_hook_;
+ CONTEXT context_after_hook_;
+};
+#endif
+
+// Specialization of a |MemoryAccessorTester| for the probes with the Clang
+// calling convention (cdecl).
+class ClangMemoryAccessorTester : public MemoryAccessorTester {
+ public:
+ ClangMemoryAccessorTester() {}
+ virtual ~ClangMemoryAccessorTester() {}
+
+ void AssertMemoryErrorIsDetected(FARPROC access_fn,
+ void* ptr,
+ BadAccessKind bad_access_type) override;
+
+ void CheckAccess(FARPROC access_fn, void* ptr);
+};
+
// A fixture class for testing memory interceptors.
class TestMemoryInterceptors : public TestWithAsanLogger {
public:
@@ -698,6 +736,11 @@
size_t size;
};
+ struct ClangInterceptFunction {
+ void (*function)(const void*);
+ size_t size;
+ };
+
struct StringInterceptFunction {
void(*function)();
size_t size;
@@ -713,29 +756,40 @@
void SetUp() override;
void TearDown() override;
+ template <size_t N_1, size_t N_2>
+ void TestValidAccess(const InterceptFunction(&fns)[N_1],
+ const ClangInterceptFunction(&clang_fns)[N_2]) {
#ifndef _WIN64
- template <size_t N>
- void TestValidAccess(const InterceptFunction (&fns)[N]) {
- TestValidAccess(fns, N);
- }
+ TestValidAccess(fns, N_1);
#endif
+ TestClangValidAccess(clang_fns, N_2);
+ }
+ template <size_t N_1, size_t N_2>
+ void TestOverrunAccess(const InterceptFunction(&fns)[N_1],
+ const ClangInterceptFunction(&clang_fns)[N_2]) {
+#ifndef _WIN64
+ TestOverrunAccess(fns, N_2);
+#endif
+ TestClangOverrunAccess(clang_fns, N_2);
+ }
+ template <size_t N_1, size_t N_2>
+ void TestUnderrunAccess(const InterceptFunction(&fns)[N_1],
+ const ClangInterceptFunction(&clang_fns)[N_2]) {
+#ifndef _WIN64
+ TestUnderrunAccess(fns, N_1);
+#endif
+ TestClangUnderrunAccess(clang_fns, N_2);
+ }
+#ifndef _WIN64
template <size_t N>
void TestValidAccessIgnoreFlags(const InterceptFunction (&fns)[N]) {
TestValidAccessIgnoreFlags(fns, N);
}
template <size_t N>
- void TestOverrunAccess(const InterceptFunction (&fns)[N]) {
- TestOverrunAccess(fns, N);
- }
- template <size_t N>
void TestOverrunAccessIgnoreFlags(const InterceptFunction (&fns)[N]) {
TestOverrunAccessIgnoreFlags(fns, N);
}
template <size_t N>
- void TestUnderrunAccess(const InterceptFunction (&fns)[N]) {
- TestUnderrunAccess(fns, N);
- }
- template <size_t N>
void TestUnderrunAccessIgnoreFlags(const InterceptFunction (&fns)[N]) {
TestUnderrunAccessIgnoreFlags(fns, N);
}
@@ -747,13 +801,13 @@
void TestStringOverrunAccess(const StringInterceptFunction (&fns)[N]) {
TestStringOverrunAccess(fns, N);
}
+#endif
protected:
#ifndef _WIN64
void TestValidAccess(const InterceptFunction* fns, size_t num_fns);
void TestValidAccessIgnoreFlags(const InterceptFunction* fns,
size_t num_fns);
-#endif
void TestOverrunAccess(const InterceptFunction* fns, size_t num_fns);
void TestOverrunAccessIgnoreFlags(const InterceptFunction* fns,
size_t num_fns);
@@ -764,6 +818,12 @@
const StringInterceptFunction* fns, size_t num_fns);
void TestStringOverrunAccess(
const StringInterceptFunction* fns, size_t num_fns);
+#endif
+ void TestClangValidAccess(const ClangInterceptFunction* fns, size_t num_fns);
+ void TestClangOverrunAccess(const ClangInterceptFunction* fns,
+ size_t num_fns);
+ void TestClangUnderrunAccess(const ClangInterceptFunction* fns,
+ size_t num_fns);
const size_t kAllocSize = 64;