From faaf094ee16bcbfb2c8d612fdb9334bcdef2fd0a Mon Sep 17 00:00:00 2001 From: nicole mazzuca <83086508+strega-nil-ms@users.noreply.github.com> Date: Thu, 15 Dec 2022 13:41:42 -0800 Subject: [PATCH] Re-enable ASAN string annotations (#3164) Co-authored-by: Nicole Mazzuca --- stl/CMakeLists.txt | 1 + .../__msvc_sanitizer_annotate_container.hpp | 135 +++++++++ stl/inc/header-units.json | 1 + stl/inc/vector | 131 ++------ stl/inc/xmemory | 124 +++++++- stl/inc/xstring | 282 ++++++------------ .../GH_002030_asan_annotate_string/env.lst | 15 +- .../GH_002030_asan_annotate_string/test.cpp | 154 +++++++--- .../GH_002030_asan_annotate_vector/env.lst | 6 +- .../GH_002030_asan_annotate_vector/test.cpp | 47 ++- 10 files changed, 531 insertions(+), 365 deletions(-) create mode 100644 stl/inc/__msvc_sanitizer_annotate_container.hpp diff --git a/stl/CMakeLists.txt b/stl/CMakeLists.txt index 2d4a34d98..9307d1d30 100644 --- a/stl/CMakeLists.txt +++ b/stl/CMakeLists.txt @@ -8,6 +8,7 @@ set(HEADERS ${CMAKE_CURRENT_LIST_DIR}/inc/__msvc_format_ucd_tables.hpp ${CMAKE_CURRENT_LIST_DIR}/inc/__msvc_int128.hpp ${CMAKE_CURRENT_LIST_DIR}/inc/__msvc_iter_core.hpp + ${CMAKE_CURRENT_LIST_DIR}/inc/__msvc_sanitizer_annotate_container.hpp ${CMAKE_CURRENT_LIST_DIR}/inc/__msvc_system_error_abi.hpp ${CMAKE_CURRENT_LIST_DIR}/inc/__msvc_tzdb.hpp ${CMAKE_CURRENT_LIST_DIR}/inc/__msvc_xlocinfo_types.hpp diff --git a/stl/inc/__msvc_sanitizer_annotate_container.hpp b/stl/inc/__msvc_sanitizer_annotate_container.hpp new file mode 100644 index 000000000..db8cbd852 --- /dev/null +++ b/stl/inc/__msvc_sanitizer_annotate_container.hpp @@ -0,0 +1,135 @@ +// __msvc_sanitizer_annotate_container.hpp internal header + +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#pragma once +#ifndef __MSVC_SANITIZER_ANNOTATE_CONTAINER_HPP +#define __MSVC_SANITIZER_ANNOTATE_CONTAINER_HPP +#include +#if _STL_COMPILER_PREPROCESSOR + +#pragma pack(push, _CRT_PACKING) +#pragma warning(push, _STL_WARNING_LEVEL) +#pragma warning(disable : _STL_DISABLED_WARNINGS) +_STL_DISABLE_CLANG_WARNINGS +#pragma push_macro("new") +#undef new + +#if !defined(_M_CEE_PURE) && !(defined(_DISABLE_STRING_ANNOTATION) && defined(_DISABLE_VECTOR_ANNOTATION)) + +#ifdef __SANITIZE_ADDRESS__ + +#define _ACTIVATE_STRING_ANNOTATION +#define _INSERT_STRING_ANNOTATION +#define _ACTIVATE_VECTOR_ANNOTATION +#define _INSERT_VECTOR_ANNOTATION + +#elif defined(__clang__) // ^^^ __SANITIZE_ADDRESS__ / __clang__ vvv + +#if __has_feature(address_sanitizer) +#define _ACTIVATE_STRING_ANNOTATION +#define _INSERT_STRING_ANNOTATION +#define _ACTIVATE_VECTOR_ANNOTATION +#define _INSERT_VECTOR_ANNOTATION +#pragma comment(linker, "/INFERASANLIBS") +#endif // __has_feature(address_sanitizer) + +#else // ^^^ __clang__ / !__clang__ && !__SANITIZE_ADDRESS__ vvv + +#ifdef _ANNOTATE_STRING +#define _INSERT_STRING_ANNOTATION +#endif // _ANNOTATE_STRING +#ifdef _ANNOTATE_VECTOR +#define _INSERT_VECTOR_ANNOTATION +#endif // _ANNOTATE_VECTOR + +#endif // __SANITIZE_ADDRESS__ + +#ifdef _DISABLE_STRING_ANNOTATION +#undef _ACTIVATE_STRING_ANNOTATION +#undef _INSERT_STRING_ANNOTATION +#endif // _DISABLE_STRING_ANNOTATION +#ifdef _DISABLE_VECTOR_ANNOTATION +#undef _ACTIVATE_VECTOR_ANNOTATION +#undef _INSERT_VECTOR_ANNOTATION +#endif // _DISABLE_VECTOR_ANNOTATION + +#ifndef _INSERT_STRING_ANNOTATION +#pragma detect_mismatch("annotate_string", "0") +#endif // !_INSERT_STRING_ANNOTATION +#ifndef _INSERT_VECTOR_ANNOTATION +#pragma detect_mismatch("annotate_vector", "0") +#endif // !_INSERT_VECTOR_ANNOTATION + +#ifdef _ACTIVATE_STRING_ANNOTATION +#pragma comment(lib, "stl_asan") +#pragma detect_mismatch("annotate_string", "1") +#endif // _ACTIVATE_STRING_ANNOTATION +#ifdef _ACTIVATE_VECTOR_ANNOTATION +#pragma comment(lib, "stl_asan") +#pragma detect_mismatch("annotate_vector", "1") +#endif // _ACTIVATE_VECTOR_ANNOTATION + +#undef _ACTIVATE_STRING_ANNOTATION +#undef _ACTIVATE_VECTOR_ANNOTATION + +extern "C" { +#ifdef _INSERT_VECTOR_ANNOTATION +extern const bool _Asan_vector_should_annotate; +#endif + +#ifdef _INSERT_STRING_ANNOTATION +extern const bool _Asan_string_should_annotate; +#endif +} + +#if defined(_INSERT_VECTOR_ANNOTATION) || defined(_INSERT_STRING_ANNOTATION) +extern "C" { +void __cdecl __sanitizer_annotate_contiguous_container( + const void* _First, const void* _End, const void* _Old_last, const void* _New_last); +} + +#ifdef _M_ARM64EC +#pragma comment(linker, \ + "/alternatename:#__sanitizer_annotate_contiguous_container=#__sanitizer_annotate_contiguous_container_default") +#pragma comment(linker, \ + "/alternatename:__sanitizer_annotate_contiguous_container=__sanitizer_annotate_contiguous_container_default") +#pragma comment(linker, "/alternatename:#_Asan_vector_should_annotate=#_Asan_vector_should_annotate_default") +#pragma comment(linker, "/alternatename:_Asan_vector_should_annotate=_Asan_vector_should_annotate_default") +#pragma comment(linker, "/alternatename:#_Asan_string_should_annotate=#_Asan_string_should_annotate_default") +#pragma comment(linker, "/alternatename:_Asan_string_should_annotate=_Asan_string_should_annotate_default") +#elif defined(_M_HYBRID) +#pragma comment(linker, \ + "/alternatename:#__sanitizer_annotate_contiguous_container=#__sanitizer_annotate_contiguous_container_default") +#pragma comment(linker, \ + "/alternatename:___sanitizer_annotate_contiguous_container=___sanitizer_annotate_contiguous_container_default") +#pragma comment(linker, "/alternatename:#_Asan_vector_should_annotate=#_Asan_vector_should_annotate_default") +#pragma comment(linker, "/alternatename:__Asan_vector_should_annotate=__Asan_vector_should_annotate_default") +#pragma comment(linker, "/alternatename:#_Asan_string_should_annotate=#_Asan_string_should_annotate_default") +#pragma comment(linker, "/alternatename:__Asan_string_should_annotate=__Asan_string_should_annotate_default") +#elif defined(_M_IX86) +#pragma comment(linker, \ + "/alternatename:___sanitizer_annotate_contiguous_container=___sanitizer_annotate_contiguous_container_default") +#pragma comment(linker, "/alternatename:__Asan_vector_should_annotate=__Asan_vector_should_annotate_default") +#pragma comment(linker, "/alternatename:__Asan_string_should_annotate=__Asan_string_should_annotate_default") +#elif defined(_M_X64) || defined(_M_ARM) || defined(_M_ARM64) +#pragma comment(linker, \ + "/alternatename:__sanitizer_annotate_contiguous_container=__sanitizer_annotate_contiguous_container_default") +#pragma comment(linker, "/alternatename:_Asan_vector_should_annotate=_Asan_vector_should_annotate_default") +#pragma comment(linker, "/alternatename:_Asan_string_should_annotate=_Asan_string_should_annotate_default") +#else // ^^^ known architecture / unknown architecture vvv +#error Unknown architecture +#endif // ^^^ unknown architecture ^^^ + +#endif // insert asan annotations + +#endif // !_M_CEE_PURE && asan not disabled + +#pragma pop_macro("new") +_STL_RESTORE_CLANG_WARNINGS +#pragma warning(pop) +#pragma pack(pop) + +#endif // _STL_COMPILER_PREPROCESSOR +#endif // __MSVC_SANITIZER_ANNOTATE_CONTAINER_HPP diff --git a/stl/inc/header-units.json b/stl/inc/header-units.json index cb5aa43c8..612fae950 100644 --- a/stl/inc/header-units.json +++ b/stl/inc/header-units.json @@ -10,6 +10,7 @@ "__msvc_format_ucd_tables.hpp", "__msvc_int128.hpp", "__msvc_iter_core.hpp", + "__msvc_sanitizer_annotate_container.hpp", "__msvc_system_error_abi.hpp", "__msvc_tzdb.hpp", "__msvc_xlocinfo_types.hpp", diff --git a/stl/inc/vector b/stl/inc/vector index 94c7e48b3..cc77bb676 100644 --- a/stl/inc/vector +++ b/stl/inc/vector @@ -14,6 +14,8 @@ #include #endif // _HAS_CXX17 +#include <__msvc_sanitizer_annotate_container.hpp> + #pragma pack(push, _CRT_PACKING) #pragma warning(push, _STL_WARNING_LEVEL) #pragma warning(disable : _STL_DISABLED_WARNINGS) @@ -426,70 +428,6 @@ constexpr _Ty* _Unfancy_maybe_null(_Ty* _Ptr) noexcept { // do nothing for plain return _Ptr; } -#if !defined(_M_CEE_PURE) && !defined(_DISABLE_VECTOR_ANNOTATION) -#if defined(__SANITIZE_ADDRESS__) -#define _ACTIVATE_VECTOR_ANNOTATION -#define _INSERT_VECTOR_ANNOTATION -#elif defined(__clang__) && defined(__has_feature) // ^^^ __SANITIZE_ADDRESS__ / __clang__ vvv -#if __has_feature(address_sanitizer) -#define _ACTIVATE_VECTOR_ANNOTATION -#define _INSERT_VECTOR_ANNOTATION -#pragma comment(linker, "/INFERASANLIBS") -#endif // __has_feature(address_sanitizer) -#elif defined(_ANNOTATE_VECTOR) // ^^^ __clang__ / _ANNOTATE_VECTOR vvv -#define _INSERT_VECTOR_ANNOTATION -#endif // _ANNOTATE_VECTOR -#endif // !_M_CEE_PURE && !_DISABLE_VECTOR_ANNOTATION - -#ifdef _ACTIVATE_VECTOR_ANNOTATION -#pragma comment(lib, "stl_asan") -#pragma detect_mismatch("annotate_vector", "1") -#endif // _ACTIVATE_VECTOR_ANNOTATION - -#ifdef _INSERT_VECTOR_ANNOTATION -extern "C" { -void __cdecl __sanitizer_annotate_contiguous_container( - const void* _First, const void* _End, const void* _Old_last, const void* _New_last) noexcept; -extern const bool _Asan_vector_should_annotate; -} - -#if defined(_M_ARM64EC) -#pragma comment(linker, \ - "/alternatename:#__sanitizer_annotate_contiguous_container=#__sanitizer_annotate_contiguous_container_default") -#pragma comment(linker, \ - "/alternatename:__sanitizer_annotate_contiguous_container=__sanitizer_annotate_contiguous_container_default") -#pragma comment(linker, "/alternatename:#_Asan_vector_should_annotate=#_Asan_vector_should_annotate_default") -#pragma comment(linker, "/alternatename:_Asan_vector_should_annotate=_Asan_vector_should_annotate_default") -#elif defined(_M_HYBRID) -#pragma comment(linker, \ - "/alternatename:#__sanitizer_annotate_contiguous_container=#__sanitizer_annotate_contiguous_container_default") -#pragma comment(linker, \ - "/alternatename:___sanitizer_annotate_contiguous_container=___sanitizer_annotate_contiguous_container_default") -#pragma comment(linker, "/alternatename:#_Asan_vector_should_annotate=#_Asan_vector_should_annotate_default") -#pragma comment(linker, "/alternatename:__Asan_vector_should_annotate=__Asan_vector_should_annotate_default") -#elif defined(_M_IX86) -#pragma comment(linker, \ - "/alternatename:___sanitizer_annotate_contiguous_container=___sanitizer_annotate_contiguous_container_default") -#pragma comment(linker, "/alternatename:__Asan_vector_should_annotate=__Asan_vector_should_annotate_default") -#elif defined(_M_X64) || defined(_M_ARM) || defined(_M_ARM64) -#pragma comment(linker, \ - "/alternatename:__sanitizer_annotate_contiguous_container=__sanitizer_annotate_contiguous_container_default") -#pragma comment(linker, "/alternatename:_Asan_vector_should_annotate=_Asan_vector_should_annotate_default") -#else // ^^^ known architecture / unknown architecture vvv -#error Unknown architecture -#endif // ^^^ unknown architecture ^^^ - -template -_INLINE_VAR constexpr bool _Has_minimum_allocation_alignment = alignof(typename _Vec::value_type) >= _Asan_granularity; - -template -_INLINE_VAR constexpr bool - _Has_minimum_allocation_alignment<_Vec, void_t> = - _Vec::allocator_type::_Minimum_allocation_alignment >= _Asan_granularity; -#else // ^^^ _INSERT_VECTOR_ANNOTATION / !_INSERT_VECTOR_ANNOTATION vvv -#pragma detect_mismatch("annotate_vector", "0") -#endif // !_INSERT_VECTOR_ANNOTATION - _EXPORT_STD template > class vector { // varying size array of values private: @@ -539,29 +477,6 @@ private: _Apply_annotation(_My_data._Myfirst, _My_data._Myend, _My_data._Mylast, _My_data._Mylast + _Count); } - _NODISCARD static const void* _Get_aligned_first(const void* _First, const void* _End) noexcept { - const auto _CFirst = reinterpret_cast(_First); - const auto _CEnd = reinterpret_cast(_End); - const size_t _Capacity = static_cast(_CEnd - _CFirst); - - if (_Capacity >= _Asan_granularity) { - // We are guaranteed to have sufficient space to find an aligned address. - return reinterpret_cast( - (reinterpret_cast(_CFirst) + (_Asan_granularity - 1)) & ~(_Asan_granularity - 1)); - } - - uintptr_t _Alignment_offset = reinterpret_cast(_CFirst) & (_Asan_granularity - 1); - if (_Alignment_offset != 0) { - _Alignment_offset = _Asan_granularity - _Alignment_offset; - } - - if (_Capacity > _Alignment_offset) { - return _CFirst + _Alignment_offset; - } - - return nullptr; - } - static _CONSTEXPR20 void _Apply_annotation( pointer _First_, pointer _End_, pointer _Old_last_, pointer _New_last_) noexcept { _STL_INTERNAL_CHECK(_First_ != nullptr); @@ -579,24 +494,38 @@ private: return; } - const auto _First = reinterpret_cast(_Unfancy(_First_)); - const auto _End = reinterpret_cast(_Unfancy(_End_)); - const auto _Old_last = reinterpret_cast(_Unfancy(_Old_last_)); - const auto _New_last = reinterpret_cast(_Unfancy(_New_last_)); - if constexpr (_Has_minimum_allocation_alignment) { - __sanitizer_annotate_contiguous_container(_First, _End, _Old_last, _New_last); + const void* const _First = _STD _Unfancy(_First_); + const void* const _End = _STD _Unfancy(_End_); + const void* const _Old_last = _STD _Unfancy(_Old_last_); + const void* const _New_last = _STD _Unfancy(_New_last_); + if constexpr ((_Container_allocation_minimum_asan_alignment) >= _Asan_granularity) { + // old state: + // [_First, _Old_last) valid + // [_Old_last, _End) poison + // new state: + // [_First, _New_last) valid + // [_New_last, asan_aligned_after(_End)) poison + _CSTD __sanitizer_annotate_contiguous_container( + _First, _STD _Get_asan_aligned_after(_End), _Old_last, _New_last); } else { - const void* const _Aligned_first = _Get_aligned_first(_First, _End); - if (!_Aligned_first) { - // There is no aligned address within the underlying buffer; nothing to do. + const auto _Aligned = _STD _Get_asan_aligned_first_end(_First, _End); + if (_Aligned._First == _Aligned._End) { + // The buffer does not end at least one shadow memory section; nothing to do. return; } - const void* const _Aligned_old_last = _Old_last < _Aligned_first ? _Aligned_first : _Old_last; - const void* const _Aligned_new_last = _New_last < _Aligned_first ? _Aligned_first : _New_last; - const void* const _Aligned_end = _End < _Aligned_first ? _Aligned_first : _End; - __sanitizer_annotate_contiguous_container( - _Aligned_first, _Aligned_end, _Aligned_old_last, _Aligned_new_last); + const void* const _Old_fixed = _Aligned._Clamp_to_end(_Old_last); + const void* const _New_fixed = _Aligned._Clamp_to_end(_New_last); + + // old state: + // [_Aligned._First, _Old_fixed) valid + // [_Old_fixed, _Aligned._End) poison + // [_Aligned._End, _End) valid + // new state: + // [_Aligned._First, _New_fixed) valid + // [_New_fixed, _Aligned._End) poison + // [_Aligned._End, _End) valid + _CSTD __sanitizer_annotate_contiguous_container(_Aligned._First, _Aligned._End, _Old_fixed, _New_fixed); } } diff --git a/stl/inc/xmemory b/stl/inc/xmemory index d958f4221..b47090632 100644 --- a/stl/inc/xmemory +++ b/stl/inc/xmemory @@ -774,7 +774,127 @@ _NODISCARD constexpr allocation_result::pointe #endif // _HAS_CXX23 // The number of user bytes a single byte of ASAN shadow memory can track. -_INLINE_VAR constexpr size_t _Asan_granularity = 8; +_INLINE_VAR constexpr size_t _Asan_granularity = 8; +_INLINE_VAR constexpr size_t _Asan_granularity_mask = _Asan_granularity - 1; + +struct _Asan_aligned_pointers { + const void* _First; + const void* _End; + + _NODISCARD constexpr const void* _Clamp_to_end(const void* _Mid) const noexcept { + _STL_INTERNAL_CHECK(_Mid >= _First); + if (_Mid > _End) { + return _End; + } else { + return _Mid; + } + } +}; + +// The way that ASan shadow memory works, each eight byte block of memory ("shadow memory section") +// has a single byte to mark it as either poison or valid. +// Each section has 0 to 8 "valid" bytes followed by poison bytes, so: +// ``` +// [ v v v p p p p p ] +// ``` +// or +// ``` +// [ v v v v v v v v ] +// ``` +// are okay, but +// ``` +// [ p p p p v v v v ] +// ``` +// is not. +// +// This function exists to fix up `first` and `end` pointers so that one can call +// `__sanitizer_annotate_contiguous_container`: +// +// - `__sanitizer_annotate_contiguous_container` checks that `first` is aligned to an 8-byte boundary +// - if `end` is not aligned to an 8-byte boundary, `__sanitizer_annotate_contiguous_container` still poisons the +// remaining bytes in the shadow memory section. +// +// Because of the second property, we can only mark poison up to the final aligned address before the true `last`. +// Otherwise, we'd poison the memory _after_ `last` as well. +// For the first property, we can assume that everything before `first` in the shadow memory section is valid +// (since otherwise we couldn't mark `first` valid), and so we just return back the first address in +// `first`'s shadow memory section. +// +// ### Example +// +// ```cpp +// struct alignas(8) cat { +// int meow; // bytes [0, 4) +// char buffer[16]; // bytes [4, 20) +// int purr; // bytes [20, 24) +// }; +// ``` +// +// First, `meow` and `purr` are just regular data members, not container buffers, so they _must_ be valid. +// Then, assume we want to poison all of `buffer`. +// This would mean that, in a perfect world, we want something like: +// +// ``` +// | meow | buffer | purr | +// [ v v v v p p p p ][ p p p p p p p p ][ p p p p v v v v ] +// sm1 sm2 sm3 +// ``` +// +// However, note that by the rules above, `sm3` is not a valid shadow memory section; we always need +// the valid bytes to come before the poison bytes. Thus, the closest we can actually get to it is: +// +// ``` +// | meow | buffer | purr | +// [ v v v v p p p p ][ p p p p p p p p ][ v v v v v v v v ] +// sm1 sm2 sm3 +// ``` +// +// We call `aligned = _Get_asan_aligned_first_end(cat.buffer, cat.buffer + 16);`, and we get back +// +// ```cpp +// aligned = { +// ._First = &cat.meow, +// ._End = cat.buffer + 12, +// }; +// ``` +// +// Then, we poison as much of buffer as we can via +// +// ```cpp +// __sanitizer_annotate_contiguous_container( +// aligned._First, +// aligned._End, +// cat.buffer, +// aligned._Clamp_to_end(cat.buffer + 16)); +// ``` +// +// We are allowed to assume that `&cat.meow` is valid, since otherwise `cat.buffer + [0, 4)` could not be valid. +// We cannot poison up to `cat.buffer + 16`, since then `&purr` could not be valid. +// Thus, this results in the shadow memory state from the second example. +_NODISCARD inline _Asan_aligned_pointers _Get_asan_aligned_first_end( + const void* const _First, const void* const _End) noexcept { + return { + reinterpret_cast(reinterpret_cast(_First) & ~_Asan_granularity_mask), + reinterpret_cast(reinterpret_cast(_End) & ~_Asan_granularity_mask), + }; +} + +// When we can assume that the allocator we are using will always align allocations to the 8-byte, +// we can simply push the `_End` pointer to the end of the shadow memory section. +// This is _not_ safe in general (see _Get_asan_aligned_first_end's comment for why). +_NODISCARD inline const void* _Get_asan_aligned_after(const void* const _End) noexcept { + return reinterpret_cast( + (reinterpret_cast(_End) + _Asan_granularity_mask) & ~_Asan_granularity_mask); +} + +template +_INLINE_VAR constexpr size_t _Container_allocation_minimum_asan_alignment = alignof(typename _Container::value_type); + +template +_INLINE_VAR constexpr size_t _Container_allocation_minimum_asan_alignment<_Container, + void_t> = + (_STD max)( + alignof(typename _Container::value_type), _Container::allocator_type::_Minimum_asan_allocation_alignment); _EXPORT_STD template class allocator { @@ -862,7 +982,7 @@ public: } #endif // _HAS_DEPRECATED_ALLOCATOR_MEMBERS - static constexpr size_t _Minimum_allocation_alignment = _Asan_granularity; + static constexpr size_t _Minimum_asan_allocation_alignment = _Asan_granularity; }; #if _HAS_DEPRECATED_ALLOCATOR_VOID || _HAS_DEPRECATED_ALLOCATOR_MEMBERS diff --git a/stl/inc/xstring b/stl/inc/xstring index c77dcc681..d159988e4 100644 --- a/stl/inc/xstring +++ b/stl/inc/xstring @@ -17,6 +17,8 @@ #include #endif // _HAS_CXX17 +#include <__msvc_sanitizer_annotate_container.hpp> + #pragma pack(push, _CRT_PACKING) #pragma warning(push, _STL_WARNING_LEVEL) #pragma warning(disable : _STL_DISABLED_WARNINGS) @@ -2350,73 +2352,6 @@ struct _String_constructor_rvalue_allocator_tag { _Xlength_error("string too long"); } -#if 0 // TRANSITION, VSO-1586016: String annotations disabled temporarily. -#if !defined(_M_CEE_PURE) && !defined(_DISABLE_STRING_ANNOTATION) -#if defined(__SANITIZE_ADDRESS__) -#define _ACTIVATE_STRING_ANNOTATION -#define _INSERT_STRING_ANNOTATION -#elif defined(__clang__) && defined(__has_feature) // ^^^ __SANITIZE_ADDRESS__ / __clang__ vvv -#if __has_feature(address_sanitizer) -#define _ACTIVATE_STRING_ANNOTATION -#define _INSERT_STRING_ANNOTATION -#pragma comment(linker, "/INFERASANLIBS") -#endif // __has_feature(address_sanitizer) -#elif defined(_ANNOTATE_STRING) // ^^^ __clang__ / _ANNOTATE_STRING vvv -#define _INSERT_STRING_ANNOTATION -#endif // _ANNOTATE_STRING -#endif // !_M_CEE_PURE && !_DISABLE_STRING_ANNOTATION - -#ifdef _ACTIVATE_STRING_ANNOTATION -#pragma comment(lib, "stl_asan") -#pragma detect_mismatch("annotate_string", "1") -#endif // _ACTIVATE_STRING_ANNOTATION -#endif // TRANSITION, VSO-1586016 - -#ifdef _INSERT_STRING_ANNOTATION -extern "C" { -void __cdecl __sanitizer_annotate_contiguous_container( - const void* _First, const void* _End, const void* _Old_last, const void* _New_last) noexcept; -extern const bool _Asan_string_should_annotate; -} - -#if defined(_M_ARM64EC) -#pragma comment(linker, \ - "/alternatename:#__sanitizer_annotate_contiguous_container=#__sanitizer_annotate_contiguous_container_default") -#pragma comment(linker, \ - "/alternatename:__sanitizer_annotate_contiguous_container=__sanitizer_annotate_contiguous_container_default") -#pragma comment(linker, "/alternatename:#_Asan_string_should_annotate=#_Asan_string_should_annotate_default") -#pragma comment(linker, "/alternatename:_Asan_string_should_annotate=_Asan_string_should_annotate_default") -#elif defined(_M_HYBRID) -#pragma comment(linker, \ - "/alternatename:#__sanitizer_annotate_contiguous_container=#__sanitizer_annotate_contiguous_container_default") -#pragma comment(linker, \ - "/alternatename:___sanitizer_annotate_contiguous_container=___sanitizer_annotate_contiguous_container_default") -#pragma comment(linker, "/alternatename:#_Asan_string_should_annotate=#_Asan_string_should_annotate_default") -#pragma comment(linker, "/alternatename:__Asan_string_should_annotate=__Asan_string_should_annotate_default") -#elif defined(_M_IX86) -#pragma comment(linker, \ - "/alternatename:___sanitizer_annotate_contiguous_container=___sanitizer_annotate_contiguous_container_default") -#pragma comment(linker, "/alternatename:__Asan_string_should_annotate=__Asan_string_should_annotate_default") -#elif defined(_M_X64) || defined(_M_ARM) || defined(_M_ARM64) -#pragma comment(linker, \ - "/alternatename:__sanitizer_annotate_contiguous_container=__sanitizer_annotate_contiguous_container_default") -#pragma comment(linker, "/alternatename:_Asan_string_should_annotate=_Asan_string_should_annotate_default") -#else // ^^^ known architecture / unknown architecture vvv -#error Unknown architecture -#endif // ^^^ unknown architecture ^^^ - -template -_INLINE_VAR constexpr bool _Has_minimum_allocation_alignment_string = - alignof(typename _String::value_type) >= _Asan_granularity; - -template -_INLINE_VAR constexpr bool _Has_minimum_allocation_alignment_string<_String, - void_t> = - _String::allocator_type::_Minimum_allocation_alignment >= _Asan_granularity; -#else // ^^^ _INSERT_STRING_ANNOTATION / !_INSERT_STRING_ANNOTATION vvv -#pragma detect_mismatch("annotate_string", "0") -#endif // !_INSERT_STRING_ANNOTATION - #if _HAS_CXX23 && defined(__cpp_lib_concepts) // TRANSITION, GH-395 template concept _Contiguous_range_of = @@ -2510,91 +2445,79 @@ private: _Apply_annotation(_My_data._Myptr(), _My_data._Myres, _My_data._Mysize, _My_data._Myres); } - _CONSTEXPR20 void _Modify_annotation(const difference_type _Count) const noexcept { - // Extends/shrinks the annotated range by _Count - if (_Count == 0) { + _CONSTEXPR20 void _Modify_annotation(const size_type _Old_size, const size_type _New_size) const noexcept { + if (_Old_size == _New_size) { return; } auto& _My_data = _Mypair._Myval2; - _Apply_annotation( - _My_data._Myptr(), _My_data._Myres, _My_data._Mysize, static_cast(_My_data._Mysize + _Count)); + _Apply_annotation(_My_data._Myptr(), _My_data._Myres, _Old_size, _New_size); } - _NODISCARD static const void* _Get_aligned_first(const void* _First, const size_type _Capacity) noexcept { - const char* _CFirst = reinterpret_cast(_First); - - if (_Capacity >= _Asan_granularity) { // We are guaranteed to have sufficient space to find an aligned address - return reinterpret_cast( - (reinterpret_cast(_CFirst) + (_Asan_granularity - 1)) & ~(_Asan_granularity - 1)); - } - - uintptr_t _Alignment_offset = reinterpret_cast(_CFirst) & (_Asan_granularity - 1); - if (_Alignment_offset != 0) { - _Alignment_offset = _Asan_granularity - _Alignment_offset; - } - - if (_Capacity > _Alignment_offset) { - return _CFirst + _Alignment_offset; - } - - return nullptr; - } - - static _CONSTEXPR20 void _Apply_annotation(const value_type* _Ptr, const size_type _Capacity, + static _CONSTEXPR20 void _Apply_annotation(const value_type* const _First, const size_type _Capacity, const size_type _Old_size, const size_type _New_size) noexcept { #if _HAS_CXX20 if (_STD is_constant_evaluated()) { return; } #endif // _HAS_CXX20 - - if (!_Asan_string_should_annotate) { + // Don't annotate small strings; only annotate on the heap. + if (_Capacity == _BUF_SIZE - 1 || !_Asan_string_should_annotate) { return; } - // We need to check whether we have a misaligned SSO buffer because of the proxy in `_Container_base` (e.g. x86) - if constexpr (_Memcpy_val_offset % _Asan_granularity != 0) { - const uintptr_t _Alignment_offset = reinterpret_cast(_Ptr) & (_Asan_granularity - 1); - if (_Alignment_offset != 0 && _Capacity == _BUF_SIZE - 1) { - return; - } - } + // Note that `_Capacity`, `_Old_size`, and `_New_size` do not include the null terminator + const void* const _End = _First + _Capacity + 1; + const void* const _Old_last = _First + _Old_size + 1; + const void* const _New_last = _First + _New_size + 1; - // Needs to consider the null terminator - const char* _First = reinterpret_cast(_Ptr); - const char* _End = reinterpret_cast(_Ptr + _Capacity + 1); - const char* _Old_last = reinterpret_cast(_Ptr + _Old_size + 1); - const char* _New_last = reinterpret_cast(_Ptr + _New_size + 1); - if constexpr (_Has_minimum_allocation_alignment_string) { - __sanitizer_annotate_contiguous_container(_First, _End, _Old_last, _New_last); + constexpr bool _Large_string_always_asan_aligned = + (_Container_allocation_minimum_asan_alignment) >= _Asan_granularity; + + // for the non-aligned buffer options, the buffer must always have size >= 9 bytes, + // so it will always end at least one shadow memory section. + + _Asan_aligned_pointers _Aligned; + if constexpr (_Large_string_always_asan_aligned) { + _Aligned = {_First, _STD _Get_asan_aligned_after(_End)}; } else { - const void* _Aligned_first = _Get_aligned_first(_First, _Capacity + 1); - if (!_Aligned_first) { - // There is no aligned address within the underlying buffer. Nothing to do - return; - } - - const void* _Aligned_old_last = _Old_last < _Aligned_first ? _Aligned_first : _Old_last; - const void* _Aligned_new_last = _New_last < _Aligned_first ? _Aligned_first : _New_last; - const void* _Aligned_end = _End < _Aligned_first ? _Aligned_first : _End; - __sanitizer_annotate_contiguous_container( - _Aligned_first, _Aligned_end, _Aligned_old_last, _Aligned_new_last); + _Aligned = _STD _Get_asan_aligned_first_end(_First, _End); } + const void* const _Old_fixed = _Aligned._Clamp_to_end(_Old_last); + const void* const _New_fixed = _Aligned._Clamp_to_end(_New_last); + + // --- always aligned case --- + // old state: + // [_First, _Old_last) valid + // [_Old_last, asan_aligned_after(_End)) poison + // new state: + // [_First, _New_last) valid + // [_New_last, asan_aligned_after(_End)) poison + + // --- sometimes non-aligned case --- + // old state: + // [_Aligned._First, _Old_fixed) valid + // [_Old_fixed, _Aligned._End) poison + // [_Aligned._End, _End) valid + // new state: + // [_Aligned._First, _New_fixed) valid + // [_New_fixed, _Aligned._End) poison + // [_Aligned._End, _End) valid + _CSTD __sanitizer_annotate_contiguous_container(_Aligned._First, _Aligned._End, _Old_fixed, _New_fixed); } -#define _ASAN_STRING_MODIFY(n) _Modify_annotation((n)) -#define _ASAN_STRING_REMOVE(_Str) (_Str)._Remove_annotation() -#define _ASAN_STRING_CREATE(_Str) (_Str)._Create_annotation() +#define _ASAN_STRING_REMOVE(_Str) (_Str)._Remove_annotation() +#define _ASAN_STRING_CREATE(_Str) (_Str)._Create_annotation() +#define _ASAN_STRING_MODIFY(_Str, _Old_size, _New_size) (_Str)._Modify_annotation(_Old_size, _New_size) #else // ^^^ _INSERT_STRING_ANNOTATION / !_INSERT_STRING_ANNOTATION vvv -#define _ASAN_STRING_MODIFY(n) #define _ASAN_STRING_REMOVE(_Str) #define _ASAN_STRING_CREATE(_Str) +#define _ASAN_STRING_MODIFY(_Str, _Old_size, _New_size) #endif // !_INSERT_STRING_ANNOTATION public: - _CONSTEXPR20 basic_string() noexcept(is_nothrow_default_constructible_v<_Alty>) - : _Mypair(_Zero_then_variadic_args_t{}) { + _CONSTEXPR20 + basic_string() noexcept(is_nothrow_default_constructible_v<_Alty>) : _Mypair(_Zero_then_variadic_args_t{}) { _Mypair._Myval2._Alloc_proxy(_GET_PROXY_ALLOCATOR(_Alty, _Getal())); _Tidy_init(); } @@ -2730,6 +2653,7 @@ private: if (_Count < _BUF_SIZE) { _My_data._Mysize = _Count; _My_data._Myres = _BUF_SIZE - 1; + if constexpr (_Strat == _Construct_strategy::_From_char) { _Traits::assign(_My_data._Bx._Buf, _Count, _Arg); _Traits::assign(_My_data._Bx._Buf[_Count], _Elem()); @@ -2744,7 +2668,6 @@ private: #endif // !_INSERT_STRING_ANNOTATION } - _ASAN_STRING_CREATE(*this); _Proxy._Release(); return; } @@ -2947,7 +2870,7 @@ public: _My_data._Alloc_proxy(_GET_PROXY_ALLOCATOR(_Alty, _Getal())); // throws, hereafter nothrow in this block _Take_contents(_Left); const auto _Ptr = _My_data._Myptr(); - _ASAN_STRING_MODIFY(static_cast(_Right_size)); + _ASAN_STRING_MODIFY(*this, _Left_size, _New_size); _Traits::copy(_Ptr + _Left_size, _Right_data._Myptr(), _Right_size + 1); _My_data._Mysize = _New_size; return; @@ -2968,7 +2891,7 @@ public: _My_data._Alloc_proxy(_GET_PROXY_ALLOCATOR(_Alty, _Getal())); // throws, hereafter nothrow in this block _Take_contents(_Right); const auto _Ptr = _Unfancy(_My_data._Bx._Ptr); - _ASAN_STRING_MODIFY(static_cast(_Left_size)); + _ASAN_STRING_MODIFY(*this, _Right_size, _New_size); _Traits::move(_Ptr + _Left_size, _Ptr, _Right_size + 1); _Traits::copy(_Ptr, _Left_data._Myptr(), _Left_size); _My_data._Mysize = _New_size; @@ -3042,7 +2965,6 @@ public: _My_data._Myres = _Res - 1; _ASAN_STRING_CREATE(*this); } else { - _ASAN_STRING_MODIFY(static_cast(_Size)); _Traits::copy(_My_data._Bx._Buf, _Right, _Res); _My_data._Mysize = _Size; _My_data._Myres = _BUF_SIZE - 1; @@ -3131,6 +3053,7 @@ private: auto& _My_data = _Mypair._Myval2; auto& _Right_data = _Right._Mypair._Myval2; +#if !defined(_INSERT_STRING_ANNOTATION) if constexpr (_Can_memcpy_val) { #if _HAS_CXX20 if (!_STD is_constant_evaluated()) @@ -3145,25 +3068,12 @@ private: } #endif // _ITERATOR_DEBUG_LEVEL != 0 -#ifdef _INSERT_STRING_ANNOTATION - if (!_Right_data._Large_string_engaged()) { - _ASAN_STRING_REMOVE(_Right); - } -#endif // _INSERT_STRING_ANNOTATION - _Memcpy_val_from(_Right); - -#ifdef _INSERT_STRING_ANNOTATION - if (!_Right_data._Large_string_engaged()) { - _ASAN_STRING_REMOVE(_Right); - _ASAN_STRING_CREATE(*this); - } -#endif // _INSERT_STRING_ANNOTATION - _Right._Tidy_init(); return; } } +#endif // !defined(_INSERT_STRING_ANNOTATION) if (_Right_data._Large_string_engaged()) { // steal buffer _Construct_in_place(_My_data._Bx._Ptr, _Right_data._Bx._Ptr); @@ -3175,8 +3085,10 @@ private: _Right_data._Orphan_all(); } - _My_data._Mysize = _Right_data._Mysize; _My_data._Myres = _Right_data._Myres; + _My_data._Mysize = _Right_data._Mysize; + + _Right._Tidy_init(); } @@ -3265,6 +3177,7 @@ private: _CONSTEXPR20 void _Copy_assign_val_from_small(const basic_string& _Right) { // TRANSITION, VSO-761321; inline into only caller when that's fixed _Tidy_deallocate(); +#if !defined(_INSERT_STRING_ANNOTATION) if constexpr (_Can_memcpy_val) { #if _HAS_CXX20 if (!_STD is_constant_evaluated()) @@ -3274,6 +3187,7 @@ private: return; } } +#endif // !defined(_INSERT_STRING_ANNOTATION) auto& _My_data = _Mypair._Myval2; auto& _Right_data = _Right._Mypair._Myval2; @@ -3345,7 +3259,7 @@ public: #endif // _HAS_CXX23 _CONSTEXPR20 basic_string& operator=(const _Elem _Ch) { // assign {_Ch, _Elem()} - _ASAN_STRING_MODIFY(static_cast(1 - _Mypair._Myval2._Mysize)); + _ASAN_STRING_MODIFY(*this, _Mypair._Myval2._Mysize, 1); _Mypair._Myval2._Mysize = 1; _Elem* const _Ptr = _Mypair._Myval2._Myptr(); _Traits::assign(_Ptr[0], _Ch); @@ -3405,7 +3319,7 @@ public: // append [_Ptr, _Ptr + _Count) const size_type _Old_size = _Mypair._Myval2._Mysize; if (_Count <= _Mypair._Myval2._Myres - _Old_size) { - _ASAN_STRING_MODIFY(static_cast(_Count)); + _ASAN_STRING_MODIFY(*this, _Old_size, _Old_size + _Count); _Mypair._Myval2._Mysize = _Old_size + _Count; _Elem* const _Old_ptr = _Mypair._Myval2._Myptr(); _Traits::move(_Old_ptr + _Old_size, _Ptr, _Count); @@ -3432,7 +3346,7 @@ public: // append _Count * _Ch const size_type _Old_size = _Mypair._Myval2._Mysize; if (_Count <= _Mypair._Myval2._Myres - _Old_size) { - _ASAN_STRING_MODIFY(static_cast(_Count)); + _ASAN_STRING_MODIFY(*this, _Old_size, _Old_size + _Count); _Mypair._Myval2._Mysize = _Old_size + _Count; _Elem* const _Old_ptr = _Mypair._Myval2._Myptr(); _Traits::assign(_Old_ptr + _Old_size, _Count, _Ch); @@ -3510,7 +3424,7 @@ public: _In_reads_(_Count) const _Elem* const _Ptr, _CRT_GUARDOVERFLOW const size_type _Count) { // assign [_Ptr, _Ptr + _Count) if (_Count <= _Mypair._Myval2._Myres) { - _ASAN_STRING_MODIFY(static_cast(_Count - _Mypair._Myval2._Mysize)); + _ASAN_STRING_MODIFY(*this, _Mypair._Myval2._Mysize, _Count); _Elem* const _Old_ptr = _Mypair._Myval2._Myptr(); _Mypair._Myval2._Mysize = _Count; _Traits::move(_Old_ptr, _Ptr, _Count); @@ -3534,7 +3448,7 @@ public: _CONSTEXPR20 basic_string& assign(_CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch) { // assign _Count * _Ch if (_Count <= _Mypair._Myval2._Myres) { - _ASAN_STRING_MODIFY(static_cast(_Count - _Mypair._Myval2._Mysize)); + _ASAN_STRING_MODIFY(*this, _Mypair._Myval2._Mysize, _Count); _Elem* const _Old_ptr = _Mypair._Myval2._Myptr(); _Mypair._Myval2._Mysize = _Count; _Traits::assign(_Old_ptr, _Count, _Ch); @@ -3634,7 +3548,7 @@ public: #endif // _HAS_CXX20 if (_Check_overlap) { - _ASAN_STRING_MODIFY(static_cast(_Count)); + _ASAN_STRING_MODIFY(*this, _Old_size, _Old_size + _Count); _Mypair._Myval2._Mysize = _Old_size + _Count; _Elem* const _Old_ptr = _Mypair._Myval2._Myptr(); _Elem* const _Insert_at = _Old_ptr + _Off; @@ -3679,7 +3593,7 @@ public: _Mypair._Myval2._Check_offset(_Off); const size_type _Old_size = _Mypair._Myval2._Mysize; if (_Count <= _Mypair._Myval2._Myres - _Old_size) { - _ASAN_STRING_MODIFY(static_cast(_Count)); + _ASAN_STRING_MODIFY(*this, _Old_size, _Old_size + _Count); _Mypair._Myval2._Mysize = _Old_size + _Count; _Elem* const _Old_ptr = _Mypair._Myval2._Myptr(); _Elem* const _Insert_at = _Old_ptr + _Off; @@ -3773,7 +3687,7 @@ private: _Elem* const _Erase_at = _My_ptr + _Off; const size_type _New_size = _Old_size - _Count; _Traits::move(_Erase_at, _Erase_at + _Count, _New_size - _Off + 1); // move suffix + null up - _ASAN_STRING_MODIFY(-static_cast(_Count)); + _ASAN_STRING_MODIFY(*this, _Old_size, _New_size); _Mypair._Myval2._Mysize = _New_size; return *this; } @@ -3852,11 +3766,14 @@ public: const size_type _Old_size = _Mypair._Myval2._Mysize; const size_type _Suffix_size = _Old_size - _Nx - _Off + 1; if (_Count < _Nx) { // suffix shifts backwards; we don't have to move anything out of the way - _Mypair._Myval2._Mysize = _Old_size - (_Nx - _Count); _Elem* const _Old_ptr = _Mypair._Myval2._Myptr(); _Elem* const _Insert_at = _Old_ptr + _Off; _Traits::move(_Insert_at, _Ptr, _Count); _Traits::move(_Insert_at + _Count, _Insert_at + _Nx, _Suffix_size); + + const auto _New_size = _Old_size - (_Nx - _Count); + _ASAN_STRING_MODIFY(*this, _Old_size, _New_size); + _Mypair._Myval2._Mysize = _New_size; return *this; } @@ -3869,6 +3786,7 @@ public: #endif // _HAS_CXX20 { if (_Growth <= _Mypair._Myval2._Myres - _Old_size) { // growth fits + _ASAN_STRING_MODIFY(*this, _Old_size, _Old_size + _Growth); _Mypair._Myval2._Mysize = _Old_size + _Growth; _Elem* const _Old_ptr = _Mypair._Myval2._Myptr(); _Elem* const _Insert_at = _Old_ptr + _Off; @@ -3924,8 +3842,10 @@ public: const size_type _Old_size = _Mypair._Myval2._Mysize; if (_Count < _Nx || _Count - _Nx <= _Mypair._Myval2._Myres - _Old_size) { // either we are shrinking, or the growth fits - _Mypair._Myval2._Mysize = _Old_size + _Count - _Nx; // may temporarily overflow; - // OK because size_type must be unsigned + // may temporarily overflow; OK because size_type must be unsigned + const auto _New_size = _Old_size + _Count - _Nx; + _ASAN_STRING_MODIFY(*this, _Old_size, _New_size); + _Mypair._Myval2._Mysize = _New_size; _Elem* const _Old_ptr = _Mypair._Myval2._Myptr(); _Elem* const _Insert_at = _Old_ptr + _Off; _Traits::move(_Insert_at + _Count, _Insert_at + _Nx, _Old_size - _Nx - _Off + 1); @@ -4179,7 +4099,7 @@ public: _CONSTEXPR20 void push_back(const _Elem _Ch) { // insert element at end const size_type _Old_size = _Mypair._Myval2._Mysize; if (_Old_size < _Mypair._Myval2._Myres) { - _ASAN_STRING_MODIFY(1); + _ASAN_STRING_MODIFY(*this, _Old_size, _Old_size + 1); _Mypair._Myval2._Mysize = _Old_size + 1; _Elem* const _Ptr = _Mypair._Myval2._Myptr(); _Traits::assign(_Ptr[_Old_size], _Ch); @@ -4385,8 +4305,10 @@ public: // exchange a string in large mode with one in small mode const pointer _Ptr = _Starts_large._Bx._Ptr; _Destroy_in_place(_Starts_large._Bx._Ptr); + _Starts_large._Activate_SSO_buffer(); _Traits::copy(_Starts_large._Bx._Buf, _Starts_small._Bx._Buf, _BUF_SIZE); + _Construct_in_place(_Starts_small._Bx._Ptr, _Ptr); } @@ -4396,19 +4318,8 @@ public: const bool _My_large = _My_data._Large_string_engaged(); const bool _Right_large = _Right_data._Large_string_engaged(); -#ifdef _INSERT_STRING_ANNOTATION - if (_My_large && _Right_large) { - // nothing - } else if (_My_large) { - _ASAN_STRING_REMOVE(_Right); - } else if (_Right_large) { - _ASAN_STRING_REMOVE(*this); - } else { - _ASAN_STRING_REMOVE(_Right); - _ASAN_STRING_REMOVE(*this); - } -#endif // _INSERT_STRING_ANNOTATION +#if !defined(_INSERT_STRING_ANNOTATION) if constexpr (_Can_memcpy_val) { #if _HAS_CXX20 if (!_STD is_constant_evaluated()) @@ -4423,38 +4334,22 @@ public: _CSTD memcpy(_My_data_mem, _Right_data_mem, _Memcpy_val_size); _CSTD memcpy(_Right_data_mem, _Temp_mem, _Memcpy_val_size); -#ifdef _INSERT_STRING_ANNOTATION - if (_My_large && _Right_large) { - // nothing - } else if (_My_large) { - _ASAN_STRING_CREATE(*this); - } else if (_Right_large) { - _ASAN_STRING_CREATE(_Right); - } else { - _ASAN_STRING_CREATE(_Right); - _ASAN_STRING_CREATE(*this); - } -#endif // _INSERT_STRING_ANNOTATION - return; } } +#endif // !defined(_INSERT_STRING_ANNOTATION) if (_My_large && _Right_large) { // swap buffers, iterators preserved _Swap_adl(_My_data._Bx._Ptr, _Right_data._Bx._Ptr); } else if (_My_large) { // swap large with small _Swap_bx_large_with_small(_My_data, _Right_data); - _ASAN_STRING_CREATE(*this); } else if (_Right_large) { // swap small with large _Swap_bx_large_with_small(_Right_data, _My_data); - _ASAN_STRING_CREATE(_Right); } else { _Elem _Temp_buf[_BUF_SIZE]; _Traits::copy(_Temp_buf, _My_data._Bx._Buf, _My_data._Mysize + 1); _Traits::copy(_My_data._Bx._Buf, _Right_data._Bx._Buf, _Right_data._Mysize + 1); _Traits::copy(_Right_data._Bx._Buf, _Temp_buf, _My_data._Mysize + 1); - _ASAN_STRING_CREATE(_Right); - _ASAN_STRING_CREATE(*this); } _STD swap(_My_data._Mysize, _Right_data._Mysize); @@ -4584,7 +4479,8 @@ public: } _NODISCARD _CONSTEXPR20 size_type find_first_of( - _In_z_ const _Elem* const _Ptr, const size_type _Off = 0) const noexcept /* strengthened */ { + _In_z_ const _Elem* const _Ptr, const size_type _Off = 0) const noexcept + /* strengthened */ { // look for one of [_Ptr, ) at or after _Off return static_cast(_Traits_find_first_of<_Traits>( _Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ptr, _Traits::length(_Ptr))); @@ -4659,7 +4555,8 @@ public: } _NODISCARD _CONSTEXPR20 size_type find_first_not_of( - _In_z_ const _Elem* const _Ptr, size_type _Off = 0) const noexcept /* strengthened */ { + _In_z_ const _Elem* const _Ptr, size_type _Off = 0) const noexcept + /* strengthened */ { // look for one of [_Ptr, ) at or after _Off return static_cast(_Traits_find_first_not_of<_Traits>( _Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ptr, _Traits::length(_Ptr))); @@ -4971,15 +4868,15 @@ private: _Traits::copy(_My_data._Bx._Buf, _Unfancy(_Ptr), _My_data._Mysize + 1); _Al.deallocate(_Ptr, _My_data._Myres + 1); _My_data._Myres = _BUF_SIZE - 1; - _ASAN_STRING_CREATE(*this); } _CONSTEXPR20 void _Eos(const size_type _New_size) { // set new length and null terminator - _ASAN_STRING_MODIFY(static_cast(_New_size - _Mypair._Myval2._Mysize)); + _ASAN_STRING_MODIFY(*this, _Mypair._Myval2._Mysize, _New_size); _Traits::assign(_Mypair._Myval2._Myptr()[_Mypair._Myval2._Mysize = _New_size], _Elem()); } - _CONSTEXPR20 void _Tidy_init() noexcept { // initialize basic_string data members + _CONSTEXPR20 void _Tidy_init() noexcept { + // initialize basic_string data members auto& _My_data = _Mypair._Myval2; _My_data._Mysize = 0; _My_data._Myres = _BUF_SIZE - 1; @@ -4987,14 +4884,13 @@ private: // the _Traits::assign is last so the codegen doesn't think the char write can alias this _Traits::assign(_My_data._Bx._Buf[0], _Elem()); - _ASAN_STRING_CREATE(*this); } _CONSTEXPR20 void _Tidy_deallocate() noexcept { // initialize buffer, deallocating any storage auto& _My_data = _Mypair._Myval2; _My_data._Orphan_all(); - _ASAN_STRING_REMOVE(*this); if (_My_data._Large_string_engaged()) { + _ASAN_STRING_REMOVE(*this); const pointer _Ptr = _My_data._Bx._Ptr; auto& _Al = _Getal(); _Destroy_in_place(_My_data._Bx._Ptr); diff --git a/tests/std/tests/GH_002030_asan_annotate_string/env.lst b/tests/std/tests/GH_002030_asan_annotate_string/env.lst index a18ef67c7..6a1706876 100644 --- a/tests/std/tests/GH_002030_asan_annotate_string/env.lst +++ b/tests/std/tests/GH_002030_asan_annotate_string/env.lst @@ -2,9 +2,9 @@ # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # This test matrix is the usual test matrix, with all currently unsupported options removed, crossed with the ASan flags. -# TRANSITION, VSO-1350252 -# Due to a bug in the ASan libs using ASan with /MD or /MT requires IDL==0 and using /MDd or /MTd requires IDL==2. -# clang-cl does not currently support targeting /MDd or /MTd. + +# TRANSITION, VSO-1350252 - due to vcasan.lib including the standard library, we can't use it (pending 17.5 preview 2). +# TRANSITION, google/sanitizers#328 - clang-cl does not currently support targeting /MDd or /MTd. RUNALL_INCLUDE ..\prefix.lst RUNALL_CROSSLIST PM_CL="/Zi /wd4611 /w14640 /Zc:threadSafeInit-" PM_LINK="/debug" @@ -56,8 +56,7 @@ PM_CL="/D_ANNOTATE_STRING /EHsc /MTd /std:c++latest /permissive- /fno-sanitize-a PM_CL="/D_ANNOTATE_STRING /Za /EHsc /MD /std:c++latest /permissive- /fno-sanitize-address-vcasan-lib" PM_CL="/D_ANNOTATE_STRING /Za /EHsc /MDd /std:c++latest /permissive- /fno-sanitize-address-vcasan-lib" # TRANSITION, clang-cl does not support /alternatename so we cannot test /D_ANNOTATE_STRING without -fsanitize=address -# TRANSITION, VSO-1586016: String annotations disabled temporarily. clang-cl fails to link with empty main. -# PM_COMPILER="clang-cl" PM_CL="-fsanitize=address -fno-ms-compatibility -fno-delayed-template-parsing -Wno-unqualified-std-cast-call /EHsc /MD /std:c++14" -# PM_COMPILER="clang-cl" PM_CL="-fsanitize=address -fno-ms-compatibility -fno-delayed-template-parsing -Wno-unqualified-std-cast-call /EHsc /MD /std:c++17" -# PM_COMPILER="clang-cl" PM_CL="-fsanitize=address -fno-ms-compatibility -fno-delayed-template-parsing -Wno-unqualified-std-cast-call /EHsc /MT /std:c++latest /permissive-" -# PM_COMPILER="clang-cl" PM_CL="-fsanitize=address -fno-ms-compatibility -fno-delayed-template-parsing -Wno-unqualified-std-cast-call /EHsc /MT /std:c++latest /permissive- /D_HAS_CXX23 /fp:strict" +PM_COMPILER="clang-cl" PM_CL="-fsanitize=address -fno-ms-compatibility -fno-delayed-template-parsing -Wno-unqualified-std-cast-call /EHsc /MD /std:c++14" +PM_COMPILER="clang-cl" PM_CL="-fsanitize=address -fno-ms-compatibility -fno-delayed-template-parsing -Wno-unqualified-std-cast-call /EHsc /MD /std:c++17" +PM_COMPILER="clang-cl" PM_CL="-fsanitize=address -fno-ms-compatibility -fno-delayed-template-parsing -Wno-unqualified-std-cast-call /EHsc /MT /std:c++20 /permissive-" +PM_COMPILER="clang-cl" PM_CL="-fsanitize=address -fno-ms-compatibility -fno-delayed-template-parsing -Wno-unqualified-std-cast-call /EHsc /MT /std:c++latest /permissive- /fp:strict" diff --git a/tests/std/tests/GH_002030_asan_annotate_string/test.cpp b/tests/std/tests/GH_002030_asan_annotate_string/test.cpp index fc0be7b0c..dcafe097f 100644 --- a/tests/std/tests/GH_002030_asan_annotate_string/test.cpp +++ b/tests/std/tests/GH_002030_asan_annotate_string/test.cpp @@ -1,15 +1,13 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// REQUIRES: asan, x64 || x86 +// REQUIRES: x64 || x86 -#if 0 // TRANSITION, VSO-1586016: String annotations disabled temporarily. -#pragma warning(disable : 4389) // signed/unsigned mismatch in arithmetic #pragma warning(disable : 4984) // 'if constexpr' is a C++17 language extension -#pragma warning(disable : 6326) // Potential comparison of a constant with another constant. +#pragma warning(disable : 4324) // '%s': structure was padded due to alignment specifier +#pragma warning(disable : 4365) // '%s': conversion from '%s' to '%s', signed/unsigned mismatch #ifdef __clang__ -#pragma clang diagnostic ignored "-Wsign-compare" #pragma clang diagnostic ignored "-Wc++17-extensions" // constexpr if is a C++17 extension #endif // __clang__ @@ -30,6 +28,8 @@ using namespace std; +#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__) + #ifdef __SANITIZE_ADDRESS__ extern "C" int __sanitizer_verify_contiguous_container(const void* beg, const void* mid, const void* end) noexcept; #endif // ASan instrumentation enabled @@ -76,6 +76,9 @@ constexpr auto get_sso_input() { } } +template +constexpr size_t max_sso_size = (16 / sizeof(CharType) < 1 ? 1 : 16 / sizeof(CharType)) - 1; + #if _HAS_CXX17 template constexpr auto get_large_input_view() { @@ -108,7 +111,7 @@ template struct throw_on_conversion { throw_on_conversion() = default; throw_on_conversion(CharType) {} - operator const CharType() const { + operator CharType() const { throw 42; } }; @@ -170,29 +173,30 @@ public: }; template -bool verify_string(basic_string, Alloc>& str) { +bool verify_string(const basic_string, Alloc>& str) { #ifdef __SANITIZE_ADDRESS__ - constexpr auto proxy_size = _Size_after_ebco_v<_Container_base>; - if constexpr (proxy_size % _Asan_granularity != 0) { // If we have a misaligned SSO buffer we disable ASAN - constexpr size_t max_sso_size = (16 / sizeof(CharType) < 1 ? 1 : 16 / sizeof(CharType)) - 1; - if (str.capacity() == max_sso_size) { - return true; - } - } - - size_t buffer_size = (str.capacity() + 1) * sizeof(CharType); - void* buffer = const_cast(static_cast(str.data())); - void* aligned_start = align(8, 1, buffer, buffer_size); - - if (!aligned_start) { + if (str.capacity() == max_sso_size) { return true; } - const void* end = const_cast(static_cast(str.data() + (str.capacity() + 1))); - const void* mid = const_cast(static_cast(str.data() + str.size() + 1)); - const void* aligned_mid = mid > aligned_start ? mid : aligned_start; + const void* const buffer = str.data(); + const void* const buf_end = str.data() + str.capacity() + 1; - return __sanitizer_verify_contiguous_container(aligned_start, aligned_mid, end) != 0; + constexpr bool _Large_string_always_aligned = + (_Container_allocation_minimum_asan_alignment>) >= 8; + + _Asan_aligned_pointers aligned; + if constexpr (_Large_string_always_aligned) { + aligned = {buffer, _Get_asan_aligned_after(buf_end)}; + } else { + aligned = _Get_asan_aligned_first_end(buffer, buf_end); + } + assert(aligned._First != aligned._End); + + const void* const mid = str.data() + str.size() + 1; + const void* const fixed_mid = aligned._Clamp_to_end(mid); + + return __sanitizer_verify_contiguous_container(aligned._First, fixed_mid, aligned._End) != 0; #else // ^^^ ASan instrumentation enabled / ASan instrumentation disabled vvv (void) str; return true; @@ -221,7 +225,7 @@ constexpr bool operator!=( template struct aligned_allocator : public custom_test_allocator { - static constexpr size_t _Minimum_allocation_alignment = 8; + static constexpr size_t _Minimum_asan_allocation_alignment = 8; aligned_allocator() = default; template @@ -235,10 +239,15 @@ struct aligned_allocator : public custom_test_allocator, aligned_allocator>> == 8); +STATIC_ASSERT(_Container_allocation_minimum_asan_alignment< + basic_string, aligned_allocator>> + == 8); template struct explicit_allocator : public custom_test_allocator { - static constexpr size_t _Minimum_allocation_alignment = alignof(CharType); + static constexpr size_t _Minimum_asan_allocation_alignment = alignof(CharType); explicit_allocator() = default; template @@ -250,9 +259,14 @@ struct explicit_allocator : public custom_test_allocator, explicit_allocator>> == 1); +STATIC_ASSERT(_Container_allocation_minimum_asan_alignment< + basic_string, explicit_allocator>> + == 2); template struct implicit_allocator : public custom_test_allocator { @@ -266,9 +280,14 @@ struct implicit_allocator : public custom_test_allocator, implicit_allocator>> == 1); +STATIC_ASSERT(_Container_allocation_minimum_asan_alignment< + basic_string, implicit_allocator>> + == 2); template void test_construction() { @@ -276,12 +295,12 @@ void test_construction() { using str = basic_string, Alloc>; { // constructors // range constructors - str literal_constructed{get_large_input()}; - assert(verify_string(literal_constructed)); - str literal_constructed_sso{get_sso_input()}; assert(verify_string(literal_constructed_sso)); + str literal_constructed{get_large_input()}; + assert(verify_string(literal_constructed)); + str initializer_list_constructed({CharType{'H'}, CharType{'e'}, CharType{'l'}, CharType{'l'}, CharType{'o'}, CharType{' '}, // CharType{'f'}, CharType{'l'}, CharType{'u'}, CharType{'f'}, CharType{'f'}, CharType{'y'}, CharType{' '}, @@ -528,13 +547,12 @@ void test_append() { using CharType = typename Alloc::value_type; using str = basic_string, Alloc>; - constexpr size_t large_size = 20; - constexpr size_t sso_size = 1; - constexpr size_t max_sso_size = (16 / sizeof(CharType) < 1 ? 1 : 16 / sizeof(CharType)) - 1; + constexpr size_t large_size = 20; + constexpr size_t sso_size = 1; const str input(large_size, CharType{'b'}); const str input_sso(sso_size, CharType{'b'}); - const str input_sso_growing(max_sso_size, CharType{'b'}); + const str input_sso_growing(max_sso_size, CharType{'b'}); { // push_back str push_back{input}; @@ -851,7 +869,7 @@ void test_append() { str op_rstr_char_sso = str(sso_size, CharType{'b'}) + CharType{'!'}; assert(verify_string(op_rstr_char_sso)); - str op_rstr_char_sso_growing = str(max_sso_size, CharType{'b'}) + CharType{'!'}; + str op_rstr_char_sso_growing = str(max_sso_size, CharType{'b'}) + CharType{'!'}; assert(verify_string(op_rstr_char_sso_growing)); str op_char_rstr_large = CharType{'!'} + str(large_size, CharType{'b'}); @@ -860,7 +878,7 @@ void test_append() { str op_char_rstr_sso = CharType{'!'} + str(sso_size, CharType{'b'}); assert(verify_string(op_char_rstr_sso)); - str op_char_rstr_sso_growing = CharType{'!'} + str(max_sso_size, CharType{'b'}); + str op_char_rstr_sso_growing = CharType{'!'} + str(max_sso_size, CharType{'b'}); assert(verify_string(op_char_rstr_sso_growing)); } } @@ -1201,13 +1219,12 @@ void test_insertion() { using CharType = typename Alloc::value_type; using str = basic_string, Alloc>; - constexpr size_t large_size = 20; - constexpr size_t sso_size = 1; - constexpr size_t max_sso_size = (16 / sizeof(CharType) < 1 ? 1 : 16 / sizeof(CharType)) - 1; + constexpr size_t large_size = 20; + constexpr size_t sso_size = 1; const str input(large_size, CharType{'b'}); const str input_sso(sso_size, CharType{'b'}); - const str input_sso_growing(max_sso_size, CharType{'b'}); + const str input_sso_growing(max_sso_size, CharType{'b'}); input_iterator_tester input_iter_data_sso; @@ -1581,6 +1598,49 @@ void test_misc() { assert(verify_string(resize_char_sso_to_sso)); } + { // replace + const CharType mrow[] = {'m', 'r', 'o', 'w', '\0'}; + + str replace_front_bigger{input}; + replace_front_bigger.replace(0, 2, mrow); + assert(verify_string(replace_front_bigger)); + str replace_front_same{input}; + replace_front_same.replace(0, 4, mrow); + assert(verify_string(replace_front_same)); + str replace_front_smaller{input}; + replace_front_smaller.replace(0, 6, mrow); + assert(verify_string(replace_front_smaller)); + + str replace_mid_bigger{input}; + replace_mid_bigger.replace(2, 2, mrow); + assert(verify_string(replace_mid_bigger)); + str replace_mid_same{input}; + replace_mid_same.replace(2, 4, mrow); + assert(verify_string(replace_mid_same)); + str replace_mid_smaller{input}; + replace_mid_smaller.replace(2, 6, mrow); + assert(verify_string(replace_mid_smaller)); + + str replace_back_bigger{input}; + replace_back_bigger.replace(replace_back_bigger.size() - 2, 2, mrow); + assert(verify_string(replace_back_bigger)); + str replace_back_same{input}; + replace_back_same.replace(replace_back_same.size() - 4, 4, mrow); + assert(verify_string(replace_back_same)); + str replace_back_smaller{input}; + replace_back_smaller.replace(replace_back_smaller.size() - 6, 6, mrow); + assert(verify_string(replace_back_smaller)); + + const CharType hi[] = {'h', 'i', '\0'}; + str replace_large_to_sso{input}; + replace_large_to_sso.replace(0, replace_large_to_sso.size() - 1, hi); + assert(verify_string(replace_large_to_sso)); + + str replace_sso_to_large{input_sso}; + replace_sso_to_large.replace(0, 1, input); + assert(verify_string(replace_sso_to_large)); + } + if constexpr (allocator_traits::propagate_on_container_swap::value) { // swap str first_large{input}; str second_large = input + str{CharType{'c'}, CharType{'a'}, CharType{'t'}}; @@ -1845,6 +1905,14 @@ void test_DevCom_10116361() { s1.~string(); } +void test_DevCom_10109507() { + // replace failed to correctly munge asan annotations while working + string s("abcd"); + s.replace(0, 1, "ef", 2); + s.replace(0, 0, "xy", 2); + assert(s == "xyefbcd"); +} + int main() { run_allocator_matrix(); #ifdef __cpp_char8_t @@ -1855,7 +1923,5 @@ int main() { run_allocator_matrix(); test_DevCom_10116361(); + test_DevCom_10109507(); } -#endif // TRANSITION, VSO-1586016 - -int main() {} diff --git a/tests/std/tests/GH_002030_asan_annotate_vector/env.lst b/tests/std/tests/GH_002030_asan_annotate_vector/env.lst index b6dd36fe0..ce99dfafa 100644 --- a/tests/std/tests/GH_002030_asan_annotate_vector/env.lst +++ b/tests/std/tests/GH_002030_asan_annotate_vector/env.lst @@ -2,9 +2,9 @@ # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # This test matrix is the usual test matrix, with all currently unsupported options removed, crossed with the ASan flags. -# TRANSITION, VSO-1350252 -# Due to a bug in the ASan libs using ASan with /MD or /MT requires IDL==0 and using /MDd or /MTd requires IDL==2. -# clang-cl does not currently support targeting /MDd or /MTd. + +# TRANSITION, VSO-1350252 - due to vcasan.lib including the standard library, we can't use it (pending 17.5 preview 2). +# TRANSITION, google/sanitizers#328 - clang-cl does not currently support targeting /MDd or /MTd. RUNALL_INCLUDE ..\prefix.lst RUNALL_CROSSLIST PM_CL="/Zi /wd4611 /w14640 /Zc:threadSafeInit-" PM_LINK="/debug" diff --git a/tests/std/tests/GH_002030_asan_annotate_vector/test.cpp b/tests/std/tests/GH_002030_asan_annotate_vector/test.cpp index b4a28f138..d2a5f1e76 100644 --- a/tests/std/tests/GH_002030_asan_annotate_vector/test.cpp +++ b/tests/std/tests/GH_002030_asan_annotate_vector/test.cpp @@ -12,8 +12,16 @@ #include #include +#pragma warning(disable : 4984) // 'if constexpr' is a C++17 language extension + +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wc++17-extensions" // constexpr if is a C++17 extension +#endif // __clang__ + using namespace std; +#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__) + #ifndef __SANITIZE_ADDRESS__ #if defined(__clang__) && defined(__has_feature) #if __has_feature(address_sanitizer) @@ -148,19 +156,23 @@ public: template bool verify_vector(vector& vec) { #ifdef __SANITIZE_ADDRESS__ - size_t buffer_bytes = vec.capacity() * sizeof(T); - void* buffer = vec.data(); - void* aligned_start = align(8, 1, buffer, buffer_bytes); + const void* buffer = vec.data(); + const void* buf_end = vec.data() + vec.capacity(); + _Asan_aligned_pointers aligned; - if (!aligned_start) { - return true; + if constexpr ((_Container_allocation_minimum_asan_alignment>) >= 8) { + aligned = {buffer, buf_end}; + } else { + aligned = _Get_asan_aligned_first_end(buffer, buf_end); + if (aligned._First == aligned._End) { + return true; + } } - void* mid = vec.data() + vec.size(); - mid = mid > aligned_start ? mid : aligned_start; + const void* const mid = vec.data() + vec.size(); + const void* const fixed_mid = aligned._Clamp_to_end(mid); - void* bad_address = - __sanitizer_contiguous_container_find_bad_address(aligned_start, mid, vec.data() + vec.capacity()); + void* bad_address = __sanitizer_contiguous_container_find_bad_address(aligned._First, fixed_mid, aligned._End); if (bad_address == nullptr) { return true; } @@ -172,9 +184,11 @@ bool verify_vector(vector& vec) { } cout << "Vector State:" << endl; cout << " begin: " << buffer << endl; - cout << " aligned begin: " << aligned_start << endl; - cout << " last: " << reinterpret_cast(vec.data() + vec.size()) << endl; - cout << " end: " << reinterpret_cast(vec.data() + vec.capacity()) << endl; + cout << " aligned begin: " << aligned._First << endl; + cout << " last: " << mid << endl; + cout << " aligned_last: " << fixed_mid << endl; + cout << " end: " << buf_end << endl; + cout << " aligned_end: " << aligned._End << endl; __asan_describe_address(bad_address); return false; @@ -206,7 +220,7 @@ constexpr bool operator!=( template struct aligned_allocator : custom_test_allocator { - static constexpr size_t _Minimum_allocation_alignment = 8; + static constexpr size_t _Minimum_asan_allocation_alignment = 8; aligned_allocator() = default; template @@ -220,10 +234,11 @@ struct aligned_allocator : custom_test_allocator { delete[] p; } }; +STATIC_ASSERT(_Container_allocation_minimum_asan_alignment>> == 8); template struct explicit_allocator : custom_test_allocator { - static constexpr size_t _Minimum_allocation_alignment = alignof(T); + static constexpr size_t _Minimum_asan_allocation_alignment = alignof(T); explicit_allocator() = default; template @@ -238,6 +253,8 @@ struct explicit_allocator : custom_test_allocator { delete[] (p - 1); } }; +STATIC_ASSERT(_Container_allocation_minimum_asan_alignment>> == 1); +STATIC_ASSERT(_Container_allocation_minimum_asan_alignment>> == 2); template struct implicit_allocator : custom_test_allocator { @@ -254,6 +271,8 @@ struct implicit_allocator : custom_test_allocator { delete[] (p - 1); } }; +STATIC_ASSERT(_Container_allocation_minimum_asan_alignment>> == 1); +STATIC_ASSERT(_Container_allocation_minimum_asan_alignment>> == 2); template void test_push_pop() {