diff --git a/compiler-rt/lib/asan/CMakeLists.txt b/compiler-rt/lib/asan/CMakeLists.txt --- a/compiler-rt/lib/asan/CMakeLists.txt +++ b/compiler-rt/lib/asan/CMakeLists.txt @@ -38,9 +38,40 @@ ) 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,6 +114,13 @@ 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}) @@ -139,6 +177,15 @@ 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} @@ -238,13 +285,24 @@ 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 @@ -254,6 +312,13 @@ 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} @@ -310,6 +375,27 @@ 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 "$") + 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 --- a/compiler-rt/lib/asan/asan_interface.inc +++ b/compiler-rt/lib/asan/asan_interface.inc @@ -187,3 +187,22 @@ 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_stack.h b/compiler-rt/lib/asan/asan_stack.h --- a/compiler-rt/lib/asan/asan_stack.h +++ b/compiler-rt/lib/asan/asan_stack.h @@ -47,6 +47,21 @@ fast, max_size); \ } +#define GET_STACK_TRACE_EXPLICIT(max_size, fast, pc, bp, caller_pc, \ + extra_context) \ + UNINITIALIZED __sanitizer::BufferedStackTrace stack; \ + if (max_size <= 2) { \ + stack.size = max_size; \ + if (max_size > 0) { \ + stack.top_frame_bp = bp; \ + stack.trace_buffer[0] = pc; \ + if (max_size > 1) \ + stack.trace_buffer[1] = caller_pc; \ + } \ + } else { \ + stack.Unwind(pc, bp, nullptr, fast, max_size + extra_context); \ + } + #define GET_STACK_TRACE_FATAL(pc, bp) \ UNINITIALIZED BufferedStackTrace stack; \ stack.Unwind(pc, bp, nullptr, common_flags()->fast_unwind_on_fatal) @@ -60,8 +75,16 @@ #define GET_STACK_TRACE_MALLOC \ GET_STACK_TRACE(GetMallocContextSize(), common_flags()->fast_unwind_on_malloc) +#define GET_STACK_TRACE_MALLOC_WIN(pc, bp, caller_pc, extra_context) \ + GET_STACK_TRACE_EXPLICIT(__asan::GetMallocContextSize(), \ + common_flags()->fast_unwind_on_malloc, pc, bp, \ + caller_pc, extra_context) + #define GET_STACK_TRACE_FREE GET_STACK_TRACE_MALLOC +#define GET_STACK_TRACE_FREE_WIN(pc, bp, caller_pc, extra_context) \ + GET_STACK_TRACE_MALLOC_WIN(pc, bp, caller_pc, extra_context) + #define PRINT_CURRENT_STACK() \ { \ GET_STACK_TRACE_FATAL_HERE; \ 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 --- /dev/null +++ b/compiler-rt/lib/asan/asan_win_delete_array_align_nothrow_thunk.cpp @@ -0,0 +1,40 @@ +//===-- 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" + +////////////////////////////////////////////////////////////////////////////////// +// 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 + +// 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 --- /dev/null +++ b/compiler-rt/lib/asan/asan_win_delete_array_align_thunk.cpp @@ -0,0 +1,48 @@ +//===-- 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" + +////////////////////////////////////////////////////////////////////////////////// +// 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 + +__asan_InitDefine init_delete_array_align; + +extern "C" void __cdecl __asan_delete_array_align( + __asan_win_new_delete_data* data, void* ptr, std::align_val_t 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::defined) { + __asan_win_new_delete_data data{}; + __asan_delete_array_align(&data, 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 --- /dev/null +++ b/compiler-rt/lib/asan/asan_win_delete_array_nothrow_thunk.cpp @@ -0,0 +1,39 @@ +//===-- 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" + +//////////////////////////////////////////////////////////////// +// 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 + +// 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 --- /dev/null +++ b/compiler-rt/lib/asan/asan_win_delete_array_size_align_thunk.cpp @@ -0,0 +1,49 @@ +//===-- 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" + +////////////////////////////////////////////////////////////////////////////////// +// 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 + +extern "C" void __cdecl __asan_delete_array_size_align( + __asan_win_new_delete_data* data, void* ptr, size_t size, + std::align_val_t align); + +// 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::defined && + __asan_InitDefine::defined) { + __asan_win_new_delete_data data{}; + __asan_delete_array_size_align(&data, 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 --- /dev/null +++ b/compiler-rt/lib/asan/asan_win_delete_array_size_thunk.cpp @@ -0,0 +1,47 @@ +//===-- 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" + +//////////////////////////////////////////////////////////////// +// 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 + +extern "C" void __cdecl __asan_delete_array_size( + __asan_win_new_delete_data* data, void* ptr, size_t size); + +// Avoid tailcall optimization to preserve stack frame. +#pragma optimize("", off) +void operator delete[](void* ptr, size_t size) noexcept { + if (__asan_InitDefine::defined && + __asan_InitDefine::defined) { + __asan_win_new_delete_data data{}; + __asan_delete_array_size(&data, 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 --- /dev/null +++ b/compiler-rt/lib/asan/asan_win_delete_array_thunk.cpp @@ -0,0 +1,48 @@ +//===-- 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" + +//////////////////////////////////////////////////////////////// +// 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 + +__asan_InitDefine init_delete_array; + +extern "C" void __cdecl __asan_delete_array(__asan_win_new_delete_data* data, + void* ptr); + +// Avoid tailcall optimization to preserve stack frame. +#pragma optimize("", off) +void operator delete[](void* ptr) noexcept { + if (__asan_InitDefine::defined) { + __asan_win_new_delete_data data{}; + __asan_delete_array(&data, 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 --- /dev/null +++ b/compiler-rt/lib/asan/asan_win_delete_scalar_align_nothrow_thunk.cpp @@ -0,0 +1,40 @@ +//===-- 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" + +////////////////////////////////////////////////////////////////////////////////// +// 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 + +// 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 --- /dev/null +++ b/compiler-rt/lib/asan/asan_win_delete_scalar_align_thunk.cpp @@ -0,0 +1,44 @@ +//===-- 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" + +////////////////////////////////////////////////////////////////////////////////// +// 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 + +__asan_InitDefine init_delete_scalar_align; + +extern "C" void __cdecl __asan_delete_align(__asan_win_new_delete_data* data, + void* ptr, std::align_val_t align); + +// Avoid tailcall optimization to preserve stack frame. +#pragma optimize("", off) +void operator delete(void* ptr, std::align_val_t align) noexcept { + __asan_win_new_delete_data data{}; + __asan_delete_align(&data, 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 --- /dev/null +++ b/compiler-rt/lib/asan/asan_win_delete_scalar_nothrow_thunk.cpp @@ -0,0 +1,39 @@ +//===-- 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" + +//////////////////////////////////////////////////////////////// +// 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 + +// 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 --- /dev/null +++ b/compiler-rt/lib/asan/asan_win_delete_scalar_size_align_thunk.cpp @@ -0,0 +1,47 @@ +//===-- 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" + +////////////////////////////////////////////////////////////////////////////////// +// 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 + +extern "C" void __cdecl __asan_delete_size_align( + __asan_win_new_delete_data* data, void* ptr, size_t size, + std::align_val_t align) noexcept; + +// 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::defined) { + __asan_win_new_delete_data data{}; + __asan_delete_size_align(&data, 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 --- /dev/null +++ b/compiler-rt/lib/asan/asan_win_delete_scalar_size_thunk.cpp @@ -0,0 +1,46 @@ +//===-- 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" + +//////////////////////////////////////////////////////////////// +// 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 + +extern "C" void __cdecl __asan_delete_size(__asan_win_new_delete_data* data, + void* ptr, size_t size); + +// Avoid tailcall optimization to preserve stack frame. +#pragma optimize("", off) +void operator delete(void* ptr, size_t size) noexcept { + if (__asan_InitDefine::defined) { + __asan_win_new_delete_data data{}; + __asan_delete_size(&data, 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 --- /dev/null +++ b/compiler-rt/lib/asan/asan_win_delete_scalar_thunk.cpp @@ -0,0 +1,44 @@ +//===-- 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" + +//////////////////////////////////////////////////////////////// +// 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 + +__asan_InitDefine init_delete_scalar; + +extern "C" void __cdecl __asan_delete(__asan_win_new_delete_data* data, + void* ptr); + +// Avoid tailcall optimization to preserve stack frame. +#pragma optimize("", off) +void operator delete(void* ptr) noexcept { + __asan_win_new_delete_data data{}; + __asan_delete(&data, 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 --- /dev/null +++ b/compiler-rt/lib/asan/asan_win_new_array_align_nothrow_thunk.cpp @@ -0,0 +1,50 @@ +//===-- 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" + +//////////////////////////////////////////////// +// clang-format off +// Aligned new() Fallback Ordering +// +// +----------------+ +// |new_scalar_align<--------------+ +// +----^-----------+ | +// | | +// +----+-------------------+ +---+-----------+ +// |new_scalar_align_nothrow| |new_array_align| +// +------------------------+ +---^-----------+ +// | +// +-----------+-----------+ +// |NEW_ARRAY_ALIGN_NOTHROW| +// +-----------------------+ +// clang-format on + +extern "C" void* __cdecl __asan_new_array_align_nothrow( + __asan_win_new_delete_data* data, size_t size, std::align_val_t align); + +// 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::defined && + __asan_InitDefine::defined) { + __asan_win_new_delete_data data{}; + return __asan_new_array_align_nothrow(&data, 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 --- /dev/null +++ b/compiler-rt/lib/asan/asan_win_new_array_align_thunk.cpp @@ -0,0 +1,46 @@ +//===-- 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" + +//////////////////////////////////////////////// +// clang-format off +// Aligned new() Fallback Ordering +// +// +----------------+ +// |new_scalar_align<--------------+ +// +----^-----------+ | +// | | +// +----+-------------------+ +---+-----------+ +// |new_scalar_align_nothrow| |NEW_ARRAY_ALIGN| +// +------------------------+ +---^-----------+ +// | +// +-----------+-----------+ +// |new_array_align_nothrow| +// +-----------------------+ +// clang-format on + +__asan_InitDefine init_new_array_align; + +extern "C" void* __cdecl __asan_new_array_align( + __asan_win_new_delete_data* data, size_t size, std::align_val_t 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::defined) { + __asan_win_new_delete_data data{}; + return __asan_new_array_align(&data, 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 --- /dev/null +++ b/compiler-rt/lib/asan/asan_win_new_array_nothrow_thunk.cpp @@ -0,0 +1,49 @@ +//===-- 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" + +//////////////////////////////////// +// clang-format off +// new() Fallback Ordering +// +// +----------+ +// |new_scalar<---------------+ +// +----^-----+ | +// | | +// +----+-------------+ +----+----+ +// |new_scalar_nothrow| |new_array| +// +------------------+ +----^----+ +// | +// +------------+----+ +// |NEW_ARRAY_NOTHROW| +// +-----------------+ +// clang-format on + +extern "C" void* __cdecl __asan_new_array_nothrow( + __asan_win_new_delete_data* data, 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::defined && + __asan_InitDefine::defined) { + __asan_win_new_delete_data data{}; + return __asan_new_array_nothrow(&data, 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 --- /dev/null +++ b/compiler-rt/lib/asan/asan_win_new_array_thunk.cpp @@ -0,0 +1,45 @@ +//===-- 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" + +//////////////////////////////////// +// clang-format off +// new() Fallback Ordering +// +// +----------+ +// |new_scalar<---------------+ +// +----^-----+ | +// | | +// +----+-------------+ +----+----+ +// |new_scalar_nothrow| |NEW_ARRAY| +// +------------------+ +----^----+ +// | +// +------------+----+ +// |new_array_nothrow| +// +-----------------+ +// clang-format on + +__asan_InitDefine init_new_array; + +extern "C" void* __cdecl __asan_new_array(__asan_win_new_delete_data* data, + size_t size); + +// Avoid tailcall optimization to preserve stack frame. +#pragma optimize("", off) +void* operator new[](size_t size) { + if (__asan_InitDefine::defined) { + __asan_win_new_delete_data data{}; + return __asan_new_array(&data, 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 --- /dev/null +++ b/compiler-rt/lib/asan/asan_win_new_delete.cpp @@ -0,0 +1,160 @@ +//===-- 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 + +#include "asan_allocator.h" +#include "asan_internal.h" +#include "asan_report.h" +#include "asan_stack.h" + +// Fake std::align_val_t to avoid including . +namespace std { +enum class align_val_t : size_t {}; +} + +using namespace __asan; + +struct __asan_win_new_delete_data { + size_t size; // Size of this struct (it travels over the DLL boundary). + int extra_context; // Number of extra frames we need to collect in the + // backtrace. + __sanitizer::uptr pc; + __sanitizer::uptr bp; + __sanitizer::uptr caller_pc; +}; + +#define OPERATOR_NEW_BODY(type, nothrow) \ + GET_STACK_TRACE_MALLOC_WIN(data->pc, data->bp, data->caller_pc, \ + data->extra_context); \ + 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_WIN(data->pc, data->bp, data->caller_pc, \ + data->extra_context); \ + 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_WIN(data->pc, data->bp, data->caller_pc, \ + data->extra_context); \ + asan_delete(ptr, 0, 0, &stack, type); + +#define OPERATOR_DELETE_BODY_SIZE(type) \ + GET_STACK_TRACE_FREE_WIN(data->pc, data->bp, data->caller_pc, \ + data->extra_context); \ + asan_delete(ptr, size, 0, &stack, type); + +#define OPERATOR_DELETE_BODY_ALIGN(type) \ + GET_STACK_TRACE_FREE_WIN(data->pc, data->bp, data->caller_pc, \ + data->extra_context); \ + asan_delete(ptr, 0, static_cast(align), &stack, type); + +#define OPERATOR_DELETE_BODY_SIZE_ALIGN(type) \ + GET_STACK_TRACE_FREE_WIN(data->pc, data->bp, data->caller_pc, \ + data->extra_context); \ + asan_delete(ptr, size, static_cast(align), &stack, type); + +extern "C" { +__declspec(dllexport) void *__cdecl __asan_new( + __asan_win_new_delete_data *const data, size_t const size) { + OPERATOR_NEW_BODY(FROM_NEW, false /*nothrow*/); +} + +__declspec(dllexport) void *__cdecl __asan_new_array( + __asan_win_new_delete_data *const data, size_t const size) { + OPERATOR_NEW_BODY(FROM_NEW_BR, false /*nothrow*/); +} + +__declspec(dllexport) void *__cdecl __asan_new_nothrow( + __asan_win_new_delete_data *const data, size_t const size) { + OPERATOR_NEW_BODY(FROM_NEW, true /*nothrow*/); +} + +__declspec(dllexport) void *__cdecl __asan_new_array_nothrow( + __asan_win_new_delete_data *const data, size_t const size) { + OPERATOR_NEW_BODY(FROM_NEW_BR, true /*nothrow*/); +} + +__declspec(dllexport) void *__cdecl __asan_new_align( + __asan_win_new_delete_data *const data, 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( + __asan_win_new_delete_data *const data, 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( + __asan_win_new_delete_data *const data, 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( + __asan_win_new_delete_data *const data, 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( + __asan_win_new_delete_data *const data, void *ptr) { + OPERATOR_DELETE_BODY(FROM_NEW); +} + +__declspec(dllexport) void __cdecl __asan_delete_array( + __asan_win_new_delete_data *const data, void *ptr) { + OPERATOR_DELETE_BODY(FROM_NEW_BR); +} + +__declspec(dllexport) void __cdecl __asan_delete_size( + __asan_win_new_delete_data *const data, void *ptr, size_t const size) { + OPERATOR_DELETE_BODY_SIZE(FROM_NEW); +} + +__declspec(dllexport) void __cdecl __asan_delete_array_size( + __asan_win_new_delete_data *const data, void *ptr, size_t const size) { + OPERATOR_DELETE_BODY_SIZE(FROM_NEW_BR); +} + +__declspec(dllexport) void __cdecl __asan_delete_align( + __asan_win_new_delete_data *const data, void *ptr, + std::align_val_t const align) { + OPERATOR_DELETE_BODY_ALIGN(FROM_NEW); +} + +__declspec(dllexport) void __cdecl __asan_delete_array_align( + __asan_win_new_delete_data *const data, void *ptr, + std::align_val_t const align) { + OPERATOR_DELETE_BODY_ALIGN(FROM_NEW_BR); +} + +__declspec(dllexport) void __cdecl __asan_delete_size_align( + __asan_win_new_delete_data *const data, 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( + __asan_win_new_delete_data *const data, 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 --- /dev/null +++ b/compiler-rt/lib/asan/asan_win_new_delete_thunk_common.h @@ -0,0 +1,99 @@ +//===-- 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. +//===----------------------------------------------------------------------===// + +#include "asan_win_thunk_common.h" +// Fake std::nothrow_t and std::align_val_t to avoid including . +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); +void operator delete[](void* ptr, std::align_val_t align); + +using __asan_win_new_delete_data = __asan_win_stack_data; + +//////////////////////////////////// +// clang-format off +// Fallback Ordering for new/delete +// +// +----------+ +----------------+ +// |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::defined to check whether that TU is +// included. +template +struct __asan_InitDefine { + __asan_InitDefine() { defined = 1; } + + static int defined; +}; + +template +int __asan_InitDefine::defined = 0; 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 --- /dev/null +++ b/compiler-rt/lib/asan/asan_win_new_scalar_align_nothrow_thunk.cpp @@ -0,0 +1,49 @@ +//===-- 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" + +//////////////////////////////////////////////// +// clang-format off +// Aligned new() Fallback Ordering +// +// +----------------+ +// |new_scalar_align<--------------+ +// +----^-----------+ | +// | | +// +----+-------------------+ +---+-----------+ +// |NEW_SCALAR_ALIGN_NOTHROW| |new_array_align| +// +------------------------+ +---^-----------+ +// | +// +-----------+-----------+ +// |new_array_align_nothrow| +// +-----------------------+ +// clang-format on + +extern "C" void* __cdecl __asan_new_align_nothrow( + __asan_win_new_delete_data* data, size_t size, std::align_val_t align); + +// 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::defined) { + __asan_win_new_delete_data data{}; + return __asan_new_align_nothrow(&data, 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 --- /dev/null +++ b/compiler-rt/lib/asan/asan_win_new_scalar_align_thunk.cpp @@ -0,0 +1,42 @@ +//===-- 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" + +//////////////////////////////////////////////// +// clang-format off +// Aligned new() Fallback Ordering +// +// +----------------+ +// |NEW_SCALAR_ALIGN<--------------+ +// +----^-----------+ | +// | | +// +----+-------------------+ +---+-----------+ +// |new_scalar_align_nothrow| |new_array_align| +// +------------------------+ +---^-----------+ +// | +// +-----------+-----------+ +// |new_array_align_nothrow| +// +-----------------------+ +// clang-format on + +__asan_InitDefine init_new_scalar_align; + +extern "C" void* __cdecl __asan_new_align(__asan_win_new_delete_data* data, + size_t size, std::align_val_t align); + +// Avoid tailcall optimization to preserve stack frame. +#pragma optimize("", off) +void* operator new(size_t size, std::align_val_t align) { + __asan_win_new_delete_data data{}; + return __asan_new_align(&data, 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 --- /dev/null +++ b/compiler-rt/lib/asan/asan_win_new_scalar_nothrow_thunk.cpp @@ -0,0 +1,48 @@ +//===-- 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" + +//////////////////////////////////// +// clang-format off +// new() Fallback Ordering +// +// +----------+ +// |new_scalar<---------------+ +// +----^-----+ | +// | | +// +----+-------------+ +----+----+ +// |NEW_SCALAR_NOTHROW| |new_array| +// +------------------+ +----^----+ +// | +// +------------+----+ +// |new_array_nothrow| +// +-----------------+ +// clang-format on + +extern "C" void* __cdecl __asan_new_nothrow(__asan_win_new_delete_data* data, + 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::defined) { + __asan_win_new_delete_data data{}; + return __asan_new_nothrow(&data, 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 --- /dev/null +++ b/compiler-rt/lib/asan/asan_win_new_scalar_thunk.cpp @@ -0,0 +1,42 @@ +//===-- 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" + +//////////////////////////////////// +// clang-format off +// new() Fallback Ordering +// +// +----------+ +// |NEW_SCALAR|<---------------+ +// +----^-----+ | +// | | +// +----+-------------+ +----+----+ +// |new_scalar_nothrow| |new_array| +// +------------------+ +----^----+ +// | +// +------------+----+ +// |new_array_nothrow| +// +-----------------+ +// clang-format on + +__asan_InitDefine init_new_scalar; + +extern "C" void* __cdecl __asan_new(__asan_win_new_delete_data* data, + size_t size); + +// Avoid tailcall optimization to preserve stack frame. +#pragma optimize("", off) +void* operator new(size_t size) { + __asan_win_new_delete_data data{}; + return __asan_new(&data, size); +} diff --git a/compiler-rt/lib/asan/asan_win_thunk_common.h b/compiler-rt/lib/asan/asan_win_thunk_common.h new file mode 100644 --- /dev/null +++ b/compiler-rt/lib/asan/asan_win_thunk_common.h @@ -0,0 +1,64 @@ +//===-- asan_win_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 common utilities to communicate between static and DLL +// portions of the ASAN runtime. +// +// This file must not include any core components of the ASAN runtime as it must +// be able to be included in the portions statically linked with the user +// program. +// +//===----------------------------------------------------------------------===// + +#pragma once + +namespace __sanitizer { +#if defined(_WIN64) +typedef unsigned long long uptr; +#else +typedef unsigned long uptr; +#endif +} // namespace __sanitizer + +extern "C" void* _ReturnAddress(void); +extern "C" void* _AddressOfReturnAddress(void); +#pragma intrinsic(_ReturnAddress) +#pragma intrinsic(_AddressOfReturnAddress) + +#define GET_CALLER_PC() (__sanitizer::uptr) _ReturnAddress() +#define GET_CURRENT_FRAME() \ + (((__sanitizer::uptr)_AddressOfReturnAddress()) + sizeof(__sanitizer::uptr)) + +__declspec(noinline) inline __sanitizer::uptr __asan_GetCurrentPc() { + return GET_CALLER_PC(); +} + +struct __asan_win_stack_data { + // Put calls to get pc, bp, and caller_pc in ctor arguments so they occur in + // the calling frame and don't rely on inlining to get correct stack + // information. + __forceinline explicit __asan_win_stack_data( + __sanitizer::uptr pc_arg = __asan_GetCurrentPc(), + __sanitizer::uptr bp_arg = GET_CURRENT_FRAME(), + __sanitizer::uptr caller_pc_arg = GET_CALLER_PC()) + : size(sizeof(__asan_win_stack_data)), + extra_context(2), // TODO: Should only need one - investigate why we + // need an extra frame. + pc(pc_arg), + bp(bp_arg), + caller_pc(caller_pc_arg) {} + + size_t size; // Size of this struct (it travels over the DLL boundary). + int extra_context; // Number of extra frames we need to collect in the + // backtrace. + __sanitizer::uptr pc; + __sanitizer::uptr bp; + __sanitizer::uptr caller_pc; +}; 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 --- /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@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@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; +} \ No newline at end of file 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 --- /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 /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@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 +#include + +int main() { + int *normal = new int; + int *debug = DEBUG_NEW int; + + delete normal; + delete debug; + + return 0; +} \ No newline at end of file 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 --- 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 @@ ~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]; 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 --- /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 --- /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_new_delete_replacement_all.cpp b/compiler-rt/test/asan/TestCases/Windows/operator_new_delete_replacement_all.cpp new file mode 100644 --- /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 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Windows/operator_new_delete_replacement_common.h @@ -0,0 +1,413 @@ +#include +#include + +// 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 class arena { +public: + void *alloc(const size_t size, const std::align_val_t al) { + return alloc(size, static_cast(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( + reinterpret_cast(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 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 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 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 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 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 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 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(); + test_allocations(); + 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 --- /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_replacement_array.cpp b/compiler-rt/test/asan/TestCases/Windows/operator_new_replacement_array.cpp new file mode 100644 --- /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 --- /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_uaf.cpp b/compiler-rt/test/asan/TestCases/Windows/operator_new_uaf.cpp --- 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