[compiler-rt] 481e9b3 - [asan][win][msvc] override new and delete and seperate TUs (#68754)

via llvm-commits llvm-commits at lists.llvm.org
Fri Dec 1 11:56:51 PST 2023


Author: Charlie Barto
Date: 2023-12-01T11:56:44-08:00
New Revision: 481e9b3e0b9c0a6843261f060822c7a41387e28c

URL: https://github.com/llvm/llvm-project/commit/481e9b3e0b9c0a6843261f060822c7a41387e28c
DIFF: https://github.com/llvm/llvm-project/commit/481e9b3e0b9c0a6843261f060822c7a41387e28c.diff

LOG: [asan][win][msvc] override new and delete and seperate TUs (#68754)

Migrated from: https://reviews.llvm.org/D155879, with some of the
suggestions applied.

PR Description copied from above:

Currently asan simply exports each overridden new/delete function from
the DLL, this works fine normally, but fails if the user is overriding
some, but not all, of these functions. In this case the non-overridden
functions still come from the asan DLL, but they can't correctly call
the user provided override (for example sized op delete should fall back
to scalar op delete, if a scalar op delete is provided). Things were
also broken in the static build because all the asan overrides were
exported from the same TU, and so if you overrode one but not all of
them then you'd get ODR violations. This PR should fix both of these
cases, but the static case isn't really tested (and indeed one such test
does fail) because linking asan statically basically doesn't work on
windows right now with LLVM's version of asan. In fact, while we did fix
this in our fork, it was a huge mess and we've now made the dynamic
version work in all situations (/MD, /MT, /MDd, /MTd, etc) instead.

The following is the description from the internal PR that implemented
most of this feature.

> Previously, operator new/delete were provided as DLL exports when
linking dynamically and wholearchived when linked statically. Both
scenarios were broken. When linking statically, the user could not
define their own op new/delete, because they were already brought into
the link by ASAN. When dynamically linking, if the user provided some
but not all of the overloads, new and delete would be partially hooked.
For example, if the user defined scalar op delete, but the program then
called sized op delete, the sized op delete would still be the version
provided by ASAN instead of falling back to the user-defined scalar op
delete, like the standard requires.

> The change <internal PR number>: ASAN operator new/delete fallbacks in
the ASAN libraries fixes this moving all operator new/delete definitions
to be statically linked. However, this still won't work if
/InferAsanLibs still whole-archives everything since then all the op
new/deletes would always be provided by ASAN, which is why these changes
are necessary.

> With these changes, we will no longer wholearchive all of ASAN and
will leave the c++ parts (the op new/delete definitions) to be included
as a default library. However, it is also necessary to ensure that the
asan library with op new/delete will be searched before the
corresponding CRT library with the same op new/delete definitions. To
accomplish this, we make sure to add the asan library to the beginning
of the default lib list, or move it explicitly to the front if it's
already in the list. If the C runtime library is explicitly provided, we
make sure to warn the user if the current linker line will result in
operator new/delete not being provided by ASAN.

Note that the rearrangement of defaultlibs is not in this diff.

---------

Co-authored-by: Charlie Barto <Charles.Barto at microsoft.com>

Added: 
    compiler-rt/lib/asan/asan_win_delete_array_align_nothrow_thunk.cpp
    compiler-rt/lib/asan/asan_win_delete_array_align_thunk.cpp
    compiler-rt/lib/asan/asan_win_delete_array_nothrow_thunk.cpp
    compiler-rt/lib/asan/asan_win_delete_array_size_align_thunk.cpp
    compiler-rt/lib/asan/asan_win_delete_array_size_thunk.cpp
    compiler-rt/lib/asan/asan_win_delete_array_thunk.cpp
    compiler-rt/lib/asan/asan_win_delete_scalar_align_nothrow_thunk.cpp
    compiler-rt/lib/asan/asan_win_delete_scalar_align_thunk.cpp
    compiler-rt/lib/asan/asan_win_delete_scalar_nothrow_thunk.cpp
    compiler-rt/lib/asan/asan_win_delete_scalar_size_align_thunk.cpp
    compiler-rt/lib/asan/asan_win_delete_scalar_size_thunk.cpp
    compiler-rt/lib/asan/asan_win_delete_scalar_thunk.cpp
    compiler-rt/lib/asan/asan_win_new_array_align_nothrow_thunk.cpp
    compiler-rt/lib/asan/asan_win_new_array_align_thunk.cpp
    compiler-rt/lib/asan/asan_win_new_array_nothrow_thunk.cpp
    compiler-rt/lib/asan/asan_win_new_array_thunk.cpp
    compiler-rt/lib/asan/asan_win_new_delete.cpp
    compiler-rt/lib/asan/asan_win_new_delete_thunk_common.h
    compiler-rt/lib/asan/asan_win_new_scalar_align_nothrow_thunk.cpp
    compiler-rt/lib/asan/asan_win_new_scalar_align_thunk.cpp
    compiler-rt/lib/asan/asan_win_new_scalar_nothrow_thunk.cpp
    compiler-rt/lib/asan/asan_win_new_scalar_thunk.cpp
    compiler-rt/test/asan/TestCases/Windows/new_delete_mfc_already_defined.cpp
    compiler-rt/test/asan/TestCases/Windows/new_delete_mfc_already_defined_dbg.cpp
    compiler-rt/test/asan/TestCases/Windows/operator_delete_replacement_array.cpp
    compiler-rt/test/asan/TestCases/Windows/operator_delete_replacement_scalar.cpp
    compiler-rt/test/asan/TestCases/Windows/operator_new_delete_replacement_all.cpp
    compiler-rt/test/asan/TestCases/Windows/operator_new_delete_replacement_common.h
    compiler-rt/test/asan/TestCases/Windows/operator_new_delete_replacement_macros.h
    compiler-rt/test/asan/TestCases/Windows/operator_new_replacement_array.cpp
    compiler-rt/test/asan/TestCases/Windows/operator_new_replacement_scalar.cpp

Modified: 
    compiler-rt/lib/asan/CMakeLists.txt
    compiler-rt/lib/asan/asan_interface.inc
    compiler-rt/test/asan/TestCases/Windows/double_operator_delete.cpp
    compiler-rt/test/asan/TestCases/Windows/operator_array_new_left_oob.cpp
    compiler-rt/test/asan/TestCases/Windows/operator_array_new_right_oob.cpp
    compiler-rt/test/asan/TestCases/Windows/operator_array_new_uaf.cpp
    compiler-rt/test/asan/TestCases/Windows/operator_array_new_with_dtor_left_oob.cpp
    compiler-rt/test/asan/TestCases/Windows/operator_delete_wrong_argument.cpp
    compiler-rt/test/asan/TestCases/Windows/operator_new_left_oob.cpp
    compiler-rt/test/asan/TestCases/Windows/operator_new_right_oob.cpp
    compiler-rt/test/asan/TestCases/Windows/operator_new_uaf.cpp
    compiler-rt/test/asan/TestCases/Windows/wrong_downcast_on_heap.cpp
    compiler-rt/test/asan/TestCases/large_func_test.cpp
    compiler-rt/test/asan/TestCases/malloc_context_size.cpp
    compiler-rt/test/asan/TestCases/use-after-delete.cpp

Removed: 
    


################################################################################
diff  --git a/compiler-rt/lib/asan/CMakeLists.txt b/compiler-rt/lib/asan/CMakeLists.txt
index f993521d3ca88be..b8bccbfcde38907 100644
--- a/compiler-rt/lib/asan/CMakeLists.txt
+++ b/compiler-rt/lib/asan/CMakeLists.txt
@@ -38,9 +38,40 @@ if (NOT WIN32 AND NOT APPLE)
     )
 endif()
 
-set(ASAN_CXX_SOURCES
-  asan_new_delete.cpp
-  )
+if (WIN32)
+  set(ASAN_CXX_SOURCES asan_win_new_delete.cpp)
+else()
+  set(ASAN_CXX_SOURCES asan_new_delete.cpp)
+endif()
+
+if (APPLE)
+  set(ASAN_SOURCES ASAN_CXX_SOURCES)
+endif()
+
+if (WIN32)
+  set(ASAN_STATIC_IMPLIB_SOURCES
+    asan_win_delete_array_thunk.cpp
+    asan_win_delete_array_align_thunk.cpp
+    asan_win_delete_array_align_nothrow_thunk.cpp
+    asan_win_delete_array_nothrow_thunk.cpp
+    asan_win_delete_array_size_thunk.cpp
+    asan_win_delete_array_size_align_thunk.cpp
+    asan_win_delete_scalar_thunk.cpp
+    asan_win_delete_scalar_align_thunk.cpp
+    asan_win_delete_scalar_align_nothrow_thunk.cpp
+    asan_win_delete_scalar_nothrow_thunk.cpp
+    asan_win_delete_scalar_size_thunk.cpp
+    asan_win_delete_scalar_size_align_thunk.cpp
+    asan_win_new_array_thunk.cpp
+    asan_win_new_array_align_thunk.cpp
+    asan_win_new_array_align_nothrow_thunk.cpp
+    asan_win_new_array_nothrow_thunk.cpp
+    asan_win_new_scalar_thunk.cpp
+    asan_win_new_scalar_align_thunk.cpp
+    asan_win_new_scalar_align_nothrow_thunk.cpp
+    asan_win_new_scalar_nothrow_thunk.cpp
+    )
+endif()
 
 set(ASAN_STATIC_SOURCES
   asan_rtl_static.cpp
@@ -83,12 +114,20 @@ SET(ASAN_HEADERS
   asan_thread.h
   )
 
+if (WIN32)
+  list(APPEND ASAN_HEADERS
+    asan_win_new_delete_thunk_common.h
+    asan_win_thunk_common.h
+    )
+endif()
+
 include_directories(..)
 
 set(ASAN_CFLAGS ${SANITIZER_COMMON_CFLAGS})
 set(ASAN_COMMON_DEFINITIONS ${COMPILER_RT_ASAN_SHADOW_SCALE_DEFINITION})
 
 append_rtti_flag(OFF ASAN_CFLAGS)
+append_list_if(MSVC /EHsc ASAN_CFLAGS)
 
 # Silence warnings in system headers with MSVC.
 if(NOT CLANG_CL)
@@ -139,6 +178,15 @@ add_compiler_rt_object_libraries(RTAsan_dynamic
   CFLAGS ${ASAN_DYNAMIC_CFLAGS}
   DEFS ${ASAN_DYNAMIC_DEFINITIONS})
 
+if (WIN32)
+  add_compiler_rt_object_libraries(RTAsan_static_implib
+    ARCHS ${ASAN_SUPPORTED_ARCH}
+    SOURCES ${ASAN_STATIC_IMPLIB_SOURCES}
+    ADDITIONAL_HEADERS ${ASAN_HEADERS}
+    CFLAGS ${ASAN_CFLAGS} ${NO_DEFAULT_LIBS_OPTION}
+    DEFS ${ASAN_COMMON_DEFINITIONS})
+endif()
+
 if(NOT APPLE)
   add_compiler_rt_object_libraries(RTAsan
     ARCHS ${ASAN_SUPPORTED_ARCH}
@@ -239,13 +287,24 @@ else()
     DEFS ${ASAN_COMMON_DEFINITIONS}
     PARENT_TARGET asan)
 
+if(WIN32)
   add_compiler_rt_runtime(clang_rt.asan_static
     STATIC
     ARCHS ${ASAN_SUPPORTED_ARCH}
     OBJECT_LIBS RTAsan_static
+      RTAsan_static_implib
     CFLAGS ${ASAN_CFLAGS}
     DEFS ${ASAN_COMMON_DEFINITIONS}
     PARENT_TARGET asan)
+else()
+  add_compiler_rt_runtime(clang_rt.asan_static
+    STATIC
+    ARCHS ${ASAN_SUPPORTED_ARCH}
+    OBJECT_LIBS RTAsan_static
+    CFLAGS ${ASAN_CFLAGS}
+    DEFS ${ASAN_COMMON_DEFINITIONS}
+    PARENT_TARGET asan)
+endif()
 
   add_compiler_rt_runtime(clang_rt.asan-preinit
     STATIC
@@ -255,6 +314,13 @@ else()
     DEFS ${ASAN_COMMON_DEFINITIONS}
     PARENT_TARGET asan)
 
+
+  if (MSVC AND COMPILER_RT_DEBUG)
+    set(MSVC_DBG_SUFFIX _dbg)
+  else()
+    set(MSVC_DBG_SUFFIX )
+  endif()
+
   foreach(arch ${ASAN_SUPPORTED_ARCH})
     if (COMPILER_RT_HAS_VERSION_SCRIPT)
       add_sanitizer_rt_version_list(clang_rt.asan-dynamic-${arch}
@@ -311,6 +377,27 @@ else()
       DEFS ${ASAN_DYNAMIC_DEFINITIONS}
       PARENT_TARGET asan)
 
+    if(WIN32)
+      set_target_properties(clang_rt.asan${MSVC_DBG_SUFFIX}-dynamic-${arch}
+        PROPERTIES ARCHIVE_OUTPUT_NAME clang_rt.asan_dynamic-${arch}_implib
+                  ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
+
+      add_library(clang_rt.asan-${arch}_implib STATIC)
+      target_link_libraries(clang_rt.asan-${arch}_implib RTAsan_static_implib.${arch})
+      add_dependencies(asan clang_rt.asan-${arch}_implib)
+      add_dependencies(clang_rt.asan-${arch}_implib clang_rt.asan${MSVC_DBG_SUFFIX}-dynamic-${arch})
+      get_compiler_rt_output_dir(${arch} IMPLIB_OUTPUT_DIR)
+      set_target_properties(clang_rt.asan-${arch}_implib
+        PROPERTIES ARCHIVE_OUTPUT_NAME clang_rt.asan${MSVC_DBG_SUFFIX}_dynamic-${arch}
+                    ARCHIVE_OUTPUT_DIRECTORY ${IMPLIB_OUTPUT_DIR}
+                    STATIC_LIBRARY_OPTIONS "$<TARGET_LINKER_FILE:clang_rt.asan${MSVC_DBG_SUFFIX}-dynamic-${arch}>")
+      get_compiler_rt_install_dir(${arch} IMPLIB_INSTALL_DIR)
+      install(TARGETS clang_rt.asan-${arch}_implib
+        ARCHIVE DESTINATION ${IMPLIB_INSTALL_DIR}
+        LIBRARY DESTINATION ${IMPLIB_INSTALL_DIR}
+        RUNTIME DESTINATION ${IMPLIB_INSTALL_DIR})
+    endif()
+
     if (SANITIZER_USE_SYMBOLS AND NOT ${arch} STREQUAL "i386")
       add_sanitizer_rt_symbols(clang_rt.asan_cxx
         ARCHS ${arch})

diff  --git a/compiler-rt/lib/asan/asan_interface.inc b/compiler-rt/lib/asan/asan_interface.inc
index bfc44b46196232f..83c0c1cffb85363 100644
--- a/compiler-rt/lib/asan/asan_interface.inc
+++ b/compiler-rt/lib/asan/asan_interface.inc
@@ -187,3 +187,22 @@ INTERFACE_FUNCTION(__asan_update_allocation_context)
 INTERFACE_WEAK_FUNCTION(__asan_default_options)
 INTERFACE_WEAK_FUNCTION(__asan_default_suppressions)
 INTERFACE_WEAK_FUNCTION(__asan_on_error)
+
+#if SANITIZER_WINDOWS
+INTERFACE_FUNCTION(__asan_delete)
+INTERFACE_FUNCTION(__asan_delete_align)
+INTERFACE_FUNCTION(__asan_delete_array)
+INTERFACE_FUNCTION(__asan_delete_array_align)
+INTERFACE_FUNCTION(__asan_delete_array_size)
+INTERFACE_FUNCTION(__asan_delete_array_size_align)
+INTERFACE_FUNCTION(__asan_delete_size)
+INTERFACE_FUNCTION(__asan_delete_size_align)
+INTERFACE_FUNCTION(__asan_new)
+INTERFACE_FUNCTION(__asan_new_align)
+INTERFACE_FUNCTION(__asan_new_align_nothrow)
+INTERFACE_FUNCTION(__asan_new_array)
+INTERFACE_FUNCTION(__asan_new_array_align)
+INTERFACE_FUNCTION(__asan_new_array_align_nothrow)
+INTERFACE_FUNCTION(__asan_new_array_nothrow)
+INTERFACE_FUNCTION(__asan_new_nothrow)
+#endif  // SANITIZER_WINDOWS

diff  --git a/compiler-rt/lib/asan/asan_win_delete_array_align_nothrow_thunk.cpp b/compiler-rt/lib/asan/asan_win_delete_array_align_nothrow_thunk.cpp
new file mode 100644
index 000000000000000..5740854b31d1f83
--- /dev/null
+++ b/compiler-rt/lib/asan/asan_win_delete_array_align_nothrow_thunk.cpp
@@ -0,0 +1,24 @@
+//===-- asan_win_delete_array_align_nothrow_thunk.cc ----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Windows-specific user-provided new/delete operator detection and fallback.
+//===----------------------------------------------------------------------===//
+#include "asan_win_new_delete_thunk_common.h"
+
+// see diagram in asan_win_new_delete_thunk_common.h for the ordering of the
+// new/delete fallbacks.
+
+// Avoid tailcall optimization to preserve stack frame.
+#pragma optimize("", off)
+void operator delete[](void* ptr, std::align_val_t align,
+                       std::nothrow_t const&) noexcept {
+  // nothrow version is identical to throwing version
+  operator delete[](ptr, align);
+}

diff  --git a/compiler-rt/lib/asan/asan_win_delete_array_align_thunk.cpp b/compiler-rt/lib/asan/asan_win_delete_array_align_thunk.cpp
new file mode 100644
index 000000000000000..7e98380cb295194
--- /dev/null
+++ b/compiler-rt/lib/asan/asan_win_delete_array_align_thunk.cpp
@@ -0,0 +1,28 @@
+//===-- asan_win_delete_array_align_thunk.cc ------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Windows-specific user-provided new/delete operator detection and fallback.
+//===----------------------------------------------------------------------===//
+#include "asan_win_new_delete_thunk_common.h"
+
+// see diagram in asan_win_new_delete_thunk_common.h for the ordering of the
+// new/delete fallbacks.
+
+__asan_InitDefine<op_delete_array_align> init_delete_array_align;
+
+// Avoid tailcall optimization to preserve stack frame.
+#pragma optimize("", off)
+void operator delete[](void* ptr, std::align_val_t align) noexcept {
+  if (__asan_InitDefine<op_delete_scalar_align>::defined) {
+    __asan_delete_array_align(ptr, align);
+  } else {
+    operator delete(ptr, align);
+  }
+}

diff  --git a/compiler-rt/lib/asan/asan_win_delete_array_nothrow_thunk.cpp b/compiler-rt/lib/asan/asan_win_delete_array_nothrow_thunk.cpp
new file mode 100644
index 000000000000000..2018adb498bbce6
--- /dev/null
+++ b/compiler-rt/lib/asan/asan_win_delete_array_nothrow_thunk.cpp
@@ -0,0 +1,23 @@
+//===-- asan_win_delete_array_nothrow_thunk.cc ----------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Windows-specific user-provided new/delete operator detection and fallback.
+//===----------------------------------------------------------------------===//
+#include "asan_win_new_delete_thunk_common.h"
+
+// see diagram in asan_win_new_delete_thunk_common.h for the ordering of the
+// new/delete fallbacks.
+
+// Avoid tailcall optimization to preserve stack frame.
+#pragma optimize("", off)
+void operator delete[](void* ptr, std::nothrow_t const&) noexcept {
+  // nothrow version is identical to throwing version
+  operator delete[](ptr);
+}

diff  --git a/compiler-rt/lib/asan/asan_win_delete_array_size_align_thunk.cpp b/compiler-rt/lib/asan/asan_win_delete_array_size_align_thunk.cpp
new file mode 100644
index 000000000000000..ac576a1945bb92d
--- /dev/null
+++ b/compiler-rt/lib/asan/asan_win_delete_array_size_align_thunk.cpp
@@ -0,0 +1,28 @@
+//===-- asan_win_delete_array_size_align_thunk.cc -------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Windows-specific user-provided new/delete operator detection and fallback.
+//===----------------------------------------------------------------------===//
+#include "asan_win_new_delete_thunk_common.h"
+
+// see diagram in asan_win_new_delete_thunk_common.h for the ordering of the
+// new/delete fallbacks.
+
+// Avoid tailcall optimization to preserve stack frame.
+#pragma optimize("", off)
+void operator delete[](void* ptr, size_t size,
+                       std::align_val_t align) noexcept {
+  if (__asan_InitDefine<op_delete_scalar_align>::defined &&
+      __asan_InitDefine<op_delete_array_align>::defined) {
+    __asan_delete_array_size_align(ptr, size, align);
+  } else {
+    operator delete[](ptr, align);
+  }
+}

diff  --git a/compiler-rt/lib/asan/asan_win_delete_array_size_thunk.cpp b/compiler-rt/lib/asan/asan_win_delete_array_size_thunk.cpp
new file mode 100644
index 000000000000000..93c91e4651ab1d5
--- /dev/null
+++ b/compiler-rt/lib/asan/asan_win_delete_array_size_thunk.cpp
@@ -0,0 +1,27 @@
+//===-- asan_win_delete_array_size_thunk.cc -------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Windows-specific user-provided new/delete operator detection and fallback.
+//===----------------------------------------------------------------------===//
+#include "asan_win_new_delete_thunk_common.h"
+
+// see diagram in asan_win_new_delete_thunk_common.h for the ordering of the
+// new/delete fallbacks.
+
+// Avoid tailcall optimization to preserve stack frame.
+#pragma optimize("", off)
+void operator delete[](void* ptr, size_t size) noexcept {
+  if (__asan_InitDefine<op_delete_scalar>::defined &&
+      __asan_InitDefine<op_delete_array>::defined) {
+    __asan_delete_array_size(ptr, size);
+  } else {
+    operator delete[](ptr);
+  }
+}

diff  --git a/compiler-rt/lib/asan/asan_win_delete_array_thunk.cpp b/compiler-rt/lib/asan/asan_win_delete_array_thunk.cpp
new file mode 100644
index 000000000000000..9cdab36c6125e99
--- /dev/null
+++ b/compiler-rt/lib/asan/asan_win_delete_array_thunk.cpp
@@ -0,0 +1,28 @@
+//===-- asan_win_delete_array_thunk.cc ------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Windows-specific user-provided new/delete operator detection and fallback.
+//===----------------------------------------------------------------------===//
+#include "asan_win_new_delete_thunk_common.h"
+
+// see diagram in asan_win_new_delete_thunk_common.h for the ordering of the
+// new/delete fallbacks.
+
+__asan_InitDefine<op_delete_array> init_delete_array;
+
+// Avoid tailcall optimization to preserve stack frame.
+#pragma optimize("", off)
+void operator delete[](void* ptr) noexcept {
+  if (__asan_InitDefine<op_delete_scalar>::defined) {
+    __asan_delete_array(ptr);
+  } else {
+    operator delete(ptr);
+  }
+}

diff  --git a/compiler-rt/lib/asan/asan_win_delete_scalar_align_nothrow_thunk.cpp b/compiler-rt/lib/asan/asan_win_delete_scalar_align_nothrow_thunk.cpp
new file mode 100644
index 000000000000000..35db32976a4fabb
--- /dev/null
+++ b/compiler-rt/lib/asan/asan_win_delete_scalar_align_nothrow_thunk.cpp
@@ -0,0 +1,24 @@
+//===-- asan_win_delete_scalar_align_nothrow_thunk.cc ---------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Windows-specific user-provided new/delete operator detection and fallback.
+//===----------------------------------------------------------------------===//
+#include "asan_win_new_delete_thunk_common.h"
+
+// see diagram in asan_win_new_delete_thunk_common.h for the ordering of the
+// new/delete fallbacks.
+
+// Avoid tailcall optimization to preserve stack frame.
+#pragma optimize("", off)
+void operator delete(void* ptr, std::align_val_t align,
+                     std::nothrow_t const&) noexcept {
+  // nothrow version is identical to throwing version
+  operator delete(ptr, align);
+}

diff  --git a/compiler-rt/lib/asan/asan_win_delete_scalar_align_thunk.cpp b/compiler-rt/lib/asan/asan_win_delete_scalar_align_thunk.cpp
new file mode 100644
index 000000000000000..4d2bd867284e0ad
--- /dev/null
+++ b/compiler-rt/lib/asan/asan_win_delete_scalar_align_thunk.cpp
@@ -0,0 +1,24 @@
+//===-- asan_win_delete_scalar_align_thunk.cc -----------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Windows-specific user-provided new/delete operator detection and fallback.
+//===----------------------------------------------------------------------===//
+#include "asan_win_new_delete_thunk_common.h"
+
+// see diagram in asan_win_new_delete_thunk_common.h for the ordering of the
+// new/delete fallbacks.
+
+__asan_InitDefine<op_delete_scalar_align> init_delete_scalar_align;
+
+// Avoid tailcall optimization to preserve stack frame.
+#pragma optimize("", off)
+void operator delete(void* ptr, std::align_val_t align) noexcept {
+  __asan_delete_align(ptr, align);
+}

diff  --git a/compiler-rt/lib/asan/asan_win_delete_scalar_nothrow_thunk.cpp b/compiler-rt/lib/asan/asan_win_delete_scalar_nothrow_thunk.cpp
new file mode 100644
index 000000000000000..e2b34cca8232dd3
--- /dev/null
+++ b/compiler-rt/lib/asan/asan_win_delete_scalar_nothrow_thunk.cpp
@@ -0,0 +1,23 @@
+//===-- asan_win_delete_scalar_nothrow_thunk.cc ---------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Windows-specific user-provided new/delete operator detection and fallback.
+//===----------------------------------------------------------------------===//
+#include "asan_win_new_delete_thunk_common.h"
+
+// see diagram in asan_win_new_delete_thunk_common.h for the ordering of the
+// new/delete fallbacks.
+
+// Avoid tailcall optimization to preserve stack frame.
+#pragma optimize("", off)
+void operator delete(void* ptr, std::nothrow_t const&) noexcept {
+  // nothrow version is identical to throwing version
+  operator delete(ptr);
+}

diff  --git a/compiler-rt/lib/asan/asan_win_delete_scalar_size_align_thunk.cpp b/compiler-rt/lib/asan/asan_win_delete_scalar_size_align_thunk.cpp
new file mode 100644
index 000000000000000..54cc54009ec2a6a
--- /dev/null
+++ b/compiler-rt/lib/asan/asan_win_delete_scalar_size_align_thunk.cpp
@@ -0,0 +1,26 @@
+//===-- asan_win_delete_scalar_size_align_thunk.cc ------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Windows-specific user-provided new/delete operator detection and fallback.
+//===----------------------------------------------------------------------===//
+#include "asan_win_new_delete_thunk_common.h"
+
+// see diagram in asan_win_new_delete_thunk_common.h for the ordering of the
+// new/delete fallbacks.
+
+// Avoid tailcall optimization to preserve stack frame.
+#pragma optimize("", off)
+void operator delete(void* ptr, size_t size, std::align_val_t align) noexcept {
+  if (__asan_InitDefine<op_delete_scalar_align>::defined) {
+    __asan_delete_size_align(ptr, size, align);
+  } else {
+    operator delete(ptr, align);
+  }
+}

diff  --git a/compiler-rt/lib/asan/asan_win_delete_scalar_size_thunk.cpp b/compiler-rt/lib/asan/asan_win_delete_scalar_size_thunk.cpp
new file mode 100644
index 000000000000000..ba988f39f12c49d
--- /dev/null
+++ b/compiler-rt/lib/asan/asan_win_delete_scalar_size_thunk.cpp
@@ -0,0 +1,26 @@
+//===-- asan_win_delete_scalar_size_thunk.cc ------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Windows-specific user-provided new/delete operator detection and fallback.
+//===----------------------------------------------------------------------===//
+#include "asan_win_new_delete_thunk_common.h"
+
+// see diagram in asan_win_new_delete_thunk_common.h for the ordering of the
+// new/delete fallbacks.
+
+// Avoid tailcall optimization to preserve stack frame.
+#pragma optimize("", off)
+void operator delete(void* ptr, size_t size) noexcept {
+  if (__asan_InitDefine<op_delete_scalar>::defined) {
+    __asan_delete_size(ptr, size);
+  } else {
+    operator delete(ptr);
+  }
+}

diff  --git a/compiler-rt/lib/asan/asan_win_delete_scalar_thunk.cpp b/compiler-rt/lib/asan/asan_win_delete_scalar_thunk.cpp
new file mode 100644
index 000000000000000..1104716fe476e56
--- /dev/null
+++ b/compiler-rt/lib/asan/asan_win_delete_scalar_thunk.cpp
@@ -0,0 +1,22 @@
+//===-- asan_win_delete_scalar_thunk.cc -----------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Windows-specific user-provided new/delete operator detection and fallback.
+//===----------------------------------------------------------------------===//
+#include "asan_win_new_delete_thunk_common.h"
+
+// see diagram in asan_win_new_delete_thunk_common.h for the ordering of the
+// new/delete fallbacks.
+
+__asan_InitDefine<op_delete_scalar> init_delete_scalar;
+
+// Avoid tailcall optimization to preserve stack frame.
+#pragma optimize("", off)
+void operator delete(void* ptr) noexcept { __asan_delete(ptr); }

diff  --git a/compiler-rt/lib/asan/asan_win_new_array_align_nothrow_thunk.cpp b/compiler-rt/lib/asan/asan_win_new_array_align_nothrow_thunk.cpp
new file mode 100644
index 000000000000000..04cc253f7e89b3f
--- /dev/null
+++ b/compiler-rt/lib/asan/asan_win_new_array_align_nothrow_thunk.cpp
@@ -0,0 +1,32 @@
+//===-- asan_win_new_array_align_nothrow_thunk.cc -------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Windows-specific user-provided new/delete operator detection and fallback.
+//===----------------------------------------------------------------------===//
+#include "asan_win_new_delete_thunk_common.h"
+
+// see diagram in asan_win_new_delete_thunk_common.h for the ordering of the
+// new/delete fallbacks.
+
+// Avoid tailcall optimization to preserve stack frame.
+#pragma optimize("", off)
+void* operator new[](size_t size, std::align_val_t align,
+                     std::nothrow_t const&) noexcept {
+  if (__asan_InitDefine<op_new_scalar_align>::defined &&
+      __asan_InitDefine<op_new_array_align>::defined) {
+    return __asan_new_array_align_nothrow(size, align);
+  }
+
+  try {
+    return operator new[](size, align);
+  } catch (...) {
+    return nullptr;
+  }
+}

diff  --git a/compiler-rt/lib/asan/asan_win_new_array_align_thunk.cpp b/compiler-rt/lib/asan/asan_win_new_array_align_thunk.cpp
new file mode 100644
index 000000000000000..92f487565067150
--- /dev/null
+++ b/compiler-rt/lib/asan/asan_win_new_array_align_thunk.cpp
@@ -0,0 +1,28 @@
+//===-- asan_win_new_array_align_thunk.cc ---------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Windows-specific user-provided new/delete operator detection and fallback.
+//===----------------------------------------------------------------------===//
+#include "asan_win_new_delete_thunk_common.h"
+
+// see diagram in asan_win_new_delete_thunk_common.h for the ordering of the
+// new/delete fallbacks.
+
+__asan_InitDefine<op_new_array_align> init_new_array_align;
+
+// Avoid tailcall optimization to preserve stack frame.
+#pragma optimize("", off)
+void* operator new[](size_t size, std::align_val_t align) {
+  if (__asan_InitDefine<op_new_scalar_align>::defined) {
+    return __asan_new_array_align(size, align);
+  }
+
+  return operator new(size, align);
+}

diff  --git a/compiler-rt/lib/asan/asan_win_new_array_nothrow_thunk.cpp b/compiler-rt/lib/asan/asan_win_new_array_nothrow_thunk.cpp
new file mode 100644
index 000000000000000..33da812090725df
--- /dev/null
+++ b/compiler-rt/lib/asan/asan_win_new_array_nothrow_thunk.cpp
@@ -0,0 +1,33 @@
+//===-- asan_win_new_array_nothrow_thunk.cc -------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Windows-specific user-provided new/delete operator detection and fallback.
+//===----------------------------------------------------------------------===//
+#include "asan_win_new_delete_thunk_common.h"
+
+// see diagram in asan_win_new_delete_thunk_common.h for the ordering of the
+// new/delete fallbacks.
+
+extern "C" void* __cdecl __asan_new_array_nothrow(size_t size);
+
+// Avoid tailcall optimization to preserve stack frame.
+#pragma optimize("", off)
+void* operator new[](size_t size, std::nothrow_t const&) noexcept {
+  if (__asan_InitDefine<op_new_scalar>::defined &&
+      __asan_InitDefine<op_new_array>::defined) {
+    return __asan_new_array_nothrow(size);
+  }
+
+  try {
+    return operator new[](size);
+  } catch (...) {
+    return nullptr;
+  }
+}

diff  --git a/compiler-rt/lib/asan/asan_win_new_array_thunk.cpp b/compiler-rt/lib/asan/asan_win_new_array_thunk.cpp
new file mode 100644
index 000000000000000..24d7ff14a4c5b41
--- /dev/null
+++ b/compiler-rt/lib/asan/asan_win_new_array_thunk.cpp
@@ -0,0 +1,27 @@
+//===-- asan_win_new_array_thunk.cc ---------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Windows-specific user-provided new/delete operator detection and fallback.
+//===----------------------------------------------------------------------===//
+#include "asan_win_new_delete_thunk_common.h"
+
+// see diagram in asan_win_new_delete_thunk_common.h for the ordering of the
+// new/delete fallbacks.
+
+__asan_InitDefine<op_new_array> init_new_array;
+
+// Avoid tailcall optimization to preserve stack frame.
+#pragma optimize("", off)
+void* operator new[](size_t size) {
+  if (__asan_InitDefine<op_new_scalar>::defined) {
+    return __asan_new_array(size);
+  }
+  return operator new(size);
+}

diff  --git a/compiler-rt/lib/asan/asan_win_new_delete.cpp b/compiler-rt/lib/asan/asan_win_new_delete.cpp
new file mode 100644
index 000000000000000..a3db56ffef7a96e
--- /dev/null
+++ b/compiler-rt/lib/asan/asan_win_new_delete.cpp
@@ -0,0 +1,132 @@
+//===-- asan_win_new_delete.cc --------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Windows-specific user-provided new/delete operator detection and fallback.
+//===----------------------------------------------------------------------===//
+#include <stddef.h>
+
+#include "asan_allocator.h"
+#include "asan_internal.h"
+#include "asan_report.h"
+#include "asan_stack.h"
+
+// Fake std::align_val_t to avoid including <new>.
+namespace std {
+enum class align_val_t : size_t {};
+}
+
+using namespace __asan;
+
+#define OPERATOR_NEW_BODY(type, nothrow)            \
+  GET_STACK_TRACE_MALLOC                            \
+  void *res = asan_memalign(0, size, &stack, type); \
+  if (!nothrow && UNLIKELY(!res))                   \
+    ReportOutOfMemory(size, &stack);                \
+  return res;
+
+#define OPERATOR_NEW_BODY_ALIGN(type, nothrow)                \
+  GET_STACK_TRACE_MALLOC                                      \
+  void *res = asan_memalign((uptr)align, size, &stack, type); \
+  if (!nothrow && UNLIKELY(!res))                             \
+    ReportOutOfMemory(size, &stack);                          \
+  return res;
+
+#define OPERATOR_DELETE_BODY(type) \
+  GET_STACK_TRACE_FREE             \
+  asan_delete(ptr, 0, 0, &stack, type);
+
+#define OPERATOR_DELETE_BODY_SIZE(type) \
+  GET_STACK_TRACE_FREE                  \
+  asan_delete(ptr, size, 0, &stack, type);
+
+#define OPERATOR_DELETE_BODY_ALIGN(type) \
+  GET_STACK_TRACE_FREE                   \
+  asan_delete(ptr, 0, static_cast<uptr>(align), &stack, type);
+
+#define OPERATOR_DELETE_BODY_SIZE_ALIGN(type) \
+  GET_STACK_TRACE_FREE                        \
+  asan_delete(ptr, size, static_cast<uptr>(align), &stack, type);
+
+extern "C" {
+__declspec(dllexport) void *__cdecl __asan_new(size_t const size) {
+  OPERATOR_NEW_BODY(FROM_NEW, false /*nothrow*/);
+}
+
+__declspec(dllexport) void *__cdecl __asan_new_array(size_t const size) {
+  OPERATOR_NEW_BODY(FROM_NEW_BR, false /*nothrow*/);
+}
+
+__declspec(dllexport) void *__cdecl __asan_new_nothrow(size_t const size) {
+  OPERATOR_NEW_BODY(FROM_NEW, true /*nothrow*/);
+}
+
+__declspec(dllexport) void *__cdecl __asan_new_array_nothrow(
+    size_t const size) {
+  OPERATOR_NEW_BODY(FROM_NEW_BR, true /*nothrow*/);
+}
+
+__declspec(dllexport) void *__cdecl __asan_new_align(
+    size_t const size, std::align_val_t const align) {
+  OPERATOR_NEW_BODY_ALIGN(FROM_NEW, false /*nothrow*/);
+}
+
+__declspec(dllexport) void *__cdecl __asan_new_array_align(
+    size_t const size, std::align_val_t const align) {
+  OPERATOR_NEW_BODY_ALIGN(FROM_NEW_BR, false /*nothrow*/);
+}
+
+__declspec(dllexport) void *__cdecl __asan_new_align_nothrow(
+    size_t const size, std::align_val_t const align) {
+  OPERATOR_NEW_BODY_ALIGN(FROM_NEW, true /*nothrow*/);
+}
+
+__declspec(dllexport) void *__cdecl __asan_new_array_align_nothrow(
+    size_t const size, std::align_val_t const align) {
+  OPERATOR_NEW_BODY_ALIGN(FROM_NEW_BR, true /*nothrow*/);
+}
+
+__declspec(dllexport) void __cdecl __asan_delete(void *ptr) {
+  OPERATOR_DELETE_BODY(FROM_NEW);
+}
+
+__declspec(dllexport) void __cdecl __asan_delete_array(void *ptr) {
+  OPERATOR_DELETE_BODY(FROM_NEW_BR);
+}
+
+__declspec(dllexport) void __cdecl __asan_delete_size(void *ptr,
+                                                      size_t const size) {
+  OPERATOR_DELETE_BODY_SIZE(FROM_NEW);
+}
+
+__declspec(dllexport) void __cdecl __asan_delete_array_size(void *ptr,
+                                                            size_t const size) {
+  OPERATOR_DELETE_BODY_SIZE(FROM_NEW_BR);
+}
+
+__declspec(dllexport) void __cdecl __asan_delete_align(
+    void *ptr, std::align_val_t const align) {
+  OPERATOR_DELETE_BODY_ALIGN(FROM_NEW);
+}
+
+__declspec(dllexport) void __cdecl __asan_delete_array_align(
+    void *ptr, std::align_val_t const align) {
+  OPERATOR_DELETE_BODY_ALIGN(FROM_NEW_BR);
+}
+
+__declspec(dllexport) void __cdecl __asan_delete_size_align(
+    void *ptr, size_t const size, std::align_val_t const align) {
+  OPERATOR_DELETE_BODY_SIZE_ALIGN(FROM_NEW);
+}
+
+__declspec(dllexport) void __cdecl __asan_delete_array_size_align(
+    void *ptr, size_t const size, std::align_val_t const align) {
+  OPERATOR_DELETE_BODY_SIZE_ALIGN(FROM_NEW_BR);
+}
+}  // extern "C"

diff  --git a/compiler-rt/lib/asan/asan_win_new_delete_thunk_common.h b/compiler-rt/lib/asan/asan_win_new_delete_thunk_common.h
new file mode 100644
index 000000000000000..c89b05c6fb13874
--- /dev/null
+++ b/compiler-rt/lib/asan/asan_win_new_delete_thunk_common.h
@@ -0,0 +1,141 @@
+//===-- asan_win_new_delete_thunk_common.h ----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Windows-specific user-provided new/delete operator detection and fallback.
+//
+// In order to provide correct fallback behavior for operator new and delete, we
+// need to honor partially hooked new and delete operators. For example, if
+// plain operator new is provided by the user, then array operator new should
+// fallback to use that operator new. This is slighly complicated in that ASAN
+// must know which operator new/delete was used to correctly track allocations.
+// The solution here is to only pass the allocation/deallocation request
+// directly to ASAN with full metadata when we know all fallbacks for the given
+// overload are provided by ASAN. This requires us to detect which overloads are
+// provided by ASAN. We can accomplish this by seperating the definitions into
+// multiple TUs so each can be selected individually, and adding a dynamic
+// initializer to those TUs to mark whether that overload is included.
+//===----------------------------------------------------------------------===//
+#ifndef ASAN_WIN_NEW_DELETE_THUNK_COMMON_H
+#define ASAN_WIN_NEW_DELETE_THUNK_COMMON_H
+
+#include "sanitizer_common/sanitizer_internal_defs.h"
+// Fake std::nothrow_t and std::align_val_t to avoid including <new>.
+namespace std {
+struct nothrow_t {};
+enum class align_val_t : size_t {};
+}  // namespace std
+
+void* operator new(size_t, std::align_val_t);
+void* operator new[](size_t, std::align_val_t);
+void operator delete(void* ptr, std::align_val_t align) noexcept;
+void operator delete[](void* ptr, std::align_val_t align) noexcept;
+
+////////////////////////////////////
+// clang-format off
+// Fallback Ordering for new/delete
+// change this if the code called by a given operator in the case where the
+// "less specific" operators are not provided by asan changes.
+//
+// +----------+                                                     +----------------+
+// |new_scalar<---------------+                                     |new_scalar_align<--------------+
+// +----^-----+               |                                     +----^-----------+              |
+//      |                     |                                          |                          |
+// +----+-------------+  +----+----+                                +----+-------------------+  +---+-----------+
+// |new_scalar_nothrow|  |new_array|                                |new_scalar_align_nothrow|  |new_array_align|
+// +------------------+  +----^----+                                +------------------------+  +---^-----------+
+//                            |                                                                     |
+//               +------------+----+                                                    +-----------+-----------+
+//               |new_array_nothrow|                                                    |new_array_align_nothrow|
+//               +-----------------+                                                    +-----------------------+
+//
+// +-------------+                                                  +-------------------+
+// |delete_scalar<----+-----------------------+                     |delete_scalar_align<----+---------------------------+
+// +--^----------+    |                       |                     +--^----------------+    |                           |
+//    |               |                       |                        |                     |                           |
+// +--+---------+  +--+---------------+  +----+----------------+    +--+---------------+  +--+---------------------+  +--+------------------------+
+// |delete_array|  |delete_scalar_size|  |delete_scalar_nothrow|    |delete_array_align|  |delete_scalar_size_align|  |delete_scalar_align_nothrow|
+// +--^----^----+  +------------------+  +---------------------+    +--^-----^---------+  +------------------------+  +---------------------------+
+//    |    |                                                           |     |
+//    |    +-------------------+                                       |     +------------------------+
+//    |                        |                                       |                              |
+// +--+--------------+  +------+-------------+                      +--+--------------------+  +------+-------------------+
+// |delete_array_size|  |delete_array_nothrow|                      |delete_array_size_align|  |delete_array_align_nothrow|
+// +-----------------+  +--------------------+                      +-----------------------+  +--------------------------+
+// clang-format on
+
+// Only need definition detection for overloads with children.
+enum defined_ops {
+  op_new_scalar,
+  op_new_array,
+
+  op_new_scalar_align,
+  op_new_array_align,
+
+  op_delete_scalar,
+  op_delete_array,
+
+  op_delete_scalar_align,
+  op_delete_array_align
+};
+
+// Define a global of this type in each overload's translation unit
+// so that the dynamic initializer will set defined to 1 when
+// that TU is included.
+// We can then use __asan_InitDefine<op>::defined to check whether that TU is
+// included.
+template <defined_ops Id>
+struct __asan_InitDefine {
+  __asan_InitDefine() { defined = 1; }
+
+  static int defined;
+};
+
+template <defined_ops Id>
+int __asan_InitDefine<Id>::defined = 0;
+
+extern "C" void __cdecl __asan_delete_array_align(void* ptr,
+                                                  std::align_val_t align);
+
+extern "C" void __cdecl __asan_delete_array_size_align(void* ptr, size_t size,
+                                                       std::align_val_t align);
+
+extern "C" void __cdecl __asan_delete_array_size(void* ptr, size_t size);
+
+extern "C" void __cdecl __asan_delete_array(void* ptr);
+
+extern "C" void __cdecl __asan_delete_align(void* ptr, std::align_val_t align);
+
+extern "C" void __cdecl __asan_delete_size_align(
+    void* ptr, size_t size, std::align_val_t align) noexcept;
+
+extern "C" void __cdecl __asan_delete_size(void* ptr, size_t size);
+
+extern "C" void __cdecl __asan_delete(void* ptr);
+
+extern "C" void* __cdecl __asan_new_array_align_nothrow(size_t size,
+                                                        std::align_val_t align);
+
+extern "C" void* __cdecl __asan_new_array_align(size_t size,
+                                                std::align_val_t align);
+
+extern "C" void* __cdecl __asan_new_array_nothrow(size_t size);
+
+extern "C" void* __cdecl __asan_new_array(size_t size);
+
+extern "C" void* __cdecl __asan_new_align_nothrow(size_t size,
+                                                  std::align_val_t align);
+
+extern "C" void* __cdecl __asan_new_align(size_t size, std::align_val_t align);
+
+extern "C" void* __cdecl __asan_new_nothrow(size_t size);
+
+extern "C" void* __cdecl __asan_new(size_t size);
+
+#endif  // ASAN_WIN_NEW_DELETE_THUNK_COMMON_H

diff  --git a/compiler-rt/lib/asan/asan_win_new_scalar_align_nothrow_thunk.cpp b/compiler-rt/lib/asan/asan_win_new_scalar_align_nothrow_thunk.cpp
new file mode 100644
index 000000000000000..44274e0db5db4c2
--- /dev/null
+++ b/compiler-rt/lib/asan/asan_win_new_scalar_align_nothrow_thunk.cpp
@@ -0,0 +1,31 @@
+//===-- asan_win_new_scalar_align_nothrow_thunk.cc ------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Windows-specific user-provided new/delete operator detection and fallback.
+//===----------------------------------------------------------------------===//
+#include "asan_win_new_delete_thunk_common.h"
+
+// see diagram in asan_win_new_delete_thunk_common.h for the ordering of the
+// new/delete fallbacks.
+
+// Avoid tailcall optimization to preserve stack frame.
+#pragma optimize("", off)
+void* operator new(size_t size, std::align_val_t align,
+                   std::nothrow_t const&) noexcept {
+  if (__asan_InitDefine<op_new_scalar_align>::defined) {
+    return __asan_new_align_nothrow(size, align);
+  }
+
+  try {
+    return operator new(size, align);
+  } catch (...) {
+    return nullptr;
+  }
+}

diff  --git a/compiler-rt/lib/asan/asan_win_new_scalar_align_thunk.cpp b/compiler-rt/lib/asan/asan_win_new_scalar_align_thunk.cpp
new file mode 100644
index 000000000000000..b654b1528701b9d
--- /dev/null
+++ b/compiler-rt/lib/asan/asan_win_new_scalar_align_thunk.cpp
@@ -0,0 +1,24 @@
+//===-- asan_win_new_scalar_align_thunk.cc --------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Windows-specific user-provided new/delete operator detection and fallback.
+//===----------------------------------------------------------------------===//
+#include "asan_win_new_delete_thunk_common.h"
+
+// see diagram in asan_win_new_delete_thunk_common.h for the ordering of the
+// new/delete fallbacks.
+
+__asan_InitDefine<op_new_scalar_align> init_new_scalar_align;
+
+// Avoid tailcall optimization to preserve stack frame.
+#pragma optimize("", off)
+void* operator new(size_t size, std::align_val_t align) {
+  return __asan_new_align(size, align);
+}

diff  --git a/compiler-rt/lib/asan/asan_win_new_scalar_nothrow_thunk.cpp b/compiler-rt/lib/asan/asan_win_new_scalar_nothrow_thunk.cpp
new file mode 100644
index 000000000000000..a2987c62bdf45d3
--- /dev/null
+++ b/compiler-rt/lib/asan/asan_win_new_scalar_nothrow_thunk.cpp
@@ -0,0 +1,30 @@
+//===-- asan_win_new_scalar_nothrow_thunk.cc ------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Windows-specific user-provided new/delete operator detection and fallback.
+//===----------------------------------------------------------------------===//
+#include "asan_win_new_delete_thunk_common.h"
+
+// see diagram in asan_win_new_delete_thunk_common.h for the ordering of the
+// new/delete fallbacks.
+
+// Avoid tailcall optimization to preserve stack frame.
+#pragma optimize("", off)
+void* operator new(size_t size, std::nothrow_t const&) noexcept {
+  if (__asan_InitDefine<op_new_scalar>::defined) {
+    return __asan_new_nothrow(size);
+  }
+
+  try {
+    return operator new(size);
+  } catch (...) {
+    return nullptr;
+  }
+}

diff  --git a/compiler-rt/lib/asan/asan_win_new_scalar_thunk.cpp b/compiler-rt/lib/asan/asan_win_new_scalar_thunk.cpp
new file mode 100644
index 000000000000000..a85647aed42bef8
--- /dev/null
+++ b/compiler-rt/lib/asan/asan_win_new_scalar_thunk.cpp
@@ -0,0 +1,22 @@
+//===-- asan_win_new_scalar_thunk.cc --------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Windows-specific user-provided new/delete operator detection and fallback.
+//===----------------------------------------------------------------------===//
+#include "asan_win_new_delete_thunk_common.h"
+
+// see diagram in asan_win_new_delete_thunk_common.h for the ordering of the
+// new/delete fallbacks.
+
+__asan_InitDefine<op_new_scalar> init_new_scalar;
+
+// Avoid tailcall optimization to preserve stack frame.
+#pragma optimize("", off)
+void* operator new(size_t size) { return __asan_new(size); }

diff  --git a/compiler-rt/test/asan/TestCases/Windows/double_operator_delete.cpp b/compiler-rt/test/asan/TestCases/Windows/double_operator_delete.cpp
index 3668a40ce01d898..3966b36738f9255 100644
--- a/compiler-rt/test/asan/TestCases/Windows/double_operator_delete.cpp
+++ b/compiler-rt/test/asan/TestCases/Windows/double_operator_delete.cpp
@@ -8,15 +8,15 @@ int main() {
   delete [] x;
   delete [] x;
   // CHECK: AddressSanitizer: attempting double-free on [[ADDR:0x[0-9a-f]+]]
-  // CHECK-NEXT: {{#0 .* operator delete}}[]
-  // CHECK-NEXT: {{#1 .* main .*double_operator_delete.cpp}}:[[@LINE-3]]
+  // CHECK: {{#1 .* operator delete}}[]
+  // CHECK-NEXT: {{#2 .* main .*double_operator_delete.cpp}}:[[@LINE-3]]
   // CHECK: [[ADDR]] is located 0 bytes inside of 168-byte region
   // CHECK-LABEL: freed by thread T0 here:
-  // CHECK-NEXT: {{#0 .* operator delete}}[]
-  // CHECK-NEXT: {{#1 .* main .*double_operator_delete.cpp}}:[[@LINE-8]]
+  // CHECK: {{#1 .* operator delete}}[]
+  // CHECK-NEXT: {{#2 .* main .*double_operator_delete.cpp}}:[[@LINE-8]]
   // CHECK-LABEL: previously allocated by thread T0 here:
-  // CHECK-NEXT: {{#0 .* operator new}}[]
-  // CHECK-NEXT: {{#1 .* main .*double_operator_delete.cpp}}:[[@LINE-12]]
+  // CHECK: {{#1 .* operator new}}[]
+  // CHECK-NEXT: {{#2 .* main .*double_operator_delete.cpp}}:[[@LINE-12]]
   return 0;
 }
 

diff  --git a/compiler-rt/test/asan/TestCases/Windows/new_delete_mfc_already_defined.cpp b/compiler-rt/test/asan/TestCases/Windows/new_delete_mfc_already_defined.cpp
new file mode 100644
index 000000000000000..851d2463eb6778e
--- /dev/null
+++ b/compiler-rt/test/asan/TestCases/Windows/new_delete_mfc_already_defined.cpp
@@ -0,0 +1,25 @@
+// RUN: %clang_cl_asan -Od %s -Fe%t
+// RUN: %run %t
+
+// test fixing new/delete already defined
+// as long as it finishes linking, it should be good
+
+// before fix:
+// nafxcw.lib(afxmem.obj) : warning LNK4006: "void * __cdecl operator new(unsigned int)" (??2 at YAPAXI@Z) already defined in clang_rt.asan_cxx-i386.lib(asan_win_new_scalar_thunk.cpp.obj); second definition ignored
+// nafxcw.lib(afxmem.obj) : warning LNK4006: "void __cdecl operator delete(void *)" (??3 at YAXPAX@Z) already defined in clang_rt.asan_cxx-i386.lib(asan_win_delete_scalar_thunk.cpp.obj); second definition ignored
+
+#ifdef _DLL
+#  define _AFXDLL
+#endif
+
+#include "afxglobals.h"
+
+int AFX_CDECL AfxCriticalNewHandler(size_t nSize);
+
+int main(int argc, char **argv) {
+  AFX_MODULE_THREAD_STATE *pState = AfxGetModuleThreadState();
+  _PNH pnhOldHandler = AfxSetNewHandler(&AfxCriticalNewHandler);
+  AfxSetNewHandler(pnhOldHandler);
+  puts("Pass");
+  return 0;
+}

diff  --git a/compiler-rt/test/asan/TestCases/Windows/new_delete_mfc_already_defined_dbg.cpp b/compiler-rt/test/asan/TestCases/Windows/new_delete_mfc_already_defined_dbg.cpp
new file mode 100644
index 000000000000000..bc53dd1f9f25629
--- /dev/null
+++ b/compiler-rt/test/asan/TestCases/Windows/new_delete_mfc_already_defined_dbg.cpp
@@ -0,0 +1,25 @@
+// RUN: %clang_cl_asan -Od %s -Fe%t /MT /link /WX
+// RUN: %env_asan_opts=alloc_dealloc_mismatch=true %run %t
+
+// test fixing new already defined and mismatch between allocation and deallocation APis
+
+// before fix:
+// nafxcwd.lib(afx_new_scalar.obj) : error LNK2005: "void * __cdecl operator new(unsigned int)" (??2 at YAPAXI@Z) already defined in clang_rt.asan_cxx_dbg-i386.lib(asan_win_new_scalar_thunk.cpp.obj)
+// Address Sanitizer error: mismatch between allocation and deallocation APis
+
+#ifdef _DLL
+#  define _AFXDLL
+#endif
+
+#include <SDKDDKVer.h>
+#include <afxglobals.h>
+
+int main() {
+  int *normal = new int;
+  int *debug = DEBUG_NEW int;
+
+  delete normal;
+  delete debug;
+
+  return 0;
+}

diff  --git a/compiler-rt/test/asan/TestCases/Windows/operator_array_new_left_oob.cpp b/compiler-rt/test/asan/TestCases/Windows/operator_array_new_left_oob.cpp
index 96b9378cc51786a..3ea71934dc6b10b 100644
--- a/compiler-rt/test/asan/TestCases/Windows/operator_array_new_left_oob.cpp
+++ b/compiler-rt/test/asan/TestCases/Windows/operator_array_new_left_oob.cpp
@@ -10,7 +10,7 @@ int main() {
   //
   // CHECK: [[ADDR]] is located 1 bytes before 42-byte region
   // CHECK-LABEL: allocated by thread T0 here:
-  // CHECK-NEXT: {{#0 .* operator new}}[]
-  // CHECK-NEXT: {{#1 .* main .*operator_array_new_left_oob.cpp}}:[[@LINE-9]]
+  // CHECK: #[[#NEW:]] {{.* operator new}}[]
+  // CHECK-NEXT: #[[#NEW+1]] {{.* main .*operator_array_new_left_oob.cpp}}:[[@LINE-9]]
   delete [] buffer;
 }

diff  --git a/compiler-rt/test/asan/TestCases/Windows/operator_array_new_right_oob.cpp b/compiler-rt/test/asan/TestCases/Windows/operator_array_new_right_oob.cpp
index 1e29d752a783f92..2b3e14f98d57627 100644
--- a/compiler-rt/test/asan/TestCases/Windows/operator_array_new_right_oob.cpp
+++ b/compiler-rt/test/asan/TestCases/Windows/operator_array_new_right_oob.cpp
@@ -11,7 +11,7 @@ int main() {
   // CHECK:   {{#0 .* main .*operator_array_new_right_oob.cpp}}:[[@LINE-3]]
   // CHECK: [[ADDR]] is located 0 bytes after 42-byte region
   // CHECK: allocated by thread T0 here:
-  // CHECK:   {{#0 .* operator new}}[]
-  // CHECK:   {{#1 .* main .*operator_array_new_right_oob.cpp}}:[[@LINE-8]]
+  // CHECK: #[[#NEW:]] {{.* operator new}}[]
+  // CHECK: #[[#NEW+1]] {{.* main .*operator_array_new_right_oob.cpp}}:[[@LINE-8]]
   delete [] buffer;
 }

diff  --git a/compiler-rt/test/asan/TestCases/Windows/operator_array_new_uaf.cpp b/compiler-rt/test/asan/TestCases/Windows/operator_array_new_uaf.cpp
index edeee99a76530bf..cb1b510c4856bbf 100644
--- a/compiler-rt/test/asan/TestCases/Windows/operator_array_new_uaf.cpp
+++ b/compiler-rt/test/asan/TestCases/Windows/operator_array_new_uaf.cpp
@@ -12,11 +12,11 @@ int main() {
   // CHECK:   {{#0 .* main .*operator_array_new_uaf.cpp}}:[[@LINE-3]]
   // CHECK: [[ADDR]] is located 0 bytes inside of 42-byte region
   // CHECK-LABEL: freed by thread T0 here:
-  // CHECK:   {{#0 .* operator delete}}[]
-  // CHECK:   {{#1 .* main .*operator_array_new_uaf.cpp}}:[[@LINE-8]]
+  // CHECK:   #[[#DEL:]] {{.* operator delete}}[]
+  // CHECK:   #[[#DEL+1]] {{.* main .*operator_array_new_uaf.cpp}}:[[@LINE-8]]
   // CHECK-LABEL: previously allocated by thread T0 here:
-  // CHECK:   {{#0 .* operator new}}[]
-  // CHECK:   {{#1 .* main .*operator_array_new_uaf.cpp}}:[[@LINE-12]]
+  // CHECK:   #[[#NEW:]] {{.* operator new}}[]
+  // CHECK:   #[[#NEW+1]] {{.* main .*operator_array_new_uaf.cpp}}:[[@LINE-12]]
   return 0;
 }
 

diff  --git a/compiler-rt/test/asan/TestCases/Windows/operator_array_new_with_dtor_left_oob.cpp b/compiler-rt/test/asan/TestCases/Windows/operator_array_new_with_dtor_left_oob.cpp
index cad28ae8ace213a..929fee44693c93f 100644
--- a/compiler-rt/test/asan/TestCases/Windows/operator_array_new_with_dtor_left_oob.cpp
+++ b/compiler-rt/test/asan/TestCases/Windows/operator_array_new_with_dtor_left_oob.cpp
@@ -6,7 +6,11 @@ struct C {
   ~C() {}
 };
 
+#ifdef _MSC_VER
+__declspec(noinline) int hide(int x) { return x; }
+#else
 int __attribute__((noinline, optnone)) hide(int x) { return x; }
+#endif
 
 int main() {
   C *buffer = new C[42];
@@ -20,7 +24,7 @@ int main() {
   //        https://code.google.com/p/address-sanitizer/issues/detail?id=314
   // CHECK: [[ADDR]] is located {{.*}} bytes before {{(172|176)}}-byte region
   // CHECK-LABEL: allocated by thread T0 here:
-  // CHECK-NEXT: {{#0 .* operator new}}[]
-  // CHECK-NEXT: {{#1 .* main .*operator_array_new_with_dtor_left_oob.cpp}}:[[@LINE-12]]
+  // CHECK: #[[#NEW:]] {{.* operator new}}[]
+  // CHECK-NEXT: #[[#NEW+1]] {{.* main .*operator_array_new_with_dtor_left_oob.cpp}}:[[@LINE-12]]
   delete [] buffer;
 }

diff  --git a/compiler-rt/test/asan/TestCases/Windows/operator_delete_replacement_array.cpp b/compiler-rt/test/asan/TestCases/Windows/operator_delete_replacement_array.cpp
new file mode 100644
index 000000000000000..b65595cafc9f0a5
--- /dev/null
+++ b/compiler-rt/test/asan/TestCases/Windows/operator_delete_replacement_array.cpp
@@ -0,0 +1,43 @@
+// RUN: %clang_cl_asan /EHsc /Od /std:c++17 %s -Fe%t
+// RUN: %run %t 2>&1 | FileCheck %s
+// RUN: %clang_cl_asan /EHsc /Od %p/dll_host.cpp -Fe%t
+// RUN: %clang_cl_asan /EHsc /LD /Od /std:c++17 /DTEST_DLL %s -Fe%t.dll
+// RUN: %run %t %t.dll 2>&1 | FileCheck %s
+
+#include "operator_new_delete_replacement_macros.h"
+#define DEFINED_REPLACEMENTS                                                   \
+  (ALL_NEW | ALL_ALIGNED_NEW | SCALAR_DELETE | ARRAY_DELETE |                  \
+   SCALAR_ALIGNED_DELETE | ARRAY_ALIGNED_DELETE)
+#include "operator_new_delete_replacement_common.h"
+
+// Covers:
+// 12. sized array (asan)              -> array (custom)
+// 14. array nothrow (asan)            -> array (custom)
+// 19. aligned sized array (asan)      -> aligned array (custom)
+// 21. aligned array nothrow (asan)    -> aligned array (custom)
+
+// CHECK: new_scalar
+// CHECK: new_array
+// CHECK: new_scalar_nothrow
+// CHECK: new_array_nothrow
+// CHECK: delete_scalar
+// CHECK: delete_array
+// CHECK: delete_scalar
+// CHECK: delete_array
+// CHECK: new_scalar
+// CHECK: new_array
+// CHECK: delete_scalar
+// CHECK: delete_array
+
+// CHECK: new_scalar_align
+// CHECK: new_array_align
+// CHECK: new_scalar_align_nothrow
+// CHECK: new_array_align_nothrow
+// CHECK: delete_scalar_align
+// CHECK: delete_array_align
+// CHECK: delete_scalar_align
+// CHECK: delete_array_align
+// CHECK: new_scalar_align
+// CHECK: new_array_align
+// CHECK: delete_scalar_align
+// CHECK: delete_array_align

diff  --git a/compiler-rt/test/asan/TestCases/Windows/operator_delete_replacement_scalar.cpp b/compiler-rt/test/asan/TestCases/Windows/operator_delete_replacement_scalar.cpp
new file mode 100644
index 000000000000000..b796b41f0affd84
--- /dev/null
+++ b/compiler-rt/test/asan/TestCases/Windows/operator_delete_replacement_scalar.cpp
@@ -0,0 +1,48 @@
+// RUN: %clang_cl_asan  /EHsc /Od /std:c++17 %s -Fe%t
+// RUN: %run %t 2>&1 | FileCheck %s
+// RUN: %clang_cl_asan /EHsc /Od %p/dll_host.cpp -Fe%t
+// RUN: %clang_cl_asan /EHsc /LD /Od /std:c++17 /DTEST_DLL %s -Fe%t.dll
+// RUN: %run %t %t.dll 2>&1 | FileCheck %s
+
+#include "operator_new_delete_replacement_macros.h"
+#define DEFINED_REPLACEMENTS                                                   \
+  (ALL_NEW | ALL_ALIGNED_NEW | SCALAR_DELETE | SCALAR_ALIGNED_DELETE)
+#include "operator_new_delete_replacement_common.h"
+
+// Covers:
+// 9.  array (asan)                    -> scalar (custom)
+// 10. nothrow (asan)                  -> scalar (custom)
+// 11. sized (asan)                    -> scalar (custom) ** original bug report scenario **
+// 13. sized array (asan)              -> array (asan)            -> scalar (custom)
+// 15. array nothrow (asan)            -> array (asan)            -> scalar (custom)
+// 16. aligned array (asan)            -> aligned scalar (custom)
+// 17. aligned nothrow (asan)          -> aligned scalar (custom)
+// 18. aligned sized (asan)            -> aligned scalar (custom)
+// 20. aligned sized array (asan)      -> aligned array (asan)    -> aligned scalar (custom)
+// 22. aligned array nothrow (asan)    -> aligned array (asan)    -> aligned scalar (custom)
+
+// CHECK: new_scalar
+// CHECK: new_array
+// CHECK: new_scalar_nothrow
+// CHECK: new_array_nothrow
+// CHECK: delete_scalar
+// CHECK: delete_scalar
+// CHECK: delete_scalar
+// CHECK: delete_scalar
+// CHECK: new_scalar
+// CHECK: new_array
+// CHECK: delete_scalar
+// CHECK: delete_scalar
+
+// CHECK: new_scalar_align
+// CHECK: new_array_align
+// CHECK: new_scalar_align_nothrow
+// CHECK: new_array_align_nothrow
+// CHECK: delete_scalar_align
+// CHECK: delete_scalar_align
+// CHECK: delete_scalar_align
+// CHECK: delete_scalar_align
+// CHECK: new_scalar_align
+// CHECK: new_array_align
+// CHECK: delete_scalar_align
+// CHECK: delete_scalar_align

diff  --git a/compiler-rt/test/asan/TestCases/Windows/operator_delete_wrong_argument.cpp b/compiler-rt/test/asan/TestCases/Windows/operator_delete_wrong_argument.cpp
index f5be333a8db1866..c5be40a810e7c3f 100644
--- a/compiler-rt/test/asan/TestCases/Windows/operator_delete_wrong_argument.cpp
+++ b/compiler-rt/test/asan/TestCases/Windows/operator_delete_wrong_argument.cpp
@@ -6,7 +6,7 @@
 int main() {
   int *x = new int[42];
   delete (x + 1);
-// CHECK: AddressSanitizer: attempting free on address which was not malloc()-ed
-// CHECK:   {{#0 0x.* operator delete}}
-// CHECK:   {{#1 .* main .*operator_delete_wrong_argument.cpp}}:[[@LINE-3]]
+  // CHECK: AddressSanitizer: attempting free on address which was not malloc()-ed
+  // CHECK:   #[[#DEL:]] {{0x.* operator delete}}
+  // CHECK:   #[[#DEL+1]] {{.* main .*operator_delete_wrong_argument.cpp}}:[[@LINE-3]]
 }

diff  --git a/compiler-rt/test/asan/TestCases/Windows/operator_new_delete_replacement_all.cpp b/compiler-rt/test/asan/TestCases/Windows/operator_new_delete_replacement_all.cpp
new file mode 100644
index 000000000000000..9c343e55cdd1865
--- /dev/null
+++ b/compiler-rt/test/asan/TestCases/Windows/operator_new_delete_replacement_all.cpp
@@ -0,0 +1,35 @@
+// RUN: %clang_cl_asan /EHsc /Od /std:c++17 %s -Fe%t
+// RUN: %run %t 2>&1 | FileCheck %s
+// RUN: %clang_cl_asan /EHsc /Od %p/dll_host.cpp -Fe%t
+// RUN: %clang_cl_asan /EHsc /LD /Od /std:c++17 /DTEST_DLL %s -Fe%t.dll
+// RUN: %run %t %t.dll 2>&1 | FileCheck %s
+
+#include "operator_new_delete_replacement_macros.h"
+#define DEFINED_REPLACEMENTS ALL_OPERATORS
+#include "operator_new_delete_replacement_common.h"
+
+// CHECK: new_scalar
+// CHECK: new_array
+// CHECK: new_scalar_nothrow
+// CHECK: new_array_nothrow
+// CHECK: delete_scalar
+// CHECK: delete_array
+// CHECK: delete_scalar_nothrow
+// CHECK: delete_array_nothrow
+// CHECK: new_scalar
+// CHECK: new_array
+// CHECK: delete_scalar_size
+// CHECK: delete_array_size
+
+// CHECK: new_scalar_align
+// CHECK: new_array_align
+// CHECK: new_scalar_align_nothrow
+// CHECK: new_array_align_nothrow
+// CHECK: delete_scalar_align
+// CHECK: delete_array_align
+// CHECK: delete_scalar_align_nothrow
+// CHECK: delete_array_align_nothrow
+// CHECK: new_scalar_align
+// CHECK: new_array_align
+// CHECK: delete_scalar_size_align
+// CHECK: delete_array_size_align

diff  --git a/compiler-rt/test/asan/TestCases/Windows/operator_new_delete_replacement_common.h b/compiler-rt/test/asan/TestCases/Windows/operator_new_delete_replacement_common.h
new file mode 100644
index 000000000000000..07e3d29f3264d82
--- /dev/null
+++ b/compiler-rt/test/asan/TestCases/Windows/operator_new_delete_replacement_common.h
@@ -0,0 +1,413 @@
+#include <new>
+#include <stdio.h>
+
+// If users provide fallback, all parents must be replaced as well.
+// That is, you can't sanely provide a non-forwarding array op new without also replacing scalar op new.
+
+// With that in mind, we need to cover the following scenarios for operator new:
+// 1. array (asan)                    -> scalar (custom)
+// 2. nothrow (asan)                  -> scalar (custom)
+// 3. array nothrow (asan)            -> array  (custom)
+// 4. array nothrow (asan)            -> array  (asan)           -> scalar (custom)
+// 5. aligned array (asan)            -> aligned scalar (custom)
+// 6. aligned nothrow (asan)          -> aligned scalar (custom)
+// 7. aligned array nothrow (asan)    -> aligned array  (custom)
+// 8. aligned array nothrow (asan)    -> aligned array  (asan)   -> aligned scalar (custom)
+
+// And the following for operator delete:
+// 9.  array (asan)                    -> scalar (custom)
+// 10. nothrow (asan)                  -> scalar (custom)
+// 11. sized (asan)                    -> scalar (custom) ** original bug report scenario **
+// 12. sized array (asan)              -> array (custom)
+// 13. sized array (asan)              -> array (asan)            -> scalar (custom)
+// 14. array nothrow (asan)            -> array (custom)
+// 15. array nothrow (asan)            -> array (asan)            -> scalar (custom)
+// 16. aligned array (asan)            -> aligned scalar (custom)
+// 17. aligned nothrow (asan)          -> aligned scalar (custom)
+// 18. aligned sized (asan)            -> aligned scalar (custom)
+// 19. aligned sized array (asan)      -> aligned array (custom)
+// 20. aligned sized array (asan)      -> aligned array (asan)    -> aligned scalar (custom)
+// 21. aligned array nothrow (asan)    -> aligned array (custom)
+// 22. aligned array nothrow (asan)    -> aligned array (asan)    -> aligned scalar (custom)
+
+#ifdef VERBOSE
+#  define PRINTF(...) printf(__VA_ARGS__)
+#else
+#  define PRINTF(...)
+#endif
+
+template <size_t N> class arena {
+public:
+  void *alloc(const size_t size, const std::align_val_t al) {
+    return alloc(size, static_cast<size_t>(al));
+  }
+
+  void *
+  alloc(const size_t size,
+        const size_t requested_alignment = __STDCPP_DEFAULT_NEW_ALIGNMENT__) {
+    if (requested_alignment == 0 ||
+        (requested_alignment & (requested_alignment - 1))) {
+      // Alignment must be non-zero and power of two.
+      PRINTF("Allocation of size '%zu' alignment '%zu' failed due to bad "
+             "arguments.\n",
+             size, requested_alignment);
+      throw std::bad_alloc{};
+    }
+
+    const size_t alignment =
+        (requested_alignment <= __STDCPP_DEFAULT_NEW_ALIGNMENT__)
+            ? __STDCPP_DEFAULT_NEW_ALIGNMENT__
+            : requested_alignment;
+
+    // Adjust for alignment
+    const size_t alignment_mask = alignment - 1;
+    m_cur = reinterpret_cast<char *>(
+        reinterpret_cast<unsigned __int64>(m_cur + alignment_mask) &
+        ~alignment_mask);
+    const size_t memory_block_size = (size + alignment_mask) & ~alignment_mask;
+
+    if (m_cur + memory_block_size > m_buffer + N) {
+      PRINTF("Allocation of size '%zu' alignment '%zu' failed due to out of "
+             "memory.\n",
+             size, requested_alignment);
+      throw std::bad_alloc{};
+    }
+
+    char *const returned_memory_block = m_cur;
+    m_cur += memory_block_size;
+
+    PRINTF("Allocated '0x%p' of size '%zu' (requested '%zu') with alignment "
+           "'%zu' (requested '%zu')\n",
+           returned_memory_block, memory_block_size, size, alignment,
+           requested_alignment);
+
+    return returned_memory_block;
+  }
+
+  void free(const void *ptr) { PRINTF("Deallocated '0x%p'\n", ptr); }
+
+private:
+  char m_buffer[N];
+  char *m_cur = m_buffer;
+};
+
+arena<100000> mem;
+
+////////////////////////////////////
+// clang-format off
+// new() Fallback Ordering
+//
+// +----------+
+// |new_scalar<---------------+
+// +----^-----+               |
+//      |                     |
+// +----+-------------+  +----+----+
+// |new_scalar_nothrow|  |new_array|
+// +------------------+  +----^----+
+//                            |
+//               +------------+----+
+//               |new_array_nothrow|
+//               +-----------------+
+// clang-format on
+
+#if (DEFINED_REPLACEMENTS & SCALAR_NEW)
+void *operator new(const size_t sz) {
+  puts("new_scalar");
+  return mem.alloc(sz);
+}
+#endif // MISSING_SCALAR_NEW
+
+#if (DEFINED_REPLACEMENTS & SCALAR_NEW_NOTHROW)
+void *operator new(const size_t sz, const std::nothrow_t &) noexcept {
+  puts("new_scalar_nothrow");
+  try {
+    return mem.alloc(sz);
+  } catch (...) {
+    return nullptr;
+  }
+}
+#endif // MISSING_SCALAR_NEW_NOTHROW
+
+#if (DEFINED_REPLACEMENTS & ARRAY_NEW)
+void *operator new[](const size_t sz) {
+  puts("new_array");
+  return mem.alloc(sz);
+}
+#endif // MISSING_ARRAY_NEW
+
+#if (DEFINED_REPLACEMENTS & ARRAY_NEW_NOTHROW)
+void *operator new[](const size_t sz, const std::nothrow_t &) noexcept {
+  puts("new_array_nothrow");
+  try {
+    return mem.alloc(sz);
+  } catch (...) {
+    return nullptr;
+  }
+}
+#endif // MISSING_ARRAY_NEW_NOTHROW
+
+////////////////////////////////////////////////
+// clang-format off
+// Aligned new() Fallback Ordering
+//
+// +----------------+
+// |new_scalar_align<--------------+
+// +----^-----------+              |
+//      |                          |
+// +----+-------------------+  +---+-----------+
+// |new_scalar_align_nothrow|  |new_array_align|
+// +------------------------+  +---^-----------+
+//                                 |
+//                     +-----------+-----------+
+//                     |new_array_align_nothrow|
+//                     +-----------------------+
+// clang-format on
+
+#if (DEFINED_REPLACEMENTS & SCALAR_ALIGNED_NEW)
+void *operator new(const size_t sz, const std::align_val_t al) {
+  puts("new_scalar_align");
+  return mem.alloc(sz, al);
+}
+#endif // MISSING_SCALAR_ALIGNED_NEW
+
+#if (DEFINED_REPLACEMENTS & SCALAR_ALIGNED_NEW_NOTHROW)
+void *operator new(const size_t sz, const std::align_val_t al,
+                   const std::nothrow_t &) noexcept {
+  puts("new_scalar_align_nothrow");
+  try {
+    return mem.alloc(sz, al);
+  } catch (...) {
+    return nullptr;
+  }
+}
+#endif // MISSING_SCALAR_NEW_ALIGNED_NOTHROW
+
+#if (DEFINED_REPLACEMENTS & ARRAY_ALIGNED_NEW)
+void *operator new[](const size_t sz, const std::align_val_t al) {
+  puts("new_array_align");
+  return mem.alloc(sz, al);
+}
+#endif // MISSING_ARRAY_ALIGNED_NEW
+
+#if (DEFINED_REPLACEMENTS & ARRAY_ALIGNED_NEW_NOTHROW)
+void *operator new[](const size_t sz, const std::align_val_t al,
+                     const std::nothrow_t &) noexcept {
+  puts("new_array_align_nothrow");
+  try {
+    return mem.alloc(sz, al);
+  } catch (...) {
+    return nullptr;
+  }
+}
+#endif // MISSING_ARRAY_ALIGNED_NEW_NOTHROW
+
+////////////////////////////////////////////////////////////////
+// clang-format off
+// delete() Fallback Ordering
+//
+// +-------------+
+// |delete_scalar<----+-----------------------+
+// +--^----------+    |                       |
+//    |               |                       |
+// +--+---------+  +--+---------------+  +----+----------------+
+// |delete_array|  |delete_scalar_size|  |delete_scalar_nothrow|
+// +--^----^----+  +------------------+  +---------------------+
+//    |    |
+//    |    +-------------------+
+//    |                        |
+// +--+--------------+  +------+-------------+
+// |delete_array_size|  |delete_array_nothrow|
+// +-----------------+  +--------------------+
+// clang-format on
+
+#if (DEFINED_REPLACEMENTS & SCALAR_DELETE)
+void operator delete(void *const ptr) noexcept {
+  puts("delete_scalar");
+  mem.free(ptr);
+}
+#endif // MISSING_SCALAR_DELETE
+
+#if (DEFINED_REPLACEMENTS & ARRAY_DELETE)
+void operator delete[](void *const ptr) noexcept {
+  puts("delete_array");
+  mem.free(ptr);
+}
+#endif // MISSING_ARRAY_DELETE
+
+#if (DEFINED_REPLACEMENTS & ARRAY_SIZED_DELETE)
+void operator delete[](void *const ptr, const size_t sz) noexcept {
+  puts("delete_array_size");
+  mem.free(ptr);
+}
+#endif // MISSING_ARRAY_SIZED_DELETE
+
+#if (DEFINED_REPLACEMENTS & ARRAY_DELETE_NOTHROW)
+void operator delete[](void *const ptr, const std::nothrow_t &) noexcept {
+  puts("delete_array_nothrow");
+  mem.free(ptr);
+}
+#endif // MISSING_ARRAY_DELETE_NOTHROW
+
+#if (DEFINED_REPLACEMENTS & SCALAR_SIZED_DELETE)
+void operator delete(void *const ptr, const size_t sz) noexcept {
+  puts("delete_scalar_size");
+  mem.free(ptr);
+}
+#endif // MISSING_SCALAR_SIZED_DELETE
+
+#if (DEFINED_REPLACEMENTS & SCALAR_DELETE_NOTHROW)
+void operator delete(void *const ptr, const std::nothrow_t &) noexcept {
+  puts("delete_scalar_nothrow");
+  mem.free(ptr);
+}
+#endif // MISSING_SCALAR_DELETE_NOTHROW
+
+//////////////////////////////////////////////////////////////////////////////////
+// clang-format off
+// Aligned delete() Fallback Ordering
+//
+// +-------------------+
+// |delete_scalar_align<----+---------------------------+
+// +--^----------------+    |                           |
+//    |                     |                           |
+// +--+---------------+  +--+---------------------+  +--+------------------------+
+// |delete_array_align|  |delete_scalar_size_align|  |delete_scalar_align_nothrow|
+// +--^-----^---------+  +------------------------+  +---------------------------+
+//    |     |
+//    |     +------------------------+
+//    |                              |
+// +--+--------------------+  +------+-------------------+
+// |delete_array_size_align|  |delete_array_align_nothrow|
+// +-----------------------+  +--------------------------+
+// clang-format on
+
+#if (DEFINED_REPLACEMENTS & SCALAR_ALIGNED_DELETE)
+void operator delete(void *const ptr, const std::align_val_t) noexcept {
+  puts("delete_scalar_align");
+  mem.free(ptr);
+}
+#endif // MISSING_SCALAR_DELETE
+
+#if (DEFINED_REPLACEMENTS & ARRAY_ALIGNED_DELETE)
+void operator delete[](void *const ptr, const std::align_val_t) noexcept {
+  puts("delete_array_align");
+  mem.free(ptr);
+}
+#endif // MISSING_ARRAY_DELETE
+
+#if (DEFINED_REPLACEMENTS & ARRAY_SIZED_ALIGNED_DELETE)
+void operator delete[](void *const ptr, const size_t sz,
+                       const std::align_val_t) noexcept {
+  puts("delete_array_size_align");
+  mem.free(ptr);
+}
+#endif // MISSING_ARRAY_SIZED_DELETE
+
+#if (DEFINED_REPLACEMENTS & ARRAY_ALIGNED_DELETE_NOTHROW)
+void operator delete[](void *const ptr, const std::align_val_t,
+                       const std::nothrow_t &) noexcept {
+  puts("delete_array_align_nothrow");
+  mem.free(ptr);
+}
+#endif // MISSING_ARRAY_DELETE_NOTHROW
+
+#if (DEFINED_REPLACEMENTS & SCALAR_SIZED_ALIGNED_DELETE)
+void operator delete(void *const ptr, const size_t sz,
+                     const std::align_val_t) noexcept {
+  puts("delete_scalar_size_align");
+  mem.free(ptr);
+}
+#endif // MISSING_SCALAR_SIZED_DELETE
+
+#if (DEFINED_REPLACEMENTS & SCALAR_ALIGNED_DELETE_NOTHROW)
+void operator delete(void *const ptr, const std::align_val_t,
+                     const std::nothrow_t &) noexcept {
+  puts("delete_scalar_align_nothrow");
+  mem.free(ptr);
+}
+#endif // MISSING_SCALAR_DELETE_NOTHROW
+
+// Explicitly call delete so we can explicitly choose sized vs non-sized versions of each.
+// Also provide explicit nothrow version, since that can't be implicitly invoked.
+template <typename T> void op_delete_scalar(T *ptr) {
+  if (alignof(T) > __STDCPP_DEFAULT_NEW_ALIGNMENT__) {
+    operator delete(ptr, std::align_val_t{alignof(T)});
+  } else {
+    operator delete(ptr);
+  }
+}
+
+template <typename T> void op_delete_array(T *ptr) {
+  if (alignof(T) > __STDCPP_DEFAULT_NEW_ALIGNMENT__) {
+    operator delete[](ptr, std::align_val_t{alignof(T)});
+  } else {
+    operator delete[](ptr);
+  }
+}
+
+template <typename T> void op_delete_scalar_nothrow(T *ptr) {
+  if (alignof(T) > __STDCPP_DEFAULT_NEW_ALIGNMENT__) {
+    operator delete(ptr, std::align_val_t{alignof(T)}, std::nothrow_t{});
+  } else {
+    operator delete(ptr, std::nothrow_t{});
+  }
+}
+
+template <typename T> void op_delete_array_nothrow(T *ptr) {
+  if (alignof(T) > __STDCPP_DEFAULT_NEW_ALIGNMENT__) {
+    operator delete[](ptr, std::align_val_t{alignof(T)}, std::nothrow_t{});
+  } else {
+    operator delete[](ptr, std::nothrow_t{});
+  }
+}
+
+template <typename T> void op_delete_scalar_size(T *ptr) {
+  if (alignof(T) > __STDCPP_DEFAULT_NEW_ALIGNMENT__) {
+    operator delete(ptr, sizeof(T), std::align_val_t{alignof(T)});
+  } else {
+    operator delete(ptr, sizeof(T));
+  }
+}
+
+template <size_t N, typename T> void op_delete_array_size(T *ptr) {
+  if (alignof(T) > __STDCPP_DEFAULT_NEW_ALIGNMENT__) {
+    operator delete[](ptr, sizeof(T) * N, std::align_val_t{alignof(T)});
+  } else {
+    operator delete[](ptr, sizeof(T) * N);
+  }
+}
+
+template <typename T> void test_allocations() {
+  T *scalar = new T();
+  T *array = new T[5];
+
+  T *scalar_nothrow = new (std::nothrow) T();
+  T *array_nothrow = new (std::nothrow) T[5];
+
+  op_delete_scalar(scalar);
+  op_delete_array(array);
+
+  op_delete_scalar_nothrow(scalar_nothrow);
+  op_delete_array_nothrow(array_nothrow);
+
+  T *scalar_size = new T();
+  T *array_size = new T[5];
+
+  op_delete_scalar_size(scalar_size);
+  op_delete_array_size<5>(array_size);
+}
+
+struct alignas(32) overaligned {
+  double a;
+};
+
+#ifdef TEST_DLL
+extern "C" __declspec(dllexport) int test_function()
+#else
+int main()
+#endif
+{
+  test_allocations<int>();
+  test_allocations<overaligned>();
+  return 0;
+}

diff  --git a/compiler-rt/test/asan/TestCases/Windows/operator_new_delete_replacement_macros.h b/compiler-rt/test/asan/TestCases/Windows/operator_new_delete_replacement_macros.h
new file mode 100644
index 000000000000000..7ea1744bf9bd377
--- /dev/null
+++ b/compiler-rt/test/asan/TestCases/Windows/operator_new_delete_replacement_macros.h
@@ -0,0 +1,30 @@
+#define SCALAR_NEW 0x000001
+#define SCALAR_NEW_NOTHROW 0x000002
+#define ARRAY_NEW 0x000004
+#define ARRAY_NEW_NOTHROW 0x000008
+#define ALL_NEW 0x00000f
+
+#define SCALAR_ALIGNED_NEW 0x000010
+#define SCALAR_ALIGNED_NEW_NOTHROW 0x000020
+#define ARRAY_ALIGNED_NEW 0x000040
+#define ARRAY_ALIGNED_NEW_NOTHROW 0x000080
+#define ALL_ALIGNED_NEW 0x0000f0
+
+#define SCALAR_DELETE 0x000100
+#define ARRAY_DELETE 0x000200
+#define ARRAY_SIZED_DELETE 0x000400
+#define ARRAY_DELETE_NOTHROW 0x000800
+#define SCALAR_SIZED_DELETE 0x001000
+#define SCALAR_DELETE_NOTHROW 0x002000
+#define ALL_DELETE 0x003f00
+
+#define SCALAR_ALIGNED_DELETE 0x010000
+#define ARRAY_ALIGNED_DELETE 0x020000
+#define ARRAY_SIZED_ALIGNED_DELETE 0x040000
+#define ARRAY_ALIGNED_DELETE_NOTHROW 0x080000
+#define SCALAR_SIZED_ALIGNED_DELETE 0x100000
+#define SCALAR_ALIGNED_DELETE_NOTHROW 0x200000
+#define ALL_ALIGNED_DELETE 0x3f0000
+
+#define ALL_OPERATORS                                                          \
+  (ALL_NEW | ALL_ALIGNED_NEW | ALL_DELETE | ALL_ALIGNED_DELETE)

diff  --git a/compiler-rt/test/asan/TestCases/Windows/operator_new_left_oob.cpp b/compiler-rt/test/asan/TestCases/Windows/operator_new_left_oob.cpp
index ab104ba1a4cddef..81303704da11e7e 100644
--- a/compiler-rt/test/asan/TestCases/Windows/operator_new_left_oob.cpp
+++ b/compiler-rt/test/asan/TestCases/Windows/operator_new_left_oob.cpp
@@ -6,12 +6,12 @@
 int main() {
   char *buffer = new char;
   buffer[-1] = 42;
-// CHECK: AddressSanitizer: heap-buffer-overflow on address [[ADDR:0x[0-9a-f]+]]
-// CHECK: WRITE of size 1 at [[ADDR]] thread T0
-// CHECK:   {{#0 .* main .*operator_new_left_oob.cpp}}:[[@LINE-3]]
-// CHECK: [[ADDR]] is located 1 bytes before 1-byte region
-// CHECK: allocated by thread T0 here:
-// CHECK:   {{#0 .* operator new}}
-// CHECK:   {{#1 .* main .*operator_new_left_oob.cpp}}:[[@LINE-8]]
+  // CHECK: AddressSanitizer: heap-buffer-overflow on address [[ADDR:0x[0-9a-f]+]]
+  // CHECK: WRITE of size 1 at [[ADDR]] thread T0
+  // CHECK:   {{#0 .* main .*operator_new_left_oob.cpp}}:[[@LINE-3]]
+  // CHECK: [[ADDR]] is located 1 bytes before 1-byte region
+  // CHECK: allocated by thread T0 here:
+  // CHECK:   #[[#NEW:]] {{.* operator new}}
+  // CHECK:   #[[#NEW+1]] {{.* main .*operator_new_left_oob.cpp}}:[[@LINE-8]]
   delete buffer;
 }

diff  --git a/compiler-rt/test/asan/TestCases/Windows/operator_new_replacement_array.cpp b/compiler-rt/test/asan/TestCases/Windows/operator_new_replacement_array.cpp
new file mode 100644
index 000000000000000..d36e235cb137ddb
--- /dev/null
+++ b/compiler-rt/test/asan/TestCases/Windows/operator_new_replacement_array.cpp
@@ -0,0 +1,41 @@
+// RUN: %clang_cl_asan /EHsc /Od /std:c++17 %s -Fe%t
+// RUN: %run %t 2>&1 | FileCheck %s
+// RUN: %clang_cl_asan /EHsc /Od %p/dll_host.cpp -Fe%t
+// RUN: %clang_cl_asan /EHsc /LD /Od /std:c++17 /DTEST_DLL %s -Fe%t.dll
+// RUN: %run %t %t.dll 2>&1 | FileCheck %s
+
+#include "operator_new_delete_replacement_macros.h"
+#define DEFINED_REPLACEMENTS                                                   \
+  (SCALAR_NEW | ARRAY_NEW | SCALAR_ALIGNED_NEW | ARRAY_ALIGNED_NEW |           \
+   ALL_DELETE | ALL_ALIGNED_DELETE)
+#include "operator_new_delete_replacement_common.h"
+
+// Covers:
+// 3. array nothrow (asan)            -> array  (custom)
+// 7. aligned array nothrow (asan)    -> aligned array  (custom)
+
+// CHECK: new_scalar
+// CHECK: new_array
+// CHECK: new_scalar
+// CHECK: new_array
+// CHECK: delete_scalar
+// CHECK: delete_array
+// CHECK: delete_scalar_nothrow
+// CHECK: delete_array_nothrow
+// CHECK: new_scalar
+// CHECK: new_array
+// CHECK: delete_scalar_size
+// CHECK: delete_array_size
+
+// CHECK: new_scalar_align
+// CHECK: new_array_align
+// CHECK: new_scalar_align
+// CHECK: new_array_align
+// CHECK: delete_scalar_align
+// CHECK: delete_array_align
+// CHECK: delete_scalar_align_nothrow
+// CHECK: delete_array_align_nothrow
+// CHECK: new_scalar_align
+// CHECK: new_array_align
+// CHECK: delete_scalar_size_align
+// CHECK: delete_array_size_align

diff  --git a/compiler-rt/test/asan/TestCases/Windows/operator_new_replacement_scalar.cpp b/compiler-rt/test/asan/TestCases/Windows/operator_new_replacement_scalar.cpp
new file mode 100644
index 000000000000000..032904e12430255
--- /dev/null
+++ b/compiler-rt/test/asan/TestCases/Windows/operator_new_replacement_scalar.cpp
@@ -0,0 +1,44 @@
+// RUN: %clang_cl_asan /EHsc /Od /std:c++17 %s -Fe%t
+// RUN: %run %t 2>&1 | FileCheck %s
+// RUN: %clang_cl_asan /EHsc /Od %p/dll_host.cpp -Fe%t
+// RUN: %clang_cl_asan /EHsc /LD /Od /std:c++17 /DTEST_DLL %s -Fe%t.dll
+// RUN: %run %t %t.dll 2>&1 | FileCheck %s
+
+#include "operator_new_delete_replacement_macros.h"
+#define DEFINED_REPLACEMENTS                                                   \
+  (SCALAR_NEW | SCALAR_ALIGNED_NEW | ALL_DELETE | ALL_ALIGNED_DELETE)
+#include "operator_new_delete_replacement_common.h"
+
+// Covers:
+// 1. array (asan)                    -> scalar (custom)
+// 2. nothrow (asan)                  -> scalar (custom)
+// 4. array nothrow (asan)            -> array  (asan)           -> scalar (custom)
+// 5. aligned array (asan)            -> aligned scalar (custom)
+// 6. aligned nothrow (asan)          -> aligned scalar (custom)
+// 8. aligned array nothrow (asan)    -> aligned array  (asan)   -> aligned scalar (custom)
+
+// CHECK: new_scalar
+// CHECK: new_scalar
+// CHECK: new_scalar
+// CHECK: new_scalar
+// CHECK: delete_scalar
+// CHECK: delete_array
+// CHECK: delete_scalar_nothrow
+// CHECK: delete_array_nothrow
+// CHECK: new_scalar
+// CHECK: new_scalar
+// CHECK: delete_scalar_size
+// CHECK: delete_array_size
+
+// CHECK: new_scalar_align
+// CHECK: new_scalar_align
+// CHECK: new_scalar_align
+// CHECK: new_scalar_align
+// CHECK: delete_scalar_align
+// CHECK: delete_array_align
+// CHECK: delete_scalar_align_nothrow
+// CHECK: delete_array_align_nothrow
+// CHECK: new_scalar_align
+// CHECK: new_scalar_align
+// CHECK: delete_scalar_size_align
+// CHECK: delete_array_size_align

diff  --git a/compiler-rt/test/asan/TestCases/Windows/operator_new_right_oob.cpp b/compiler-rt/test/asan/TestCases/Windows/operator_new_right_oob.cpp
index 75db1498bb1a5a5..0950e6f0802b72e 100644
--- a/compiler-rt/test/asan/TestCases/Windows/operator_new_right_oob.cpp
+++ b/compiler-rt/test/asan/TestCases/Windows/operator_new_right_oob.cpp
@@ -6,12 +6,12 @@
 int main() {
   char *buffer = new char;
   buffer[1] = 42;
-// CHECK: AddressSanitizer: heap-buffer-overflow on address [[ADDR:0x[0-9a-f]+]]
-// CHECK: WRITE of size 1 at [[ADDR]] thread T0
-// CHECK:   {{#0 .* main .*operator_new_right_oob.cpp}}:[[@LINE-3]]
-// CHECK: [[ADDR]] is located 0 bytes after 1-byte region
-// CHECK: allocated by thread T0 here:
-// CHECK:   {{#0 .* operator new}}
-// CHECK:   {{#1 .* main .*operator_new_right_oob.cpp}}:[[@LINE-8]]
+  // CHECK: AddressSanitizer: heap-buffer-overflow on address [[ADDR:0x[0-9a-f]+]]
+  // CHECK: WRITE of size 1 at [[ADDR]] thread T0
+  // CHECK:   {{#0 .* main .*operator_new_right_oob.cpp}}:[[@LINE-3]]
+  // CHECK: [[ADDR]] is located 0 bytes after 1-byte region
+  // CHECK: allocated by thread T0 here:
+  // CHECK:   #[[#NEW:]] {{.* operator new}}
+  // CHECK:   #[[#NEW+1]] {{.* main .*operator_new_right_oob.cpp}}:[[@LINE-8]]
   delete buffer;
 }

diff  --git a/compiler-rt/test/asan/TestCases/Windows/operator_new_uaf.cpp b/compiler-rt/test/asan/TestCases/Windows/operator_new_uaf.cpp
index f9c3753f7f8bfaa..762d84f8c955025 100644
--- a/compiler-rt/test/asan/TestCases/Windows/operator_new_uaf.cpp
+++ b/compiler-rt/test/asan/TestCases/Windows/operator_new_uaf.cpp
@@ -1,5 +1,7 @@
 // RUN: %clang_cl_asan %Od %s %Fe%t
 // RUN: not %run %t 2>&1 | FileCheck %s
+// RUN: %clang_cl_asan -Od %s -Fe%t_dbg /link /INFERASANLIBS:DEBUG
+// RUN: not %run %t_dbg 2>&1 | FileCheck %s
 
 #include <windows.h>
 
@@ -7,16 +9,16 @@ int main() {
   char *buffer = new char;
   delete buffer;
   *buffer = 42;
-// CHECK: AddressSanitizer: heap-use-after-free on address [[ADDR:0x[0-9a-f]+]]
-// CHECK: WRITE of size 1 at [[ADDR]] thread T0
-// CHECK:   {{#0 .* main .*operator_new_uaf.cpp}}:[[@LINE-3]]
-// CHECK: [[ADDR]] is located 0 bytes inside of 1-byte region
-// CHECK-LABEL: freed by thread T0 here:
-// CHECK:   {{#0 .* operator delete}}
-// CHECK:   {{#1 .* main .*operator_new_uaf.cpp}}:[[@LINE-8]]
-// CHECK-LABEL: previously allocated by thread T0 here:
-// CHECK:   {{#0 .* operator new}}
-// CHECK:   {{#1 .* main .*operator_new_uaf.cpp}}:[[@LINE-12]]
+  // CHECK: AddressSanitizer: heap-use-after-free on address [[ADDR:0x[0-9a-f]+]]
+  // CHECK: WRITE of size 1 at [[ADDR]] thread T0
+  // CHECK:   {{#0 .* main .*operator_new_uaf.cpp}}:[[@LINE-3]]
+  // CHECK: [[ADDR]] is located 0 bytes inside of 1-byte region
+  // CHECK-LABEL: freed by thread T0 here:
+  // CHECK:   #[[#DEL:]] {{.* operator delete}}
+  // CHECK:   #[[#DEL+1]] {{.* main .*operator_new_uaf.cpp}}:[[@LINE-8]]
+  // CHECK-LABEL: previously allocated by thread T0 here:
+  // CHECK:   #[[#NEW:]] {{.* operator new}}
+  // CHECK:   #[[#NEW+1]] {{.* main .*operator_new_uaf.cpp}}:[[@LINE-12]]
   return 0;
 }
 

diff  --git a/compiler-rt/test/asan/TestCases/Windows/wrong_downcast_on_heap.cpp b/compiler-rt/test/asan/TestCases/Windows/wrong_downcast_on_heap.cpp
index a70a86eafec6fad..6b5d54d87cdeeb0 100644
--- a/compiler-rt/test/asan/TestCases/Windows/wrong_downcast_on_heap.cpp
+++ b/compiler-rt/test/asan/TestCases/Windows/wrong_downcast_on_heap.cpp
@@ -15,12 +15,12 @@ int main(void) {
   Parent *p = new Parent;
   Child *c = (Child*)p;  // Intentional error here!
   c->extra_field = 42;
-// CHECK: AddressSanitizer: heap-buffer-overflow on address [[ADDR:0x[0-9a-f]+]]
-// CHECK: WRITE of size 4 at [[ADDR]] thread T0
-// CHECK:   {{#0 0x[0-9a-f]* in main .*wrong_downcast_on_heap.cpp}}:[[@LINE-3]]
-// CHECK: [[ADDR]] is located 0 bytes after 4-byte region
-// CHECK: allocated by thread T0 here:
-// CHECK:   #0 {{.*}} operator new
+  // CHECK: AddressSanitizer: heap-buffer-overflow on address [[ADDR:0x[0-9a-f]+]]
+  // CHECK: WRITE of size 4 at [[ADDR]] thread T0
+  // CHECK:   {{#0 0x[0-9a-f]* in main .*wrong_downcast_on_heap.cpp}}:[[@LINE-3]]
+  // CHECK: [[ADDR]] is located 0 bytes after 4-byte region
+  // CHECK: allocated by thread T0 here:
+  // CHECK:   {{.*}} operator new
   return 0;
 }
 

diff  --git a/compiler-rt/test/asan/TestCases/large_func_test.cpp b/compiler-rt/test/asan/TestCases/large_func_test.cpp
index 37fec8bef6ce813..4d289586ac0416f 100644
--- a/compiler-rt/test/asan/TestCases/large_func_test.cpp
+++ b/compiler-rt/test/asan/TestCases/large_func_test.cpp
@@ -50,10 +50,10 @@ int main(int argc, char **argv) {
   // CHECK: {{allocated by thread T0 here:}}
   // CHECK-Linux:  {{    #0 0x.* in operator new}}
   // CHECK-SunOS:  {{    #0 0x.* in operator new}}
-  // CHECK-Windows:{{    #0 0x.* in operator new}}
+  // CHECK-Windows:{{    #0 0x.* in __asan_new_array}}
   // CHECK-FreeBSD:{{    #0 0x.* in operator new}}
   // CHECK-Darwin: {{    #0 0x.* in .*_Zna}}
-  // CHECK-NEXT:   {{    #1 0x.* in main .*large_func_test.cpp:}}[[@LINE-10]]
+  // CHECK:   {{    0x.* in main .*large_func_test.cpp:}}[[@LINE-10]]
   int y = x[argc];
   delete[] x;
   return y;

diff  --git a/compiler-rt/test/asan/TestCases/malloc_context_size.cpp b/compiler-rt/test/asan/TestCases/malloc_context_size.cpp
index e75bc48793ad135..e90cd017682a0c2 100644
--- a/compiler-rt/test/asan/TestCases/malloc_context_size.cpp
+++ b/compiler-rt/test/asan/TestCases/malloc_context_size.cpp
@@ -1,9 +1,9 @@
 // RUN: %clangxx_asan -O0 %s -o %t
-// RUN: %env_asan_opts=malloc_context_size=0:fast_unwind_on_malloc=0 not %run %t 2>&1 | FileCheck %s
-// RUN: %env_asan_opts=malloc_context_size=0:fast_unwind_on_malloc=1 not %run %t 2>&1 | FileCheck %s
-// RUN: %env_asan_opts=malloc_context_size=1:fast_unwind_on_malloc=0 not %run %t 2>&1 | FileCheck %s
-// RUN: %env_asan_opts=malloc_context_size=1:fast_unwind_on_malloc=1 not %run %t 2>&1 | FileCheck %s
-// RUN: %env_asan_opts=malloc_context_size=2 not %run %t 2>&1 | FileCheck %s --check-prefix=TWO
+// RUN: %env_asan_opts=malloc_context_size=0:fast_unwind_on_malloc=0 not %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK,%if target={{.*-windows-.*}} %{CHECK-WIN%} %else %{CHECK-NOT-WIN%}
+// RUN: %env_asan_opts=malloc_context_size=0:fast_unwind_on_malloc=1 not %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK,%if target={{.*-windows-.*}} %{CHECK-WIN%} %else %{CHECK-NOT-WIN%}
+// RUN: %env_asan_opts=malloc_context_size=1:fast_unwind_on_malloc=0 not %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK,%if target={{.*-windows-.*}} %{CHECK-WIN%} %else %{CHECK-NOT-WIN%}
+// RUN: %env_asan_opts=malloc_context_size=1:fast_unwind_on_malloc=1 not %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK,%if target={{.*-windows-.*}} %{CHECK-WIN%} %else %{CHECK-NOT-WIN%}
+// RUN: %env_asan_opts=malloc_context_size=2 not %run %t 2>&1 | FileCheck %s --check-prefixes=TWO,%if target={{.*-windows-.*}} %{TWO-WIN%} %else %{TWO-NOT-WIN%}
 
 int main() {
   char *x = new char[20];
@@ -11,17 +11,20 @@ int main() {
   return x[0];
 
   // CHECK: freed by thread T{{.*}} here:
-  // CHECK-NEXT: #0 0x{{.*}} in {{operator delete( )?\[\]|_ZdaPv}}
+  // CHECK-NOT-WIN-NEXT: #0 0x{{.*}} in {{operator delete( )?\[\]|_ZdaPv}}
+  // CHECK-WIN-NEXT: #0 0x{{.*}} in {{__asan_delete_array}}
   // CHECK-NOT: #1 0x{{.*}}
 
   // CHECK: previously allocated by thread T{{.*}} here:
-  // CHECK-NEXT: #0 0x{{.*}} in {{operator new( )?\[\]|_Znam}}
+  // CHECK-NOT-WIN-NEXT: #0 0x{{.*}} in {{operator new( )?\[\]|_Znam}}
+  // CHECK-WIN-NEXT: #0 0x{{.*}} in {{__asan_new_array}}
   // CHECK-NOT: #1 0x{{.*}}
 
   // CHECK: SUMMARY: AddressSanitizer: heap-use-after-free
 
   // TWO: previously allocated by thread T{{.*}} here:
   // TWO-NEXT: #0 0x{{.*}}
-  // TWO-NEXT: #1 0x{{.*}} in main {{.*}}malloc_context_size.cpp
+  // TWO-NOT-WIN-NEXT: #1 0x{{.*}} in main {{.*}}malloc_context_size.cpp
+  // TWO-WIN-NEXT: #1 0x{{.*}} in {{operator new( )?\[\]|_Znam}}
   // TWO: SUMMARY: AddressSanitizer: heap-use-after-free
 }

diff  --git a/compiler-rt/test/asan/TestCases/use-after-delete.cpp b/compiler-rt/test/asan/TestCases/use-after-delete.cpp
index 4d0c055368bb0c5..273ebf72f132f6a 100644
--- a/compiler-rt/test/asan/TestCases/use-after-delete.cpp
+++ b/compiler-rt/test/asan/TestCases/use-after-delete.cpp
@@ -16,21 +16,20 @@ int main() {
   // CHECK: {{0x.* is located 5 bytes inside of 10-byte region .0x.*,0x.*}}
 
   // CHECK: {{freed by thread T0 here:}}
-  // CHECK-Linux:  {{    #0 0x.* in operator delete\[\]}}
-  // CHECK-SunOS:  {{    #0 0x.* in operator delete\[\]}}
-  // CHECK-Windows:{{    #0 0x.* in operator delete\[\]}}
-  // CHECK-FreeBSD:{{    #0 0x.* in operator delete\[\]}}
-  // CHECK-Darwin: {{    #0 0x.* in .*_Zda}}
-  // CHECK-NEXT:   {{    #1 0x.* in main .*use-after-delete.cpp:}}[[@LINE-14]]
+  // CHECK-Linux: #[[#DEL:]] {{0x.* in operator delete\[\]}}
+  // CHECK-SunOS: #[[#DEL:]] {{0x.* in operator delete\[\]}}
+  // CHECK-Windows: #[[#DEL:]] {{0x.* in operator delete\[\]}}
+  // CHECK-FreeBSD: #[[#DEL:]] {{0x.* in operator delete\[\]}}
+  // CHECK-Darwin: #[[#DEL:]] {{0x.* in .*_Zda}}
+  // CHECK-NEXT: #[[#DEL+1]] {{0x.* in main .*use-after-delete.cpp:}}[[@LINE-14]]
 
   // CHECK: {{previously allocated by thread T0 here:}}
-  // CHECK-Linux:  {{    #0 0x.* in operator new\[\]}}
-  // CHECK-SunOS:  {{    #0 0x.* in operator new\[\]}}
-  // CHECK-Windows:{{    #0 0x.* in operator new\[\]}}
-  // CHECK-FreeBSD:{{    #0 0x.* in operator new\[\]}}
-  // CHECK-Darwin: {{    #0 0x.* in .*_Zna}}
-  // CHECK-NEXT:   {{    #1 0x.* in main .*use-after-delete.cpp:}}[[@LINE-23]]
-
+  // CHECK-Linux: #[[#NEW:]] {{0x.* in operator new\[\]}}
+  // CHECK-SunOS: #[[#NEW:]] {{0x.* in operator new\[\]}}
+  // CHECK-Windows: #[[#NEW:]] {{0x.* in operator new\[\]}}
+  // CHECK-FreeBSD: #[[#NEW:]] {{0x.* in operator new\[\]}}
+  // CHECK-Darwin: #[[#NEW:]] {{0x.* in .*_Zna}}
+  // CHECK-NEXT: #[[#NEW+1]] {{0x.* in main .*use-after-delete.cpp:}}[[@LINE-23]]
 
   // CHECK: Shadow byte legend (one shadow byte represents {{[0-9]+}} application bytes):
   // CHECK: Global redzone:


        


More information about the llvm-commits mailing list