diff --git a/stl/CMakeLists.txt b/stl/CMakeLists.txt index d74d7a11e6a..0fcbc1a7ea9 100644 --- a/stl/CMakeLists.txt +++ b/stl/CMakeLists.txt @@ -250,6 +250,7 @@ endforeach() # Objs that exist in both libcpmt[d][01].lib and msvcprt[d].lib. set(IMPLIB_SOURCES + ${CMAKE_CURRENT_LIST_DIR}/src/asan_noop.cpp ${CMAKE_CURRENT_LIST_DIR}/src/filesystem.cpp ${CMAKE_CURRENT_LIST_DIR}/src/format.cpp ${CMAKE_CURRENT_LIST_DIR}/src/locale0_implib.cpp @@ -392,6 +393,10 @@ set(SOURCES ${CMAKE_CURRENT_LIST_DIR}/src/xwstoxfl.cpp ) +set(ASAN_SOURCES + ${CMAKE_CURRENT_LIST_DIR}/src/asan.cpp +) + set(EHA_SOURCES ${CMAKE_CURRENT_LIST_DIR}/src/excptptr.cpp ) @@ -543,11 +548,11 @@ add_stl_dlls("d" "_DEBUG" "${VCLIBS_DEBUG_OPTIONS}" "" "/opt:ref,noicf") function(add_stl_statics FLAVOR_SUFFIX THIS_CONFIG_DEFINITIONS THIS_CONFIG_COMPILE_OPTIONS) add_library(libcpmt${FLAVOR_SUFFIX}_eha OBJECT ${EHA_SOURCES}) - target_compile_definitions(libcpmt${FLAVOR_SUFFIX}_eha PRIVATE "${THIS_CONFIG_DEFINITIONS}") + target_compile_definitions(libcpmt${FLAVOR_SUFFIX}_eha PRIVATE "${THIS_CONFIG_DEFINITIONS};_ANNOTATE_VECTOR") target_compile_options(libcpmt${FLAVOR_SUFFIX}_eha PRIVATE "${THIS_CONFIG_COMPILE_OPTIONS};/EHa") add_library(libcpmt${FLAVOR_SUFFIX} STATIC ${HEADERS} ${IMPLIB_SOURCES} ${SOURCES} ${INITIALIZER_SOURCES} ${STATIC_SOURCES}) - target_compile_definitions(libcpmt${FLAVOR_SUFFIX} PRIVATE "${THIS_CONFIG_DEFINITIONS}") + target_compile_definitions(libcpmt${FLAVOR_SUFFIX} PRIVATE "${THIS_CONFIG_DEFINITIONS};_ANNOTATE_VECTOR") target_compile_options(libcpmt${FLAVOR_SUFFIX} PRIVATE "${THIS_CONFIG_COMPILE_OPTIONS};/EHsc") target_link_libraries(libcpmt${FLAVOR_SUFFIX} PRIVATE Boost::math libcpmt${FLAVOR_SUFFIX}_eha std_init_once_begin_initialize std_init_once_complete) endfunction() @@ -557,3 +562,5 @@ add_stl_statics("1" "_ITERATOR_DEBUG_LEVEL=1" "${VCLIBS_RELEASE_OPTIONS}") add_stl_statics("d" "_DEBUG;_ITERATOR_DEBUG_LEVEL=2" "${VCLIBS_DEBUG_OPTIONS}") add_stl_statics("d1" "_DEBUG;_ITERATOR_DEBUG_LEVEL=1" "${VCLIBS_DEBUG_OPTIONS}") add_stl_statics("d0" "_DEBUG;_ITERATOR_DEBUG_LEVEL=0" "${VCLIBS_DEBUG_OPTIONS}") + +add_library(stl_asan STATIC ${ASAN_SOURCES}) diff --git a/stl/inc/vector b/stl/inc/vector index 130a9c9544e..4f7ef44b271 100644 --- a/stl/inc/vector +++ b/stl/inc/vector @@ -428,6 +428,54 @@ 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__ ^^^ // vvv __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__ ^^^ // vvv _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_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") +#endif + +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 ^^^ // vvv !_INSERT_VECTOR_ANNOTATION vvv +#pragma detect_mismatch("annotate_vector", "0") +#endif // !_INSERT_VECTOR_ANNOTATION + template > class vector { // varying size array of values private: @@ -452,6 +500,142 @@ public: using difference_type = typename _Alty_traits::difference_type; private: +#ifdef _INSERT_VECTOR_ANNOTATION + _CONSTEXPR20 void _Create_annotation() const noexcept { + // Annotates the shadow memory of the valid range + auto& _My_data = _Mypair._Myval2; + _Apply_annotation(_My_data._Myfirst, _My_data._Myend, _My_data._Myend, _My_data._Mylast); + } + + _CONSTEXPR20 void _Remove_annotation() const noexcept { + // Removes the shadow memory annotation of the range + auto& _My_data = _Mypair._Myval2; + _Apply_annotation(_My_data._Myfirst, _My_data._Myend, _My_data._Mylast, _My_data._Myend); + } + + _CONSTEXPR20 void _Modify_annotation(const difference_type _Count) const noexcept { + // Extends/shrinks the annotated range by _Count + auto& _My_data = _Mypair._Myval2; + _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 { +#if _HAS_CXX20 + if (_STD is_constant_evaluated()) { + return; + } +#endif // _HAS_CXX20 + + if (!_Asan_vector_should_annotate) { + return; + } + + const auto _First = reinterpret_cast(_Unfancy_maybe_null(_First_)); + const auto _End = reinterpret_cast(_Unfancy_maybe_null(_End_)); + const auto _Old_last = reinterpret_cast(_Unfancy_maybe_null(_Old_last_)); + const auto _New_last = reinterpret_cast(_Unfancy_maybe_null(_New_last_)); + if constexpr (_Has_minimum_allocation_alignment) { + __sanitizer_annotate_contiguous_container(_First, _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. + 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); + } + } + + class _NODISCARD _Asan_extend_guard { + public: + _Asan_extend_guard(const _Asan_extend_guard&) = delete; + _Asan_extend_guard& operator=(const _Asan_extend_guard&) = delete; + + constexpr explicit _Asan_extend_guard(vector* _Myvec_, size_type _Target_size_) noexcept + : _Myvec(_Myvec_), _Target_size(_Target_size_) { + _STL_INTERNAL_CHECK(_Myvec != nullptr); + auto& _My_data = _Myvec->_Mypair._Myval2; + _Apply_annotation(_My_data._Myfirst, _My_data._Myend, _My_data._Mylast, _My_data._Myfirst + _Target_size); + } + + _CONSTEXPR20 ~_Asan_extend_guard() { + if (!_Myvec) { // Operation succeeded, no modification to the shadow memory required. + return; + } + + // Shrinks the shadow memory to the current size of the vector. + auto& _My_data = _Myvec->_Mypair._Myval2; + _Apply_annotation(_My_data._Myfirst, _My_data._Myend, _My_data._Myfirst + _Target_size, _My_data._Mylast); + } + + _CONSTEXPR20 void _Release() noexcept { + _Myvec = nullptr; + } + + private: + vector* _Myvec; + size_type _Target_size; + }; + + class _NODISCARD _Asan_create_guard { + public: + _Asan_create_guard(const _Asan_create_guard&) = delete; + _Asan_create_guard& operator=(const _Asan_create_guard&) = delete; + + constexpr explicit _Asan_create_guard(const vector& _Myvec_) noexcept : _Myvec(_Myvec_) {} + + _CONSTEXPR20 ~_Asan_create_guard() { + _Myvec._Create_annotation(); + } + + private: + const vector& _Myvec; + }; + +#define _ASAN_VECTOR_MODIFY(n) _Modify_annotation((n)) +#define _ASAN_VECTOR_REMOVE _Remove_annotation() +#define _ASAN_VECTOR_CREATE _Create_annotation() +#define _ASAN_VECTOR_CREATE_GUARD _Asan_create_guard _Annotator(*this) +#define _ASAN_VECTOR_EXTEND_GUARD(n) _Asan_extend_guard _Annotator(this, (n)) +#define _ASAN_VECTOR_RELEASE_GUARD _Annotator._Release() +#else // ^^^ _INSERT_VECTOR_ANNOTATION ^^^ // vvv !_INSERT_VECTOR_ANNOTATION vvv +#define _ASAN_VECTOR_MODIFY(n) +#define _ASAN_VECTOR_REMOVE +#define _ASAN_VECTOR_CREATE +#define _ASAN_VECTOR_CREATE_GUARD +#define _ASAN_VECTOR_EXTEND_GUARD(n) +#define _ASAN_VECTOR_RELEASE_GUARD +#endif // !_INSERT_VECTOR_ANNOTATION + using _Scary_val = _Vector_val, _Simple_types<_Ty>, _Vec_iter_types<_Ty, size_type, difference_type, pointer, const_pointer, _Ty&, const _Ty&>>>; @@ -494,7 +678,8 @@ public: _Tidy_guard _Guard{this}; for (; _UFirst != _ULast; ++_UFirst) { - emplace_back(*_UFirst); // performance note: emplace_back()'s strong guarantee is unnecessary here + // performance note: _Emplace_one_at_back's strong guarantee is unnecessary here. + _Emplace_one_at_back(*_UFirst); } _Guard._Target = nullptr; @@ -547,6 +732,8 @@ public: _Tidy_guard _Guard{this}; _My_data._Mylast = _Uninitialized_move(_Right_data._Myfirst, _Right_data._Mylast, _My_data._Myfirst, _Al); + + _ASAN_VECTOR_CREATE; _Guard._Target = nullptr; } _Proxy._Release(); @@ -599,47 +786,39 @@ public: private: template - _CONSTEXPR20 decltype(auto) _Emplace_back_with_unused_capacity(_Valty&&... _Val) { + _CONSTEXPR20 _Ty& _Emplace_one_at_back(_Valty&&... _Val) { // insert by perfectly forwarding into element at end, provide strong guarantee auto& _My_data = _Mypair._Myval2; pointer& _Mylast = _My_data._Mylast; - _STL_INTERNAL_CHECK(_Mylast != _My_data._Myend); // check that we have unused capacity - _Alty_traits::construct(_Getal(), _Unfancy(_Mylast), _STD forward<_Valty>(_Val)...); - _Orphan_range(_Mylast, _Mylast); - _Ty& _Result = *_Mylast; - ++_Mylast; -#if _HAS_CXX17 - return _Result; -#else // ^^^ _HAS_CXX17 ^^^ // vvv !_HAS_CXX17 vvv - (void) _Result; -#endif // _HAS_CXX17 + + if (_Mylast != _My_data._Myend) { + return _Emplace_back_with_unused_capacity(_STD forward<_Valty>(_Val)...); + } + + return *_Emplace_reallocate(_Mylast, _STD forward<_Valty>(_Val)...); } -public: template - _CONSTEXPR20 decltype(auto) emplace_back(_Valty&&... _Val) { + _CONSTEXPR20 _Ty& _Emplace_back_with_unused_capacity(_Valty&&... _Val) { // insert by perfectly forwarding into element at end, provide strong guarantee auto& _My_data = _Mypair._Myval2; pointer& _Mylast = _My_data._Mylast; - if (_Mylast != _My_data._Myend) { - return _Emplace_back_with_unused_capacity(_STD forward<_Valty>(_Val)...); + _STL_INTERNAL_CHECK(_Mylast != _My_data._Myend); // check that we have unused capacity + if constexpr (conjunction_v, + _Uses_default_construct<_Alloc, _Ty*, _Valty...>>) { + _ASAN_VECTOR_MODIFY(1); + _Construct_in_place(*_Mylast, _STD forward<_Valty>(_Val)...); + } else { + _ASAN_VECTOR_EXTEND_GUARD(static_cast(_Mylast - _My_data._Myfirst) + 1); + _Alty_traits::construct(_Getal(), _Unfancy(_Mylast), _STD forward<_Valty>(_Val)...); + _ASAN_VECTOR_RELEASE_GUARD; } - _Ty& _Result = *_Emplace_reallocate(_Mylast, _STD forward<_Valty>(_Val)...); -#if _HAS_CXX17 - return _Result; -#else // ^^^ _HAS_CXX17 ^^^ // vvv !_HAS_CXX17 vvv - (void) _Result; -#endif // _HAS_CXX17 - } - - _CONSTEXPR20 void push_back(const _Ty& _Val) { // insert element at end, provide strong guarantee - emplace_back(_Val); - } + _Orphan_range(_Mylast, _Mylast); + _Ty& _Result = *_Mylast; + ++_Mylast; - _CONSTEXPR20 void push_back(_Ty&& _Val) { - // insert by moving into element at end, provide strong guarantee - emplace_back(_STD move(_Val)); + return _Result; } template @@ -691,6 +870,27 @@ public: return _Newvec + _Whereoff; } +public: + template + _CONSTEXPR20 decltype(auto) emplace_back(_Valty&&... _Val) { + // insert by perfectly forwarding into element at end, provide strong guarantee + _Ty& _Result = _Emplace_one_at_back(_STD forward<_Valty>(_Val)...); +#if _HAS_CXX17 + return _Result; +#else // ^^^ _HAS_CXX17 ^^^ // vvv !_HAS_CXX17 vvv + (void) _Result; +#endif // _HAS_CXX17 + } + + _CONSTEXPR20 void push_back(const _Ty& _Val) { // insert element at end, provide strong guarantee + emplace_back(_Val); + } + + _CONSTEXPR20 void push_back(_Ty&& _Val) { + // insert by moving into element at end, provide strong guarantee + emplace_back(_STD move(_Val)); + } + template _CONSTEXPR20 iterator emplace(const_iterator _Where, _Valty&&... _Val) { // insert by perfectly forwarding _Val at _Where @@ -711,7 +911,9 @@ public: _Alloc_temporary2<_Alty> _Obj(_Al, _STD forward<_Valty>(_Val)...); // handle aliasing // after constructing _Obj, provide basic guarantee _Orphan_range(_Whereptr, _Oldlast); + _ASAN_VECTOR_EXTEND_GUARD(static_cast(_Oldlast - _My_data._Myfirst) + 1); _Alty_traits::construct(_Al, _Unfancy(_Oldlast), _STD move(_Oldlast[-1])); + _ASAN_VECTOR_RELEASE_GUARD; ++_My_data._Mylast; _Move_backward_unchecked(_Whereptr, _Oldlast - 1, _Oldlast); *_Whereptr = _STD move(_Obj._Get_value()); @@ -794,6 +996,7 @@ public: const auto _Affected_elements = static_cast(_Oldlast - _Whereptr); _Orphan_range(_Whereptr, _Oldlast); + _ASAN_VECTOR_EXTEND_GUARD(static_cast(_Oldlast - _My_data._Myfirst) + _Count); if (_Count > _Affected_elements) { // new stuff spills off end _Mylast = _Uninitialized_fill_n(_Oldlast, _Count - _Affected_elements, _Tmp, _Al); _Mylast = _Uninitialized_move(_Whereptr, _Oldlast, _Mylast, _Al); @@ -803,6 +1006,7 @@ public: _Move_backward_unchecked(_Whereptr, _Oldlast - _Count, _Oldlast); _STD fill(_Whereptr, _Whereptr + _Count, _Tmp); } + _ASAN_VECTOR_RELEASE_GUARD; } return _Make_iterator_offset(_Whereoff); @@ -824,10 +1028,10 @@ private: // For one-at-back, provide strong guarantee. // Otherwise, provide basic guarantee (despite N4659 26.3.11.5 [vector.modifiers]/1). - // Performance note: except for one-at-back, emplace_back()'s strong guarantee is unnecessary here. + // Performance note: except for one-at-back, _Emplace_one_at_back()'s strong guarantee is unnecessary here. for (; _First != _Last; ++_First) { - emplace_back(*_First); + _Emplace_one_at_back(*_First); } _Orphan_range(_Myfirst + _Whereoff, _Myfirst + _Oldsize); @@ -893,6 +1097,7 @@ private: const auto _Affected_elements = static_cast(_Oldlast - _Whereptr); + _ASAN_VECTOR_EXTEND_GUARD(static_cast(_Oldlast - _Oldfirst) + _Count); if (_Count < _Affected_elements) { // some affected elements must be assigned _Mylast = _Uninitialized_move(_Oldlast - _Count, _Oldlast, _Oldlast, _Al); _Move_backward_unchecked(_Whereptr, _Oldlast - _Count, _Oldlast); @@ -945,6 +1150,7 @@ private: } _Orphan_range(_Whereptr, _Oldlast); + _ASAN_VECTOR_RELEASE_GUARD; } } @@ -977,22 +1183,40 @@ public: pointer& _Myfirst = _My_data._Myfirst; pointer& _Mylast = _My_data._Mylast; + constexpr bool _Nothrow_construct = + conjunction_v, _Uses_default_construct<_Alloc, _Ty*, const _Ty&>>; + _My_data._Orphan_all(); const auto _Oldcapacity = static_cast(_My_data._Myend - _Myfirst); if (_Newsize > _Oldcapacity) { // reallocate _Clear_and_reserve_geometric(_Newsize); - _Mylast = _Uninitialized_fill_n(_Myfirst, _Newsize, _Val, _Al); + if constexpr (_Nothrow_construct) { + _Mylast = _Uninitialized_fill_n(_Myfirst, _Newsize, _Val, _Al); + _ASAN_VECTOR_CREATE; + } else { + _ASAN_VECTOR_CREATE_GUARD; + _Mylast = _Uninitialized_fill_n(_Myfirst, _Newsize, _Val, _Al); + } + return; } const auto _Oldsize = static_cast(_Mylast - _Myfirst); if (_Newsize > _Oldsize) { _STD fill(_Myfirst, _Mylast, _Val); - _Mylast = _Uninitialized_fill_n(_Mylast, _Newsize - _Oldsize, _Val, _Al); + if constexpr (_Nothrow_construct) { + _ASAN_VECTOR_MODIFY(static_cast(_Newsize - _Oldsize)); + _Mylast = _Uninitialized_fill_n(_Mylast, _Newsize - _Oldsize, _Val, _Al); + } else { + _ASAN_VECTOR_EXTEND_GUARD(static_cast(_Newsize - _Oldsize)); + _Mylast = _Uninitialized_fill_n(_Mylast, _Newsize - _Oldsize, _Val, _Al); + _ASAN_VECTOR_RELEASE_GUARD; + } } else { const pointer _Newlast = _Myfirst + _Newsize; _STD fill(_Myfirst, _Newlast, _Val); _Destroy_range(_Newlast, _Mylast, _Al); + _ASAN_VECTOR_MODIFY(static_cast(_Newsize - _Oldsize)); _Mylast = _Newlast; } } @@ -1008,7 +1232,6 @@ private: _My_data._Orphan_all(); pointer _Next = _Myfirst; - for (; _First != _Last && _Next != _Mylast; ++_First, (void) ++_Next) { *_Next = *_First; } @@ -1020,11 +1243,13 @@ private: // Trim. _Destroy_range(_Next, _Mylast, _Getal()); + _ASAN_VECTOR_MODIFY(static_cast(_Next - _Mylast)); _Mylast = _Next; // Append. for (; _First != _Last; ++_First) { - emplace_back(*_First); // performance note: emplace_back()'s strong guarantee is unnecessary here + // performance note: _Emplace_one_at_back()'s strong guarantee is unnecessary here + _Emplace_one_at_back(*_First); } } @@ -1038,11 +1263,20 @@ private: pointer& _Mylast = _My_data._Mylast; pointer& _Myend = _My_data._Myend; + constexpr bool _Nothrow_construct = conjunction_v>, + _Uses_default_construct<_Alloc, _Ty*, _Iter_ref_t<_Iter>>>; + _My_data._Orphan_all(); const auto _Oldcapacity = static_cast(_Myend - _Myfirst); if (_Newsize > _Oldcapacity) { _Clear_and_reserve_geometric(_Newsize); - _Mylast = _Uninitialized_copy(_First, _Last, _Myfirst, _Al); + if constexpr (_Nothrow_construct) { + _Mylast = _Uninitialized_copy(_First, _Last, _Myfirst, _Al); + _ASAN_VECTOR_CREATE; + } else { + _ASAN_VECTOR_CREATE_GUARD; + _Mylast = _Uninitialized_copy(_First, _Last, _Myfirst, _Al); + } return; } @@ -1051,11 +1285,20 @@ private: // performance note: traversing [_First, _Mid) twice const _Iter _Mid = _STD next(_First, static_cast(_Oldsize)); _Copy_unchecked(_First, _Mid, _Myfirst); - _Mylast = _Uninitialized_copy(_Mid, _Last, _Mylast, _Al); + + if constexpr (_Nothrow_construct) { + _ASAN_VECTOR_MODIFY(static_cast(_Newsize - _Oldsize)); + _Mylast = _Uninitialized_copy(_Mid, _Last, _Mylast, _Al); + } else { + _ASAN_VECTOR_EXTEND_GUARD(static_cast(_Newsize - _Oldsize)); + _Mylast = _Uninitialized_copy(_Mid, _Last, _Mylast, _Al); + _ASAN_VECTOR_RELEASE_GUARD; + } } else { const pointer _Newlast = _Myfirst + _Newsize; _Copy_unchecked(_First, _Last, _Myfirst); _Destroy_range(_Newlast, _Mylast, _Al); + _ASAN_VECTOR_MODIFY(static_cast(_Newsize - _Oldsize)); _Mylast = _Newlast; } } @@ -1151,6 +1394,7 @@ private: const pointer _Newlast = _Myfirst + _Newsize; _Orphan_range(_Newlast, _Mylast); _Destroy_range(_Newlast, _Mylast, _Al); + _ASAN_VECTOR_MODIFY(static_cast(_Newsize - _Oldsize)); _Mylast = _Newlast; return; } @@ -1162,6 +1406,7 @@ private: return; } + _ASAN_VECTOR_EXTEND_GUARD(_Newsize - _Oldsize); const pointer _Oldlast = _Mylast; if constexpr (is_same_v<_Ty2, _Ty>) { _Mylast = _Uninitialized_fill_n(_Oldlast, _Newsize - _Oldsize, _Val, _Al); @@ -1169,6 +1414,7 @@ private: _STL_INTERNAL_STATIC_ASSERT(is_same_v<_Ty2, _Value_init_tag>); _Mylast = _Uninitialized_value_construct_n(_Oldlast, _Newsize - _Oldsize, _Al); } + _ASAN_VECTOR_RELEASE_GUARD; _Orphan_range(_Oldlast, _Oldlast); } @@ -1252,6 +1498,7 @@ private: if (_Myfirst) { // destroy and deallocate old array _Destroy_range(_Myfirst, _Mylast, _Al); + _ASAN_VECTOR_REMOVE; _Al.deallocate(_Myfirst, static_cast(_Myend - _Myfirst)); _Myfirst = nullptr; @@ -1297,6 +1544,7 @@ public: _Orphan_range(_Mylast - 1, _Mylast); _Alty_traits::destroy(_Getal(), _Unfancy(_Mylast - 1)); + _ASAN_VECTOR_MODIFY(-1); --_Mylast; } @@ -1315,6 +1563,7 @@ public: _Orphan_range(_Whereptr, _Mylast); _Move_unchecked(_Whereptr + 1, _Mylast, _Whereptr); _Alty_traits::destroy(_Getal(), _Unfancy(_Mylast - 1)); + _ASAN_VECTOR_MODIFY(-1); --_Mylast; return iterator(_Whereptr, _STD addressof(_My_data)); } @@ -1337,6 +1586,7 @@ public: const pointer _Newlast = _Move_unchecked(_Lastptr, _Mylast, _Firstptr); _Destroy_range(_Newlast, _Mylast, _Getal()); + _ASAN_VECTOR_MODIFY(static_cast(_Newlast - _Mylast)); _Mylast = _Newlast; } @@ -1350,6 +1600,7 @@ public: _My_data._Orphan_all(); _Destroy_range(_Myfirst, _Mylast, _Getal()); + _ASAN_VECTOR_MODIFY(static_cast(_Mylast - _Myfirst)); _Mylast = _Myfirst; } @@ -1599,12 +1850,14 @@ private: if (_Myfirst) { // destroy and deallocate old array _Destroy_range(_Myfirst, _Mylast, _Al); + _ASAN_VECTOR_REMOVE; _Al.deallocate(_Myfirst, static_cast(_Myend - _Myfirst)); } _Myfirst = _Newvec; _Mylast = _Newvec + _Newsize; _Myend = _Newvec + _Newcapacity; + _ASAN_VECTOR_CREATE; } _CONSTEXPR20 void _Tidy() noexcept { // free all storage @@ -1618,6 +1871,7 @@ private: if (_Myfirst) { // destroy and deallocate old array _Destroy_range(_Myfirst, _Mylast, _Al); + _ASAN_VECTOR_REMOVE; _Al.deallocate(_Myfirst, static_cast(_Myend - _Myfirst)); _Myfirst = nullptr; @@ -1649,6 +1903,7 @@ private: } else { static_assert(_Always_false<_Ty>, "Should be unreachable"); } + _ASAN_VECTOR_CREATE; _Guard._Target = nullptr; } @@ -1667,11 +1922,20 @@ private: pointer& _Myfirst = _My_data._Myfirst; pointer& _Mylast = _My_data._Mylast; + constexpr bool _Nothrow_construct = + conjunction_v, _Uses_default_construct<_Alloc, _Ty*, _Ty>>; + _My_data._Orphan_all(); const auto _Oldcapacity = static_cast(_My_data._Myend - _Myfirst); if (_Newsize > _Oldcapacity) { _Clear_and_reserve_geometric(_Newsize); - _Mylast = _Uninitialized_move(_First, _Last, _Myfirst, _Al); + if constexpr (_Nothrow_construct) { + _Mylast = _Uninitialized_move(_First, _Last, _Myfirst, _Al); + _ASAN_VECTOR_CREATE; + } else { + _ASAN_VECTOR_CREATE_GUARD; + _Mylast = _Uninitialized_move(_First, _Last, _Myfirst, _Al); + } return; } @@ -1679,11 +1943,20 @@ private: if (_Newsize > _Oldsize) { const pointer _Mid = _First + _Oldsize; _Move_unchecked(_First, _Mid, _Myfirst); - _Mylast = _Uninitialized_move(_Mid, _Last, _Mylast, _Al); + + if constexpr (_Nothrow_construct) { + _ASAN_VECTOR_MODIFY(static_cast(_Newsize - _Oldsize)); + _Mylast = _Uninitialized_move(_Mid, _Last, _Mylast, _Al); + } else { + _ASAN_VECTOR_EXTEND_GUARD(_Newsize - _Oldsize); + _Mylast = _Uninitialized_move(_Mid, _Last, _Mylast, _Al); + _ASAN_VECTOR_RELEASE_GUARD; + } } else { const pointer _Newlast = _Myfirst + _Newsize; _Move_unchecked(_First, _Last, _Myfirst); _Destroy_range(_Newlast, _Mylast, _Al); + _ASAN_VECTOR_MODIFY(static_cast(_Newsize - _Oldsize)); _Mylast = _Newlast; } } @@ -3150,6 +3423,15 @@ _NODISCARD _CONSTEXPR20 _Iter_diff_t<_VbIt> _Count_vbool(_VbIt _First, const _Vb return _Count; }); } + +#undef _ASAN_VECTOR_MODIFY +#undef _ASAN_VECTOR_REMOVE +#undef _ASAN_VECTOR_CREATE +#undef _ASAN_VECTOR_CREATE_GUARD +#undef _ASAN_VECTOR_EXTEND_GUARD +#undef _ASAN_VECTOR_RELEASE_GUARD +#undef _ACTIVATE_VECTOR_ANNOTATION +#undef _INSERT_VECTOR_ANNOTATION _STD_END #pragma pop_macro("new") diff --git a/stl/inc/xmemory b/stl/inc/xmemory index 0701858f35e..35008b7a9f2 100644 --- a/stl/inc/xmemory +++ b/stl/inc/xmemory @@ -774,6 +774,9 @@ _NODISCARD constexpr allocation_result::pointe #endif // __cpp_lib_concepts #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; + template class allocator { public: @@ -857,6 +860,8 @@ public: return static_cast(-1) / sizeof(_Ty); } #endif // _HAS_DEPRECATED_ALLOCATOR_MEMBERS + + static constexpr size_t _Minimum_allocation_alignment = _Asan_granularity; }; template <> diff --git a/stl/msbuild/stl_asan/dirs.proj b/stl/msbuild/stl_asan/dirs.proj new file mode 100644 index 00000000000..438fc98319d --- /dev/null +++ b/stl/msbuild/stl_asan/dirs.proj @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/stl/msbuild/stl_asan/stl_asan.files.settings.targets b/stl/msbuild/stl_asan/stl_asan.files.settings.targets new file mode 100644 index 00000000000..0ae0c140546 --- /dev/null +++ b/stl/msbuild/stl_asan/stl_asan.files.settings.targets @@ -0,0 +1,16 @@ + + + + + + + nativecpp + + + + diff --git a/stl/msbuild/stl_asan/stl_asan.nativeproj b/stl/msbuild/stl_asan/stl_asan.nativeproj new file mode 100644 index 00000000000..cdb216a23ea --- /dev/null +++ b/stl/msbuild/stl_asan/stl_asan.nativeproj @@ -0,0 +1,14 @@ + + + + + + true + + + + + diff --git a/stl/msbuild/stl_asan/stl_asan.settings.targets b/stl/msbuild/stl_asan/stl_asan.settings.targets new file mode 100644 index 00000000000..766ed8b6612 --- /dev/null +++ b/stl/msbuild/stl_asan/stl_asan.settings.targets @@ -0,0 +1,29 @@ + + + + + + p_stl_asan + LIBRARY + true + + + + + + stl_asan + + + + $(OutputLibPdbPath)$(OutputName)$(PdbVerName).pdb + + + + + + + + diff --git a/stl/msbuild/stl_base/libcp.settings.targets b/stl/msbuild/stl_base/libcp.settings.targets index 8af9d76f00e..b373b39e2a8 100644 --- a/stl/msbuild/stl_base/libcp.settings.targets +++ b/stl/msbuild/stl_base/libcp.settings.targets @@ -27,7 +27,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception $(OutputLibPdbPath)$(OutputName)$(PdbVerName).pdb $(ClDefines);_STL_CONCRT_SUPPORT - $(ClDefines);_VCRT_ALLOW_INTERNALS + $(ClDefines);_VCRT_ALLOW_INTERNALS;_ANNOTATE_VECTOR diff --git a/stl/msbuild/stl_base/stl.files.settings.targets b/stl/msbuild/stl_base/stl.files.settings.targets index 32f06b05871..0b34e661cf5 100644 --- a/stl/msbuild/stl_base/stl.files.settings.targets +++ b/stl/msbuild/stl_base/stl.files.settings.targets @@ -160,6 +160,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +#ifndef __SANITIZE_ADDRESS__ +#if defined(__clang__) && defined(__has_feature) +#if __has_feature(address_sanitizer) +#define __SANITIZE_ADDRESS__ +#endif +#endif +#endif + +#ifdef __SANITIZE_ADDRESS__ +extern "C" { +void* __sanitizer_contiguous_container_find_bad_address(const void* beg, const void* mid, const void* end) noexcept; +void __asan_describe_address(void*) noexcept; +} +#endif // ASan instrumentation enabled + +struct non_trivial_can_throw { + non_trivial_can_throw() {} +}; + +struct non_trivial_cannot_throw { + non_trivial_cannot_throw() noexcept {} +}; + +struct throw_on_construction { + throw_on_construction() { + throw 0; + } + + explicit throw_on_construction(bool should_throw) { + if (should_throw) { + throw 0; + } + } + + throw_on_construction(const throw_on_construction&) { + throw 0; + } + + [[noreturn]] throw_on_construction& operator=(const throw_on_construction&) { + throw 0; + } +}; + +struct throw_on_copy { + throw_on_copy() = default; + + throw_on_copy(const throw_on_copy&) { + throw 0; + } + + throw_on_copy(throw_on_copy&&) {} + + [[noreturn]] throw_on_copy& operator=(const throw_on_copy&) { + throw 0; + } +}; + +template +class input_iterator_tester { +private: + T data[N] = {}; + +public: + class iterator { + private: + T* curr; + + public: + using iterator_category = input_iterator_tag; + using value_type = T; + using difference_type = ptrdiff_t; + using pointer = void; + using reference = T&; + + explicit iterator(T* start) : curr(start) {} + + reference operator*() const { + return *curr; + } + + iterator& operator++() { + ++curr; + return *this; + } + + iterator operator++(int) { + auto tmp = *this; + ++curr; + return tmp; + } + + bool operator==(const iterator& that) const { + return curr == that.curr; + } + + bool operator!=(const iterator& that) const { + return !(*this == that); + } + }; + + iterator begin() { + return iterator(data); + } + + iterator end() { + return iterator(data + N); + } +}; + +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); + + if (!aligned_start) { + return true; + } + + void* mid = vec.data() + vec.size(); + mid = mid > aligned_start ? mid : aligned_start; + + void* bad_address = + __sanitizer_contiguous_container_find_bad_address(aligned_start, mid, vec.data() + vec.capacity()); + if (bad_address == nullptr) { + return true; + } + + if (bad_address < mid) { + cout << bad_address << " was marked as poisoned when it should not be." << endl; + } else { + cout << bad_address << " was not marked as poisoned when it should be." << endl; + } + 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; + __asan_describe_address(bad_address); + + return false; +#else // ^^^ ASan instrumentation enabled ^^^ // vvv ASan instrumentation disabled vvv + (void) vec; + return true; +#endif // Asan instrumentation disabled +} + +// Note: This class does not satisfy all the allocator requirements but is sufficient for this test. +template +struct custom_test_allocator { + using value_type = T; + using propagate_on_container_move_assignment = Pocma; + using is_always_equal = Stateless; +}; + +template +constexpr bool operator==( + const custom_test_allocator&, const custom_test_allocator&) noexcept { + return Stateless::value; +} + +template +constexpr bool operator!=( + const custom_test_allocator&, const custom_test_allocator&) noexcept { + return !Stateless::value; +} + +template +struct aligned_allocator : custom_test_allocator { + static constexpr size_t _Minimum_allocation_alignment = 8; + + aligned_allocator() = default; + template + constexpr aligned_allocator(const aligned_allocator&) noexcept {} + + T* allocate(size_t n) { + return new T[n]; + } + + void deallocate(T* p, size_t) noexcept { + delete[] p; + } +}; + +template +struct explicit_allocator : custom_test_allocator { + static constexpr size_t _Minimum_allocation_alignment = alignof(T); + + explicit_allocator() = default; + template + constexpr explicit_allocator(const explicit_allocator&) noexcept {} + + T* allocate(size_t n) { + T* mem = new T[n + 1]; + return mem + 1; + } + + void deallocate(T* p, size_t) noexcept { + delete[](p - 1); + } +}; + +template +struct implicit_allocator : custom_test_allocator { + implicit_allocator() = default; + template + constexpr implicit_allocator(const implicit_allocator&) noexcept {} + + T* allocate(size_t n) { + T* mem = new T[n + 1]; + return mem + 1; + } + + void deallocate(T* p, size_t) noexcept { + delete[](p - 1); + } +}; + +template +void test_push_pop() { + using T = typename Alloc::value_type; + + vector v; + assert(verify_vector(v)); + + v.push_back(T()); + assert(verify_vector(v)); + + v.pop_back(); + assert(verify_vector(v)); +} + +template +void test_reserve_shrink() { + using T = typename Alloc::value_type; + + vector v; + assert(verify_vector(v)); + + v.reserve(Size); + assert(verify_vector(v)); + + for (int i = 0; i < Size; i += Stride) { + for (int j = 0; j < Stride && j + i < Size; ++j) { + v.push_back(T()); + } + + assert(verify_vector(v)); + } + + v.push_back(T()); + assert(verify_vector(v)); + + for (int i = 0; i < Size; i += Stride) { + for (int j = 0; j < Stride && j + i < Size; ++j) { + v.pop_back(); + } + + v.shrink_to_fit(); + assert(verify_vector(v)); + } + + v.pop_back(); + assert(verify_vector(v)); + v.shrink_to_fit(); + assert(verify_vector(v)); +} + +template +void test_emplace_pop() { + using T = typename Alloc::value_type; + + vector v; + assert(verify_vector(v)); + + v.emplace_back(T()); + assert(verify_vector(v)); + + v.emplace(v.begin(), T()); + assert(verify_vector(v)); + + v.emplace(v.end(), T()); + assert(verify_vector(v)); + + v.pop_back(); + assert(verify_vector(v)); +} + +template +void test_move_assign() { + using T = typename Alloc::value_type; + + vector v1; + vector v2; + assert(verify_vector(v1)); + assert(verify_vector(v2)); + + v1.push_back(T()); + assert(verify_vector(v1)); + + v2 = move(v1); + assert(verify_vector(v1)); + assert(verify_vector(v2)); +} + +template +void test_copy_assign() { + using T = typename Alloc::value_type; + + vector v1; + vector v2; + assert(verify_vector(v1)); + assert(verify_vector(v2)); + + v1.push_back(T()); + assert(verify_vector(v1)); + + v2 = v1; + assert(verify_vector(v1)); + assert(verify_vector(v2)); +} + +template +void test_constructors() { + using T = typename Alloc::value_type; + Alloc al = Alloc(); + + vector v1; + vector v2(al); + vector v3(N, T()); + vector v4(N); + assert(verify_vector(v1)); + assert(verify_vector(v2)); + assert(verify_vector(v3)); + assert(verify_vector(v4)); + + vector v5(v3.begin(), v3.end()); + vector v6(v3); + vector v7(v3, al); + assert(verify_vector(v5)); + assert(verify_vector(v6)); + assert(verify_vector(v7)); + + vector v8(move(v3)); + vector v9(move(v4), al); + assert(verify_vector(v8)); + assert(verify_vector(v9)); + + vector v10({T(), T()}); + assert(verify_vector(v10)); +} + +template +void test_insert_n() { + using T = typename Alloc::value_type; + + vector v(1); + + v.insert(v.begin(), N, T()); + assert(verify_vector(v)); + v.insert(v.end(), N, T()); + assert(verify_vector(v)); + v.insert(v.begin() + N, N, T()); + assert(verify_vector(v)); +} + +template +void test_insert_range() { + using T = typename Alloc::value_type; + + vector v1(1); + vector v2(N); + input_iterator_tester t; + + v1.insert(v1.begin(), v2.begin(), v2.end()); + assert(verify_vector(v1)); + v1.insert(v1.end(), v2.begin(), v2.end()); + assert(verify_vector(v1)); + v1.insert(v1.begin() + N, v2.begin(), v2.end()); + assert(verify_vector(v1)); + + v1.insert(v1.begin(), t.begin(), t.end()); + assert(verify_vector(v1)); + v1.insert(v1.end(), t.begin(), t.end()); + assert(verify_vector(v1)); + v1.insert(v1.begin() + N, t.begin(), t.end()); + assert(verify_vector(v1)); +} + +template +void test_assign() { + using T = typename Alloc::value_type; + + vector v1(1); + vector v2(N + 1); + vector v3(N + 2); + input_iterator_tester t1; + input_iterator_tester t2; + + v1.assign(N, T()); + assert(verify_vector(v1)); + v1.assign(v2.begin(), v2.end()); + assert(verify_vector(v1)); + v1.assign(v3.begin(), v3.end()); + assert(verify_vector(v1)); + v1.assign(t1.begin(), t1.end()); + assert(verify_vector(v1)); + v1.assign(t2.begin(), t2.end()); + assert(verify_vector(v1)); + v1.assign(t1.begin(), t1.end()); + assert(verify_vector(v1)); + v1.assign(v3.begin(), v3.end()); + assert(verify_vector(v1)); + v1.assign(v2.begin(), v2.end()); + assert(verify_vector(v1)); + v1.assign(N, T()); + assert(verify_vector(v1)); + + vector v4; + v4.assign({T()}); + assert(verify_vector(v4)); + v4.assign({T(), T()}); + assert(verify_vector(v4)); + v4.assign({T()}); + assert(verify_vector(v4)); +} + +template +void test_resize() { + using T = typename Alloc::value_type; + + vector v; + v.resize(N, T()); + assert(verify_vector(v)); + v.resize(1, T()); + assert(verify_vector(v)); +} + +void test_push_back_throw() { + { + vector v; + v.reserve(1); + + throw_on_construction t(false); + try { + v.push_back(t); + assert(false); + } catch (int) { + assert(verify_vector(v)); + } + } + { + vector v; + + throw_on_construction t(false); + try { + v.push_back(t); + assert(false); + } catch (int) { + assert(verify_vector(v)); + } + } + { + vector v; + v.reserve(1); + + try { + v.push_back(throw_on_construction(false)); + assert(false); + } catch (int) { + assert(verify_vector(v)); + } + } + { + vector v; + + try { + v.push_back(throw_on_construction(false)); + assert(false); + } catch (int) { + assert(verify_vector(v)); + } + } +} + +void test_emplace_back_throw() { + { + vector v; + v.reserve(1); + + try { + v.emplace_back(true); + assert(false); + } catch (int) { + assert(verify_vector(v)); + } + } + { + vector v; + + try { + v.emplace_back(true); + assert(false); + } catch (int) { + assert(verify_vector(v)); + } + } +} + +void test_insert_range_throw() { + { + vector v; + + v.reserve(4); + v.emplace_back(false); + v.emplace_back(false); + + try { + v.insert(v.begin(), {throw_on_construction(false), throw_on_construction(false)}); + assert(false); + } catch (int) { + assert(verify_vector(v)); + } + } + + { + vector v; + + v.reserve(2); + v.emplace_back(false); + v.emplace_back(false); + + try { + v.insert(v.begin(), {throw_on_construction(false), throw_on_construction(false)}); + assert(false); + } catch (int) { + assert(verify_vector(v)); + } + } + + { + vector v; + + v.reserve(2); + + try { + v.insert(v.end(), {throw_on_construction(false), throw_on_construction(false)}); + assert(false); + } catch (int) { + assert(verify_vector(v)); + } + } + + { + vector v; + + try { + v.insert(v.end(), {throw_on_construction(false), throw_on_construction(false)}); + assert(false); + } catch (int) { + assert(verify_vector(v)); + } + } +} + +void test_insert_throw() { + { + vector v; + + v.reserve(3); + v.emplace_back(false); + v.emplace_back(false); + + try { + v.insert(v.begin(), throw_on_construction(false)); + assert(false); + } catch (int) { + assert(verify_vector(v)); + } + } + + { + vector v; + + v.reserve(2); + v.emplace_back(false); + v.emplace_back(false); + + try { + v.insert(v.begin(), throw_on_construction(false)); + assert(false); + } catch (int) { + assert(verify_vector(v)); + } + } + + { + vector v; + + v.reserve(1); + + try { + v.insert(v.end(), throw_on_construction(false)); + assert(false); + } catch (int) { + assert(verify_vector(v)); + } + } + + { + vector v; + + try { + v.insert(v.end(), throw_on_construction(false)); + assert(false); + } catch (int) { + assert(verify_vector(v)); + } + } +} + +void test_emplace_throw() { + { + vector v; + + v.reserve(3); + v.emplace_back(false); + v.emplace_back(false); + + try { + v.emplace(v.begin(), false); + assert(false); + } catch (int) { + assert(verify_vector(v)); + } + } + + { + vector v; + + v.reserve(2); + v.emplace_back(false); + v.emplace_back(false); + + try { + v.emplace(v.begin(), true); + assert(false); + } catch (int) { + assert(verify_vector(v)); + } + } + + { + vector v; + + v.reserve(1); + + try { + v.emplace(v.end(), true); + assert(false); + } catch (int) { + assert(verify_vector(v)); + } + } + + { + vector v; + + try { + v.emplace(v.end(), true); + assert(false); + } catch (int) { + assert(verify_vector(v)); + } + } +} + +void test_resize_throw() { + { + vector v; + + v.reserve(2); + v.emplace_back(false); + + try { + v.resize(2); + assert(false); + } catch (int) { + assert(verify_vector(v)); + } + } + + { + vector v; + + v.reserve(1); + v.emplace_back(false); + + try { + v.resize(2); + assert(false); + } catch (int) { + assert(verify_vector(v)); + } + } + + { + vector v; + + v.reserve(2); + v.push_back(throw_on_copy()); + + try { + v.resize(2, throw_on_copy()); + assert(false); + } catch (int) { + assert(verify_vector(v)); + } + } + + { + vector v; + + v.reserve(1); + v.push_back(throw_on_copy()); + + try { + v.resize(2, throw_on_copy()); + assert(false); + } catch (int) { + assert(verify_vector(v)); + } + } +} + +void test_insert_n_throw() { + { + vector v; + + v.reserve(2); + v.emplace_back(false); + + try { + v.insert(v.begin(), 2, throw_on_construction(false)); + assert(false); + } catch (int) { + assert(verify_vector(v)); + } + } + + { + vector v; + + v.reserve(2); + v.emplace_back(false); + + try { + v.insert(v.end(), 2, throw_on_construction(false)); + assert(false); + } catch (int) { + assert(verify_vector(v)); + } + } + + { + vector v; + + v.reserve(1); + v.emplace_back(false); + + try { + v.insert(v.begin(), 2, throw_on_construction(false)); + assert(false); + } catch (int) { + assert(verify_vector(v)); + } + } + + { + vector v; + + v.reserve(1); + v.emplace_back(false); + + try { + v.insert(v.end(), 2, throw_on_construction(false)); + assert(false); + } catch (int) { + assert(verify_vector(v)); + } + } + + { + vector v; + + v.reserve(2); + v.push_back(throw_on_copy()); + + try { + v.insert(v.begin(), 2, throw_on_copy()); + assert(false); + } catch (int) { + assert(verify_vector(v)); + } + } + + { + vector v; + + v.reserve(2); + v.push_back(throw_on_copy()); + + try { + v.insert(v.end(), 2, throw_on_copy()); + assert(false); + } catch (int) { + assert(verify_vector(v)); + } + } + + { + vector v; + + v.reserve(1); + v.push_back(throw_on_copy()); + + try { + v.insert(v.begin(), 2, throw_on_copy()); + assert(false); + } catch (int) { + assert(verify_vector(v)); + } + } + + { + vector v; + + v.reserve(1); + v.push_back(throw_on_copy()); + + try { + v.insert(v.end(), 2, throw_on_copy()); + assert(false); + } catch (int) { + assert(verify_vector(v)); + } + } +} + +template +void run_tests() { + test_push_pop(); + test_reserve_shrink(); + test_emplace_pop(); + test_move_assign(); + test_copy_assign(); + test_constructors(); + test_insert_n(); + test_insert_range(); + test_assign(); + test_resize(); +} + +template class AllocT> +void run_custom_allocator_matrix() { + run_tests>(); + run_tests>(); + run_tests>(); + run_tests>(); +} + +template +void run_allocator_matrix() { + run_tests>(); + run_custom_allocator_matrix(); + run_custom_allocator_matrix(); + run_custom_allocator_matrix(); +} + +int main() { + // Do some work even when we aren't instrumented + run_allocator_matrix(); +#ifdef __SANITIZE_ADDRESS__ + run_allocator_matrix(); + run_allocator_matrix(); + run_allocator_matrix(); + run_allocator_matrix(); + +// TRANSITION, LLVM-35365 +#ifndef __clang__ + test_push_back_throw(); + test_emplace_back_throw(); + test_insert_range_throw(); + test_insert_throw(); + test_emplace_throw(); + test_resize_throw(); + test_insert_n_throw(); +#endif // !__clang__ +#endif // ASan instrumentation enabled +} diff --git a/tests/utils/stl/test/format.py b/tests/utils/stl/test/format.py index c9d40635dd2..f2ad078b47f 100644 --- a/tests/utils/stl/test/format.py +++ b/tests/utils/stl/test/format.py @@ -15,6 +15,7 @@ import os import re import shutil +import sys import lit @@ -173,13 +174,14 @@ def getBuildSteps(self, test, litConfig, shared): if TestType.COMPILE in test.testType: cmd = [test.cxx, '-c', test.getSourcePath(), *test.flags, *test.compileFlags] yield TestStep(cmd, shared.execDir, shared.env, shouldFail) - elif TestType.LINK in test.testType: + elif TestType.LINK in test.testType or \ + ('clang' in test.config.available_features and 'asan' in test.config.available_features): objFile = tmpBase + '.o' cmd = [test.cxx, '-c', test.getSourcePath(), *test.flags, *test.compileFlags, '-Fo' + objFile] yield TestStep(cmd, shared.execDir, shared.env, False) - exeFile = tmpBase + '.exe' - cmd = [test.cxx, objFile, *test.flags, '-Fe' + exeFile, '-link', *test.linkFlags] + shared.execFile = tmpBase + '.exe' + cmd = ['link.exe', objFile, *test.flags, '-out:' + shared.execFile, *test.linkFlags] yield TestStep(cmd, shared.execDir, shared.env, shouldFail) elif TestType.RUN in test.testType: shared.execFile = tmpBase + '.exe' @@ -239,7 +241,10 @@ def execute(self, test, litConfig): return (lit.Test.PASS, '') except Exception as e: - litConfig.error(repr(e)) + _, _, exception_traceback = sys.exc_info() + filename = exception_traceback.tb_frame.f_code.co_filename + line_number = exception_traceback.tb_lineno + litConfig.error(repr(e) + ' at ' + filename + ':' + str(line_number)) class LibcxxTestFormat(STLTestFormat): diff --git a/tests/utils/stl/test/tests.py b/tests/utils/stl/test/tests.py index 797b9a4c9fb..9d8dc869dc6 100644 --- a/tests/utils/stl/test/tests.py +++ b/tests/utils/stl/test/tests.py @@ -53,7 +53,7 @@ def configureTest(self, litConfig): return result self._parseTest() - self._parseFlags() + self._parseFlags(litConfig) missing_required_features = self.getMissingRequiredFeatures() if missing_required_features: @@ -204,6 +204,8 @@ def _handleEnvlst(self, litConfig): self.linkFlags.extend(self.envlstEntry.getEnvVal('PM_LINK', '').split()) if ('clang'.casefold() in os.path.basename(cxx).casefold()): + self._addCustomFeature('clang') + targetArch = litConfig.target_arch.casefold() if (targetArch == 'x64'.casefold()): self.compileFlags.append('-m64') @@ -213,10 +215,13 @@ def _handleEnvlst(self, litConfig): return Result(UNSUPPORTED, 'clang targeting arm is not supported') elif (targetArch == 'arm64'.casefold()): self.compileFlags.append('--target=arm64-pc-windows-msvc') + elif ('nvcc'.casefold() in os.path.basename(cxx).casefold()): + self._addCustomFeature('nvcc') - if ('nvcc'.casefold() in os.path.basename(cxx).casefold()): # nvcc only supports targeting x64 self.requires.append('x64') + else: + self._addCustomFeature('cl') self.cxx = os.path.normpath(cxx) return None @@ -226,8 +231,9 @@ def _addCustomFeature(self, name): for action in actions: action.applyTo(self.config) - def _parseFlags(self): + def _parseFlags(self, litConfig): foundStd = False + foundCRT = False for flag in chain(self.flags, self.compileFlags, self.linkFlags): if flag[1:5] == 'std:': foundStd = True @@ -251,13 +257,41 @@ def _parseFlags(self): self.requires.append('arch_ia32') # available for x86, see features.py elif flag[1:] == 'arch:VFPv4': self.requires.append('arch_vfpv4') # available for arm, see features.py + elif flag[1:] == 'fsanitize=address': + self._addCustomFeature('asan') + elif flag[1:] == 'MDd': + self._addCustomFeature('MDd') + self._addCustomFeature('debug_CRT') + self._addCustomFeature('dynamic_CRT') + foundCRT = True + elif flag[1:] == 'MD': + self._addCustomFeature('MD') + self._addCustomFeature('dynamic_CRT') + foundCRT = True + elif flag[1:] == 'MTd': + self._addCustomFeature('MTd') + self._addCustomFeature('debug_CRT') + self._addCustomFeature('static_CRT') + foundCRT = True + elif flag[1:] == 'MT': + self._addCustomFeature('MT') + self._addCustomFeature('static_CRT') + foundCRT = True if not foundStd: self._addCustomFeature('c++14') + if not foundCRT: + self._addCustomFeature('MT') + self._addCustomFeature('static_CRT') + self._addCustomFeature('non-lockfree-atomics') # we always support non-lockfree-atomics self._addCustomFeature('is-lockfree-runtime-function') # Ditto + # clang doesn't know how to link in the VS version of the asan runtime automatically + if 'asan' in self.config.available_features and 'clang' in self.config.available_features: + self.linkFlags.append("/INFERASANLIBS") + class LibcxxTest(STLTest): def getTestName(self):