Re-enable ASAN string annotations (#3164)

Co-authored-by: Nicole Mazzuca <mazzucan@outlook.com>
This commit is contained in:
nicole mazzuca 2022-12-15 13:41:42 -08:00 коммит произвёл GitHub
Родитель 67d89ce691
Коммит faaf094ee1
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
10 изменённых файлов: 531 добавлений и 365 удалений

Просмотреть файл

@ -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

Просмотреть файл

@ -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 <yvals_core.h>
#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

Просмотреть файл

@ -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",

Просмотреть файл

@ -14,6 +14,8 @@
#include <xpolymorphic_allocator.h>
#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 <class _Vec, class = void>
_INLINE_VAR constexpr bool _Has_minimum_allocation_alignment = alignof(typename _Vec::value_type) >= _Asan_granularity;
template <class _Vec>
_INLINE_VAR constexpr bool
_Has_minimum_allocation_alignment<_Vec, void_t<decltype(_Vec::allocator_type::_Minimum_allocation_alignment)>> =
_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 _Ty, class _Alloc = allocator<_Ty>>
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<const char*>(_First);
const auto _CEnd = reinterpret_cast<const char*>(_End);
const size_t _Capacity = static_cast<size_t>(_CEnd - _CFirst);
if (_Capacity >= _Asan_granularity) {
// We are guaranteed to have sufficient space to find an aligned address.
return reinterpret_cast<const void*>(
(reinterpret_cast<uintptr_t>(_CFirst) + (_Asan_granularity - 1)) & ~(_Asan_granularity - 1));
}
uintptr_t _Alignment_offset = reinterpret_cast<uintptr_t>(_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<const char*>(_Unfancy(_First_));
const auto _End = reinterpret_cast<const char*>(_Unfancy(_End_));
const auto _Old_last = reinterpret_cast<const char*>(_Unfancy(_Old_last_));
const auto _New_last = reinterpret_cast<const char*>(_Unfancy(_New_last_));
if constexpr (_Has_minimum_allocation_alignment<vector>) {
__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<vector>) >= _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);
}
}

Просмотреть файл

@ -774,7 +774,127 @@ _NODISCARD constexpr allocation_result<typename allocator_traits<_Alloc>::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<const void*>(reinterpret_cast<uintptr_t>(_First) & ~_Asan_granularity_mask),
reinterpret_cast<const void*>(reinterpret_cast<uintptr_t>(_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<const void*>(
(reinterpret_cast<uintptr_t>(_End) + _Asan_granularity_mask) & ~_Asan_granularity_mask);
}
template <class _Container, class = void>
_INLINE_VAR constexpr size_t _Container_allocation_minimum_asan_alignment = alignof(typename _Container::value_type);
template <class _Container>
_INLINE_VAR constexpr size_t _Container_allocation_minimum_asan_alignment<_Container,
void_t<decltype(_Container::allocator_type::_Minimum_asan_allocation_alignment)>> =
(_STD max)(
alignof(typename _Container::value_type), _Container::allocator_type::_Minimum_asan_allocation_alignment);
_EXPORT_STD template <class _Ty>
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

Просмотреть файл

@ -17,6 +17,8 @@
#include <xpolymorphic_allocator.h>
#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 <class _String, class = void>
_INLINE_VAR constexpr bool _Has_minimum_allocation_alignment_string =
alignof(typename _String::value_type) >= _Asan_granularity;
template <class _String>
_INLINE_VAR constexpr bool _Has_minimum_allocation_alignment_string<_String,
void_t<decltype(_String::allocator_type::_Minimum_allocation_alignment)>> =
_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 <class _Rng, class _Ty>
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<size_type>(_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<const char*>(_First);
if (_Capacity >= _Asan_granularity) { // We are guaranteed to have sufficient space to find an aligned address
return reinterpret_cast<const void*>(
(reinterpret_cast<uintptr_t>(_CFirst) + (_Asan_granularity - 1)) & ~(_Asan_granularity - 1));
}
uintptr_t _Alignment_offset = reinterpret_cast<uintptr_t>(_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<uintptr_t>(_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<const char*>(_Ptr);
const char* _End = reinterpret_cast<const char*>(_Ptr + _Capacity + 1);
const char* _Old_last = reinterpret_cast<const char*>(_Ptr + _Old_size + 1);
const char* _New_last = reinterpret_cast<const char*>(_Ptr + _New_size + 1);
if constexpr (_Has_minimum_allocation_alignment_string<basic_string>) {
__sanitizer_annotate_contiguous_container(_First, _End, _Old_last, _New_last);
constexpr bool _Large_string_always_asan_aligned =
(_Container_allocation_minimum_asan_alignment<basic_string>) >= _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<difference_type>(_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<difference_type>(_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<difference_type>(_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<difference_type>(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<difference_type>(_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<difference_type>(_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<difference_type>(_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<difference_type>(_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<difference_type>(_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<difference_type>(_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<difference_type>(_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, <null>) at or after _Off
return static_cast<size_type>(_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, <null>) at or after _Off
return static_cast<size_type>(_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<difference_type>(_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);

Просмотреть файл

@ -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"

Просмотреть файл

@ -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 <class CharType>
constexpr size_t max_sso_size = (16 / sizeof(CharType) < 1 ? 1 : 16 / sizeof(CharType)) - 1;
#if _HAS_CXX17
template <class CharType>
constexpr auto get_large_input_view() {
@ -108,7 +111,7 @@ template <class CharType>
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 <class CharType, class Alloc>
bool verify_string(basic_string<CharType, char_traits<CharType>, Alloc>& str) {
bool verify_string(const basic_string<CharType, char_traits<CharType>, 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<void*>(static_cast<const void*>(str.data()));
void* aligned_start = align(8, 1, buffer, buffer_size);
if (!aligned_start) {
if (str.capacity() == max_sso_size<CharType>) {
return true;
}
const void* end = const_cast<void*>(static_cast<const void*>(str.data() + (str.capacity() + 1)));
const void* mid = const_cast<void*>(static_cast<const void*>(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<decay_t<decltype(str)>>) >= 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 <class CharType, class Pocma = true_type, class Stateless = true_type>
struct aligned_allocator : public custom_test_allocator<CharType, Pocma, Stateless> {
static constexpr size_t _Minimum_allocation_alignment = 8;
static constexpr size_t _Minimum_asan_allocation_alignment = 8;
aligned_allocator() = default;
template <class U>
@ -235,10 +239,15 @@ struct aligned_allocator : public custom_test_allocator<CharType, Pocma, Statele
delete[] p;
}
};
STATIC_ASSERT(
_Container_allocation_minimum_asan_alignment<basic_string<char, char_traits<char>, aligned_allocator<char>>> == 8);
STATIC_ASSERT(_Container_allocation_minimum_asan_alignment<
basic_string<wchar_t, char_traits<wchar_t>, aligned_allocator<wchar_t>>>
== 8);
template <class CharType, class Pocma = true_type, class Stateless = true_type>
struct explicit_allocator : public custom_test_allocator<CharType, Pocma, Stateless> {
static constexpr size_t _Minimum_allocation_alignment = alignof(CharType);
static constexpr size_t _Minimum_asan_allocation_alignment = alignof(CharType);
explicit_allocator() = default;
template <class U>
@ -250,9 +259,14 @@ struct explicit_allocator : public custom_test_allocator<CharType, Pocma, Statel
}
void deallocate(CharType* p, size_t) noexcept {
delete[](p - 1);
delete[] (p - 1);
}
};
STATIC_ASSERT(
_Container_allocation_minimum_asan_alignment<basic_string<char, char_traits<char>, explicit_allocator<char>>> == 1);
STATIC_ASSERT(_Container_allocation_minimum_asan_alignment<
basic_string<wchar_t, char_traits<wchar_t>, explicit_allocator<wchar_t>>>
== 2);
template <class CharType, class Pocma = true_type, class Stateless = true_type>
struct implicit_allocator : public custom_test_allocator<CharType, Pocma, Stateless> {
@ -266,9 +280,14 @@ struct implicit_allocator : public custom_test_allocator<CharType, Pocma, Statel
}
void deallocate(CharType* p, size_t) noexcept {
delete[](p - 1);
delete[] (p - 1);
}
};
STATIC_ASSERT(
_Container_allocation_minimum_asan_alignment<basic_string<char, char_traits<char>, implicit_allocator<char>>> == 1);
STATIC_ASSERT(_Container_allocation_minimum_asan_alignment<
basic_string<wchar_t, char_traits<wchar_t>, implicit_allocator<wchar_t>>>
== 2);
template <class Alloc>
void test_construction() {
@ -276,12 +295,12 @@ void test_construction() {
using str = basic_string<CharType, char_traits<CharType>, Alloc>;
{ // constructors
// range constructors
str literal_constructed{get_large_input<CharType>()};
assert(verify_string(literal_constructed));
str literal_constructed_sso{get_sso_input<CharType>()};
assert(verify_string(literal_constructed_sso));
str literal_constructed{get_large_input<CharType>()};
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<CharType, char_traits<CharType>, 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>, 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>, 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>, 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<CharType, char_traits<CharType>, 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>, CharType{'b'});
input_iterator_tester<CharType, 3> 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<Alloc>::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<char>();
#ifdef __cpp_char8_t
@ -1855,7 +1923,5 @@ int main() {
run_allocator_matrix<wchar_t>();
test_DevCom_10116361();
test_DevCom_10109507();
}
#endif // TRANSITION, VSO-1586016
int main() {}

Просмотреть файл

@ -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"

Просмотреть файл

@ -12,8 +12,16 @@
#include <utility>
#include <vector>
#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 <class T, class Alloc>
bool verify_vector(vector<T, Alloc>& 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<vector<T, Alloc>>) >= 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<T, Alloc>& vec) {
}
cout << "Vector State:" << endl;
cout << " begin: " << buffer << endl;
cout << " aligned begin: " << aligned_start << endl;
cout << " last: " << reinterpret_cast<void*>(vec.data() + vec.size()) << endl;
cout << " end: " << reinterpret_cast<void*>(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 <class T, class Pocma = true_type, class Stateless = true_type>
struct aligned_allocator : custom_test_allocator<T, Pocma, Stateless> {
static constexpr size_t _Minimum_allocation_alignment = 8;
static constexpr size_t _Minimum_asan_allocation_alignment = 8;
aligned_allocator() = default;
template <class U>
@ -220,10 +234,11 @@ struct aligned_allocator : custom_test_allocator<T, Pocma, Stateless> {
delete[] p;
}
};
STATIC_ASSERT(_Container_allocation_minimum_asan_alignment<vector<char, aligned_allocator<char>>> == 8);
template <class T, class Pocma = true_type, class Stateless = true_type>
struct explicit_allocator : custom_test_allocator<T, Pocma, Stateless> {
static constexpr size_t _Minimum_allocation_alignment = alignof(T);
static constexpr size_t _Minimum_asan_allocation_alignment = alignof(T);
explicit_allocator() = default;
template <class U>
@ -238,6 +253,8 @@ struct explicit_allocator : custom_test_allocator<T, Pocma, Stateless> {
delete[] (p - 1);
}
};
STATIC_ASSERT(_Container_allocation_minimum_asan_alignment<vector<char, explicit_allocator<char>>> == 1);
STATIC_ASSERT(_Container_allocation_minimum_asan_alignment<vector<wchar_t, explicit_allocator<wchar_t>>> == 2);
template <class T, class Pocma = true_type, class Stateless = true_type>
struct implicit_allocator : custom_test_allocator<T, Pocma, Stateless> {
@ -254,6 +271,8 @@ struct implicit_allocator : custom_test_allocator<T, Pocma, Stateless> {
delete[] (p - 1);
}
};
STATIC_ASSERT(_Container_allocation_minimum_asan_alignment<vector<char, implicit_allocator<char>>> == 1);
STATIC_ASSERT(_Container_allocation_minimum_asan_alignment<vector<wchar_t, implicit_allocator<wchar_t>>> == 2);
template <class Alloc>
void test_push_pop() {