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;