From 46a84b63b829cf209fb0aeb19038835d1a076d30 Mon Sep 17 00:00:00 2001 From: Curtis Bezault Date: Wed, 30 Jun 2021 22:21:14 -0700 Subject: [PATCH 01/59] Initial asan annotation commit --- stl/inc/vector | 178 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 154 insertions(+), 24 deletions(-) diff --git a/stl/inc/vector b/stl/inc/vector index 4ec774e6f7..6067c0bf09 100644 --- a/stl/inc/vector +++ b/stl/inc/vector @@ -438,6 +438,90 @@ constexpr _Ty* _Unfancy_maybe_null(_Ty* _Ptr) noexcept { // do nothing for plain return _Ptr; } +template +class _NODISCARD _Asan_annotater { +public: + using size_type = typename _Ty::size_type; + + constexpr _Asan_annotater(const _Ty& _Target) +#ifdef __SANITIZER_ADDRESS__ + : _Target_vec(_Target), _Old_last(nullptr) { + } +#else // ^^^ __SANITIZER_ADDRESS__ ^^^ // vvv !__SANITIZER_ADDRESS__ vvv + { + (void) _Target; + } +#endif // !__SANITIZER_ADDRESS__ + + constexpr _Asan_annotater(const _Ty& _Target, size_type _New_size) +#ifdef __SANITIZER_ADDRESS__ + : _Target_vec(_Target) { + if (_STD is_constant_evaluated()) { + return; + } + + const auto& _My_data = _Target_vec._Mypair._Myval2; + const pointer& _Myfirst = _My_data._Myfirst; + const pointer& _Mylast = _My_data._Mylast; + const pointer& _Myend = _My_data._Myend; + + size_type _Old_capacity = static_cast(_Myend - _Myfirst); + + if (_New_size > _Old_capacity) { + _Old_first = nullptr; + _Old_last = nullptr; + _Old_end = nullptr; + return; + } + + _Old_first = _Myfirst; + _Old_last = _Mylast; + _Old_end = _Myend; + size_type _Old_size = static_cast(_Mylast - _Myfirst); + + if (_New_size > _Old_size) { + __sanitizer_annotate_contiguous_container(_Myfirst, _Myend, _Old_last, _Myfirst + _New_size); + } + } +#else // ^^^ __SANITIZER_ADDRESS__ ^^^ // vvv !__SANITIZER_ADDRESS__ vvv + { + (void) _Target; + (void) _New_size; + } +#endif // !__SANITIZER_ADDRESS__ + + _CONSTEXPR20_CONTAINER ~_Asan_annotater() { +#ifndef __SANITIZER_ADDRESS__ + } +#else // ^^^ !__SANITIZER_ADDRESS__ ^^^ // vvv __SANITIZER_ADDRESS__ vvv + if (_STD is_constant_evaluated()) { + return; + } + + const auto& _My_data = _Target_vec._Mypair._Myval2; + const pointer& _Myfirst = _My_data._Myfirst; + const pointer& _Mylast = _My_data._Mylast; + const pointer& _Myend = _My_data._Myend; + + // If any of the pointers are not set we assume that entire illegal range of the vector + // needs to be annotated. We also need to check if the vector's buffer was reallocated + // from underneath us. In that case we also need to annotate the entire illegal range. + if (!_Old_first || !_Old_last || !_Old_end || _Old_first != _Myfirst || _Old_end != _Myend) { + __sanitizer_annotate_contiguous_container(_Myfirst, _Myend, _Myend, _Mylast); + } else if (_Mylast < _Old_last) { + __sanitizer_annotate_contiguous_container(_Myfirst, _Myend, _Old_last, _Mylast); + } + } +} + +private : const _Ty& _Target_vec; + +pointer _Old_first; +pointer _Old_last; +pointer _Old_end; +#endif // __SANITIZER_ADDRESS__ +}; + // CLASS TEMPLATE vector template > class vector { // varying size array of values @@ -445,9 +529,11 @@ private: template friend class _Vb_val; friend _Tidy_guard; + friend _Asan_annotater; - using _Alty = _Rebind_alloc_t<_Alloc, _Ty>; - using _Alty_traits = allocator_traits<_Alty>; + using _Alty = _Rebind_alloc_t<_Alloc, _Ty>; + using _Alty_traits = allocator_traits<_Alty>; + using _Asan_annotater = _Asan_annotater>; public: static_assert(!_ENFORCE_MATCHING_ALLOCATORS || is_same_v<_Ty, typename _Alloc::value_type>, @@ -483,18 +569,34 @@ public: _CONSTEXPR20_CONTAINER explicit vector(_CRT_GUARDOVERFLOW const size_type _Count, const _Alloc& _Al = _Alloc()) : _Mypair(_One_then_variadic_args_t{}, _Al) { + _Asan_annotater _Asan_annotate(*this); _Construct_n(_Count); } _CONSTEXPR20_CONTAINER vector( _CRT_GUARDOVERFLOW const size_type _Count, const _Ty& _Val, const _Alloc& _Al = _Alloc()) : _Mypair(_One_then_variadic_args_t{}, _Al) { + _Asan_annotater _Asan_annotate(*this); _Construct_n(_Count, _Val); } +private: + template + _CONSTEXPR20_CONTAINER _Ty& _Emplace_one_at_back(_Valty&&... _Val) { + 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)...); + } + + return *_Emplace_reallocate(_Mylast, _STD forward<_Valty>(_Val)...); + } + template , int> = 0> _CONSTEXPR20_CONTAINER vector(_Iter _First, _Iter _Last, const _Alloc& _Al = _Alloc()) : _Mypair(_One_then_variadic_args_t{}, _Al) { + _Asan_annotater _Asan_annotate(*this); _Adl_verify_range(_First, _Last); auto _UFirst = _Get_unwrapped(_First); auto _ULast = _Get_unwrapped(_Last); @@ -517,17 +619,22 @@ public: _CONSTEXPR20_CONTAINER vector(initializer_list<_Ty> _Ilist, const _Alloc& _Al = _Alloc()) : _Mypair(_One_then_variadic_args_t{}, _Al) { + _Asan_annotater _Asan_annotate(*this); _Construct_n(_Convert_size(_Ilist.size()), _Ilist.begin(), _Ilist.end()); } _CONSTEXPR20_CONTAINER vector(const vector& _Right) : _Mypair(_One_then_variadic_args_t{}, _Alty_traits::select_on_container_copy_construction(_Right._Getal())) { + _Asan_annotater _Asan_annotate(*this); + const auto& _Right_data = _Right._Mypair._Myval2; const auto _Count = static_cast(_Right_data._Mylast - _Right_data._Myfirst); _Construct_n(_Count, _Right_data._Myfirst, _Right_data._Mylast); } _CONSTEXPR20_CONTAINER vector(const vector& _Right, const _Alloc& _Al) : _Mypair(_One_then_variadic_args_t{}, _Al) { + _Asan_annotater _Asan_annotate(*this); + const auto& _Right_data = _Right._Mypair._Myval2; const auto _Count = static_cast(_Right_data._Mylast - _Right_data._Myfirst); _Construct_n(_Count, _Right_data._Myfirst, _Right_data._Mylast); @@ -555,6 +662,7 @@ public: if (_Al != _Right._Getal()) { const auto _Count = static_cast(_Right_data._Mylast - _Right_data._Myfirst); if (_Count != 0) { + _Asan_annotater _Asan_annotate(*this); _Buy_raw(_Count); _Tidy_guard _Guard{this}; _My_data._Mylast = @@ -596,33 +704,31 @@ public: private: template - _CONSTEXPR20_CONTAINER decltype(auto) _Emplace_back_with_unused_capacity(_Valty&&... _Val) { + _CONSTEXPR20_CONTAINER _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; _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 } public: template _CONSTEXPR20_CONTAINER decltype(auto) emplace_back(_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)...); - } + auto& _My_data = _Mypair._Myval2; + pointer& _Myfirst = _My_data._Myfirst; + pointer& _Mylast = _My_data._Mylast; + + _Asan_annotater _Asan_annotate(*this, static_cast(_Mylast - _Myfirst) + 1); - _Ty& _Result = *_Emplace_reallocate(_Mylast, _STD forward<_Valty>(_Val)...); + _Ty& _Result = _Emplace_one_at_back(_STD forward<_Valty>(_Val)...); #if _HAS_CXX17 return _Result; #else // ^^^ _HAS_CXX17 ^^^ // vvv !_HAS_CXX17 vvv @@ -700,6 +806,8 @@ public: "vector emplace iterator outside range"); #endif // _ITERATOR_DEBUG_LEVEL == 2 + _Asan_annotater _Asan_annotate(*this, static_cast(_Oldlast - _My_data._Myfirst) + 1); + if (_Oldlast != _My_data._Myend) { if (_Whereptr == _Oldlast) { // at back, provide strong guarantee _Emplace_back_with_unused_capacity(_STD forward<_Valty>(_Val)...); @@ -744,6 +852,8 @@ public: "vector insert iterator outside range"); #endif // _ITERATOR_DEBUG_LEVEL == 2 + _Asan_annotater _Asan_annotate(*this, static_cast(_Oldlast - _Oldfirst) + _Count); + const auto _Whereoff = static_cast(_Whereptr - _Oldfirst); const auto _Unused_capacity = static_cast(_My_data._Myend - _Oldlast); const bool _One_at_back = _Count == 1 && _Whereptr == _Oldlast; @@ -822,10 +932,9 @@ 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. for (; _First != _Last; ++_First) { - emplace_back(*_First); + _Emplace_one_at_back(*_First); } _Orphan_range(_Myfirst + _Whereoff, _Myfirst + _Oldsize); @@ -843,10 +952,13 @@ private: auto& _My_data = _Mypair._Myval2; pointer& _Mylast = _My_data._Mylast; + const pointer _Oldfirst = _My_data._Myfirst; const pointer _Oldlast = _Mylast; const auto _Unused_capacity = static_cast(_My_data._Myend - _Oldlast); + _Asan_annotater _Asan_annotate(*this, static_cast(_Oldlast - _Oldfirst) + _Count); + if (_Count == 0) { // nothing to do, avoid invalidating iterators } else if (_Count > _Unused_capacity) { // reallocate const auto _Oldsize = static_cast(_Oldlast - _Oldfirst); @@ -888,7 +1000,6 @@ private: } else { // Attempt to provide the strong guarantee for EmplaceConstructible failure. // If we encounter copy/move construction/assignment failure, provide the basic guarantee. // (For one-at-back, this provides the strong guarantee.) - const auto _Affected_elements = static_cast(_Oldlast - _Whereptr); if (_Count < _Affected_elements) { // some affected elements must be assigned @@ -959,6 +1070,7 @@ public: #endif // _ITERATOR_DEBUG_LEVEL == 2 _Adl_verify_range(_First, _Last); + const auto _Whereoff = static_cast(_Whereptr - _Oldfirst); _Insert_range(_Where, _Get_unwrapped(_First), _Get_unwrapped(_Last), _Iter_cat_t<_Iter>{}); return _Make_iterator_offset(_Whereoff); @@ -977,6 +1089,8 @@ public: _My_data._Orphan_all(); + _Asan_annotater _Asan_annotate(*this, _Newsize); + auto _Oldsize = static_cast(_Mylast - _Myfirst); if (_Newsize > _Oldsize) { const auto _Oldcapacity = static_cast(_My_data._Myend - _Myfirst); @@ -1023,7 +1137,7 @@ private: // Append. for (; _First != _Last; ++_First) { - emplace_back(*_First); // performance note: emplace_back()'s strong guarantee is unnecessary here + _Emplace_one_at_back(*_First); } } @@ -1039,6 +1153,8 @@ private: _My_data._Orphan_all(); + _Asan_annotater _Asan_annotate(*this, _Newsize); + if constexpr (conjunction_v::_Trivially_copyable>, _Uses_default_construct<_Alty, _Ty*, decltype(*_First)>, _Uses_default_destroy<_Alty, _Ty*>>) { @@ -1198,11 +1314,13 @@ private: public: _CONSTEXPR20_CONTAINER void resize(_CRT_GUARDOVERFLOW const size_type _Newsize) { // trim or append value-initialized elements, provide strong guarantee + _Asan_annotater _Asan_annotate(*this, _Newsize); _Resize(_Newsize, _Value_init_tag{}); } _CONSTEXPR20_CONTAINER void resize(_CRT_GUARDOVERFLOW const size_type _Newsize, const _Ty& _Val) { // trim or append copies of _Val, provide strong guarantee + _Asan_annotater _Asan_annotate(*this, _Newsize); _Resize(_Newsize, _Val); } @@ -1290,6 +1408,7 @@ public: _Xlength(); } + _Asan_annotater _Asan_annotate(*this); _Reallocate_exactly(_Newcapacity); } } @@ -1302,14 +1421,18 @@ public: if (_Oldfirst == _Oldlast) { _Tidy(); } else { + _Asan_annotater _Asan_annotate(*this); _Reallocate_exactly(static_cast(_Oldlast - _Oldfirst)); } } } _CONSTEXPR20_CONTAINER void pop_back() noexcept /* strengthened */ { - auto& _My_data = _Mypair._Myval2; - pointer& _Mylast = _My_data._Mylast; + auto& _My_data = _Mypair._Myval2; + pointer& _Myfirst = _My_data._Myfirst; + pointer& _Mylast = _My_data._Mylast; + + _Asan_annotater _Asan_annotate(*this, static_cast(_Mylast - _Myfirst) - 1); #if _ITERATOR_DEBUG_LEVEL == 2 _STL_VERIFY(_My_data._Myfirst != _Mylast, "vector empty before pop"); @@ -1324,6 +1447,7 @@ public: is_nothrow_move_assignable_v) /* strengthened */ { const pointer _Whereptr = _Where._Ptr; auto& _My_data = _Mypair._Myval2; + pointer& _Myfirst = _My_data._Myfirst; pointer& _Mylast = _My_data._Mylast; #if _ITERATOR_DEBUG_LEVEL == 2 @@ -1332,6 +1456,8 @@ public: "vector erase iterator outside range"); #endif // _ITERATOR_DEBUG_LEVEL == 2 + _Asan_annotater _Asan_annotate(*this, static_cast(_Mylast - _Myfirst) - 1); + _Orphan_range(_Whereptr, _Mylast); _Move_unchecked(_Whereptr + 1, _Mylast, _Whereptr); _Alty_traits::destroy(_Getal(), _Unfancy(_Mylast - 1)); @@ -1344,6 +1470,7 @@ public: const pointer _Firstptr = _First._Ptr; const pointer _Lastptr = _Last._Ptr; auto& _My_data = _Mypair._Myval2; + pointer& _Myfirst = _My_data._Myfirst; pointer& _Mylast = _My_data._Mylast; #if _ITERATOR_DEBUG_LEVEL == 2 @@ -1356,6 +1483,7 @@ public: _Orphan_range(_Firstptr, _Mylast); const pointer _Newlast = _Move_unchecked(_Lastptr, _Mylast, _Firstptr); + _Asan_annotater _Asan_annotate(*this, static_cast(_Newlast - _Myfirst)); _Destroy_range(_Newlast, _Mylast, _Getal()); _Mylast = _Newlast; } @@ -1368,6 +1496,8 @@ public: pointer& _Myfirst = _My_data._Myfirst; pointer& _Mylast = _My_data._Mylast; + _Asan_annotater _Asan_annotate(*this); + _My_data._Orphan_all(); _Destroy_range(_Myfirst, _Mylast, _Getal()); _Mylast = _Myfirst; @@ -1467,8 +1597,8 @@ public: } _NODISCARD _CONSTEXPR20_CONTAINER size_type max_size() const noexcept { - return (_STD min)( - static_cast((numeric_limits::max)()), _Alty_traits::max_size(_Getal())); + return (_STD min) (static_cast((numeric_limits::max) ()), + _Alty_traits::max_size(_Getal())); } _NODISCARD _CONSTEXPR20_CONTAINER size_type capacity() const noexcept { @@ -1875,7 +2005,7 @@ _NODISCARD _CONSTEXPR20_CONTAINER _Synth_three_way_result<_Ty> operator<=>( const vector<_Ty, _Alloc>& _Left, const vector<_Ty, _Alloc>& _Right) { if constexpr (is_same_v<_Ty, bool>) { // This optimization works because vector "trims" its underlying storage by zeroing out unused bits. - const auto _Min_word_size = (_STD min)(_Left._Myvec.size(), _Right._Myvec.size()); + const auto _Min_word_size = (_STD min) (_Left._Myvec.size(), _Right._Myvec.size()); const auto _Left_words = _Left._Myvec._Unchecked_begin(); const auto _Right_words = _Right._Myvec._Unchecked_begin(); @@ -1902,7 +2032,7 @@ _NODISCARD _CONSTEXPR20_CONTAINER bool operator<(const vector<_Ty, _Alloc>& _Lef auto _First = _Left._Myvec._Unchecked_begin(); auto _Other = _Right._Myvec._Unchecked_begin(); - const auto _Last = _First + (_STD min)(_Left._Myvec.size(), _Right._Myvec.size()); + const auto _Last = _First + (_STD min) (_Left._Myvec.size(), _Right._Myvec.size()); for (; _First != _Last; ++_First, (void) ++_Other) { using _Comp = _Vbase_compare_three_way; @@ -2746,7 +2876,7 @@ public: } _NODISCARD _CONSTEXPR20_CONTAINER size_type max_size() const noexcept { - constexpr auto _Diff_max = static_cast((numeric_limits::max)()); + constexpr auto _Diff_max = static_cast((numeric_limits::max) ()); const size_type _Ints_max = this->_Myvec.max_size(); if (_Ints_max > _Diff_max / _VBITS) { // max_size bound by difference_type limits return _Diff_max; From b6cf70cbc1f53a3c34382c625205875e48bacfcc Mon Sep 17 00:00:00 2001 From: Curtis Bezault Date: Wed, 30 Jun 2021 22:12:05 -0700 Subject: [PATCH 02/59] Some testing fixups --- tests/utils/stl/test/tests.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/utils/stl/test/tests.py b/tests/utils/stl/test/tests.py index 797b9a4c9f..8e1c7f9e8a 100644 --- a/tests/utils/stl/test/tests.py +++ b/tests/utils/stl/test/tests.py @@ -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 @@ -251,6 +256,9 @@ 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') + self.requires.append('cl') # We only run AddressSanitizer tests with cl for now if not foundStd: self._addCustomFeature('c++14') From daa4db9bf10400678438d9c6b33630c7947e469e Mon Sep 17 00:00:00 2001 From: Curtis Bezault Date: Wed, 21 Jul 2021 12:17:55 -0700 Subject: [PATCH 03/59] First working pass at asan annotations --- stl/inc/vector | 280 +++++++++----- tests/std/test.lst | 1 + .../GH_002030_asan_annotate_vector/env.lst | 6 + .../GH_002030_asan_annotate_vector/test.cpp | 360 ++++++++++++++++++ 4 files changed, 554 insertions(+), 93 deletions(-) create mode 100644 tests/std/tests/GH_002030_asan_annotate_vector/env.lst create mode 100644 tests/std/tests/GH_002030_asan_annotate_vector/test.cpp diff --git a/stl/inc/vector b/stl/inc/vector index 6067c0bf09..93316ad055 100644 --- a/stl/inc/vector +++ b/stl/inc/vector @@ -438,90 +438,190 @@ constexpr _Ty* _Unfancy_maybe_null(_Ty* _Ptr) noexcept { // do nothing for plain return _Ptr; } -template +#ifdef __SANITIZE_ADDRESS__ +extern "C" void __sanitizer_annotate_contiguous_container(const void*, const void*, const void*, const void*); + +template +struct _Has_minimum_allocation_alignment : bool_constant= 8>, is_same>>> {}; + +template +struct _Has_minimum_allocation_alignment<_Vec, void_t> : bool_constant<_Vec::allocator_type::_Minimum_allocation_alignment >= 8> {}; + +template ::value> class _NODISCARD _Asan_annotater { -public: - using size_type = typename _Ty::size_type; +private: + using _Alloc = typename _Ty::allocator_type; + using _Pointer = typename _Ty::pointer; + using _Size_type = typename _Ty::size_type; + using _Value_type = typename _Ty::value_type; +public: constexpr _Asan_annotater(const _Ty& _Target) -#ifdef __SANITIZER_ADDRESS__ - : _Target_vec(_Target), _Old_last(nullptr) { - } -#else // ^^^ __SANITIZER_ADDRESS__ ^^^ // vvv !__SANITIZER_ADDRESS__ vvv - { - (void) _Target; - } -#endif // !__SANITIZER_ADDRESS__ + : _Target_vec(_Target), _Old_last(nullptr) {} - constexpr _Asan_annotater(const _Ty& _Target, size_type _New_size) -#ifdef __SANITIZER_ADDRESS__ + _CONSTEXPR20_CONTAINER _Asan_annotater(const _Ty& _Target, _Size_type _New_size) : _Target_vec(_Target) { - if (_STD is_constant_evaluated()) { +#ifdef __cpp_lib_constexpr_dynamic_alloc + if (_STD is_constant_evaluated()) { return; } +#endif // __cpp_lib_constexpr_dynamic_alloc - const auto& _My_data = _Target_vec._Mypair._Myval2; - const pointer& _Myfirst = _My_data._Myfirst; - const pointer& _Mylast = _My_data._Mylast; - const pointer& _Myend = _My_data._Myend; + const auto& _My_data = _Target_vec._Mypair._Myval2; + const _Pointer& _Myfirst = _My_data._Myfirst; + const _Pointer& _Mylast = _My_data._Mylast; + const _Pointer& _Myend = _My_data._Myend; - size_type _Old_capacity = static_cast(_Myend - _Myfirst); + _Size_type _Old_capacity = static_cast<_Size_type>(_Myend - _Myfirst); if (_New_size > _Old_capacity) { - _Old_first = nullptr; _Old_last = nullptr; - _Old_end = nullptr; return; } - _Old_first = _Myfirst; - _Old_last = _Mylast; - _Old_end = _Myend; - size_type _Old_size = static_cast(_Mylast - _Myfirst); + _Old_last = _Mylast; + _Size_type _Old_size = static_cast<_Size_type>(_Mylast - _Myfirst); if (_New_size > _Old_size) { __sanitizer_annotate_contiguous_container(_Myfirst, _Myend, _Old_last, _Myfirst + _New_size); } } -#else // ^^^ __SANITIZER_ADDRESS__ ^^^ // vvv !__SANITIZER_ADDRESS__ vvv - { - (void) _Target; - (void) _New_size; - } -#endif // !__SANITIZER_ADDRESS__ _CONSTEXPR20_CONTAINER ~_Asan_annotater() { -#ifndef __SANITIZER_ADDRESS__ - } -#else // ^^^ !__SANITIZER_ADDRESS__ ^^^ // vvv __SANITIZER_ADDRESS__ vvv +#ifdef __cpp_lib_constexpr_dynamic_alloc if (_STD is_constant_evaluated()) { return; } +#endif // __cpp_lib_constexpr_dynamic_alloc - const auto& _My_data = _Target_vec._Mypair._Myval2; - const pointer& _Myfirst = _My_data._Myfirst; - const pointer& _Mylast = _My_data._Mylast; - const pointer& _Myend = _My_data._Myend; + const auto& _My_data = _Target_vec._Mypair._Myval2; + const _Pointer& _Myfirst = _My_data._Myfirst; + const _Pointer& _Mylast = _My_data._Mylast; + const _Pointer& _Myend = _My_data._Myend; - // If any of the pointers are not set we assume that entire illegal range of the vector - // needs to be annotated. We also need to check if the vector's buffer was reallocated - // from underneath us. In that case we also need to annotate the entire illegal range. - if (!_Old_first || !_Old_last || !_Old_end || _Old_first != _Myfirst || _Old_end != _Myend) { + if (!_Old_last) { __sanitizer_annotate_contiguous_container(_Myfirst, _Myend, _Myend, _Mylast); } else if (_Mylast < _Old_last) { __sanitizer_annotate_contiguous_container(_Myfirst, _Myend, _Old_last, _Mylast); } } -} -private : const _Ty& _Target_vec; +private: + const _Ty& _Target_vec; + + _Pointer _Old_last; +}; + +template +class _NODISCARD _Asan_annotater<_Ty, false> { +private: + using _Alloc = typename _Ty::allocator_type; + using _Pointer = typename _Ty::pointer; + using _Size_type = typename _Ty::size_type; + using _Value_type = typename _Ty::value_type; + + const void* _Get_aligned_start() { + const auto& _My_data = _Target_vec._Mypair._Myval2; + const _Pointer& _Myfirst = _My_data._Myfirst; + const _Pointer& _Myend = _My_data._Myend; + const _Size_type _Capacity = static_cast<_Size_type>(_Myend - _Myfirst); + + if (_Capacity * sizeof(_Value_type) < 8) { + uintptr_t _Alignment_offset = reinterpret_cast(_Myfirst) & 0x7; + if (_Alignment_offset) { + _Alignment_offset = 8 - _Alignment_offset; + } + + if (_Capacity * sizeof(_Value_type) > _Alignment_offset) { + return reinterpret_cast(_Myfirst) + _Alignment_offset; + } else { + return nullptr; + } + } else { + return reinterpret_cast((reinterpret_cast(_Myfirst) + 7) & ~7); + } + } + +public: + constexpr _Asan_annotater(const _Ty& _Target) + : _Target_vec(_Target), _Old_last(nullptr), _Aligned_first(nullptr) {} + + _CONSTEXPR20_CONTAINER _Asan_annotater(const _Ty& _Target, _Size_type _New_size) + : _Target_vec(_Target) { +#ifdef __cpp_lib_constexpr_dynamic_alloc + if (_STD is_constant_evaluated()) { + return; + } +#endif // __cpp_lib_constexpr_dynamic_alloc + + const auto& _My_data = _Target_vec._Mypair._Myval2; + const _Pointer& _Myfirst = _My_data._Myfirst; + const _Pointer& _Mylast = _My_data._Mylast; + const _Pointer& _Myend = _My_data._Myend; + + const _Size_type _Old_capacity = static_cast<_Size_type>(_Myend - _Myfirst); + + if (_New_size > _Old_capacity) { + _Old_last = nullptr; + return; + } + + _Aligned_first = _Get_aligned_start(); + if (!_Aligned_first) { + _Old_last = _Myend; + } else { + const void* _New_last = static_cast(_Myfirst + _New_size); + _New_last = _New_last > _Aligned_first ? _New_last : _Aligned_first; + _Old_last = _Mylast > _Aligned_first ? _Mylast : _Aligned_first; + + if (_New_last > _Old_last) { + __sanitizer_annotate_contiguous_container(_Aligned_first, _Myend, _Old_last, _New_last); + } + } + } + + _CONSTEXPR20_CONTAINER ~_Asan_annotater() { +#ifdef __cpp_lib_constexpr_dynamic_alloc + if (_STD is_constant_evaluated()) { + return; + } +#endif // __cpp_lib_constexpr_dynamic_alloc + + const auto& _My_data = _Target_vec._Mypair._Myval2; + const _Pointer& _Mylast = _My_data._Mylast; + const _Pointer& _Myend = _My_data._Myend; -pointer _Old_first; -pointer _Old_last; -pointer _Old_end; -#endif // __SANITIZER_ADDRESS__ + if (!_Old_last) { + _Aligned_first = _Get_aligned_start(); + if (_Aligned_first) { + const void* _New_last = static_cast(_Mylast); + _New_last = _New_last > _Aligned_first ? _New_last : _Aligned_first; + + __sanitizer_annotate_contiguous_container(_Aligned_first, _Myend, _Myend, _New_last); + } + return; + } else if (_Aligned_first) { + const void* _New_last = static_cast(_Mylast); + _New_last = _New_last > _Aligned_first ? _New_last : _Aligned_first; + + if (_Old_last > _New_last) { + __sanitizer_annotate_contiguous_container(_Aligned_first, _Myend, _Old_last, _New_last); + } + } + } + +private: + const _Ty& _Target_vec; + + const void *_Old_last; + const void *_Aligned_first; }; +#define _ASAN_ANNOTATE(...) _Asan_annotater>> _Annotater(__VA_ARGS__) +#else // ^^^ __SANITIZE_ADDRESS__ ^^^ / vvv !__SANITIZE_ADDRESS__ vvv +#define _ASAN_ANNOTATE(...) +#endif // !__SANITIZE_ADDRESS__ + // CLASS TEMPLATE vector template > class vector { // varying size array of values @@ -529,11 +629,9 @@ private: template friend class _Vb_val; friend _Tidy_guard; - friend _Asan_annotater; using _Alty = _Rebind_alloc_t<_Alloc, _Ty>; using _Alty_traits = allocator_traits<_Alty>; - using _Asan_annotater = _Asan_annotater>; public: static_assert(!_ENFORCE_MATCHING_ALLOCATORS || is_same_v<_Ty, typename _Alloc::value_type>, @@ -549,9 +647,25 @@ public: using difference_type = typename _Alty_traits::difference_type; private: +#ifdef __SANITIZE_ADDRESS__ + friend _Asan_annotater>; +#endif // __SANITIZE_ADDRESS__ + using _Scary_val = _Vector_val, _Simple_types<_Ty>, _Vec_iter_types<_Ty, size_type, difference_type, pointer, const_pointer, _Ty&, const _Ty&>>>; + template + _CONSTEXPR20_CONTAINER _Ty& _Emplace_one_at_back(_Valty&&... _Val) { + 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)...); + } + + return *_Emplace_reallocate(_Mylast, _STD forward<_Valty>(_Val)...); + } + public: using iterator = _Vector_iterator<_Scary_val>; using const_iterator = _Vector_const_iterator<_Scary_val>; @@ -569,34 +683,21 @@ public: _CONSTEXPR20_CONTAINER explicit vector(_CRT_GUARDOVERFLOW const size_type _Count, const _Alloc& _Al = _Alloc()) : _Mypair(_One_then_variadic_args_t{}, _Al) { - _Asan_annotater _Asan_annotate(*this); + _ASAN_ANNOTATE(*this); _Construct_n(_Count); } _CONSTEXPR20_CONTAINER vector( _CRT_GUARDOVERFLOW const size_type _Count, const _Ty& _Val, const _Alloc& _Al = _Alloc()) : _Mypair(_One_then_variadic_args_t{}, _Al) { - _Asan_annotater _Asan_annotate(*this); + _ASAN_ANNOTATE(*this); _Construct_n(_Count, _Val); } -private: - template - _CONSTEXPR20_CONTAINER _Ty& _Emplace_one_at_back(_Valty&&... _Val) { - 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)...); - } - - return *_Emplace_reallocate(_Mylast, _STD forward<_Valty>(_Val)...); - } - template , int> = 0> _CONSTEXPR20_CONTAINER vector(_Iter _First, _Iter _Last, const _Alloc& _Al = _Alloc()) : _Mypair(_One_then_variadic_args_t{}, _Al) { - _Asan_annotater _Asan_annotate(*this); + _ASAN_ANNOTATE(*this); _Adl_verify_range(_First, _Last); auto _UFirst = _Get_unwrapped(_First); auto _ULast = _Get_unwrapped(_Last); @@ -609,7 +710,7 @@ private: _Tidy_guard _Guard{this}; for (; _UFirst != _ULast; ++_UFirst) { - emplace_back(*_UFirst); // performance note: emplace_back()'s strong guarantee is unnecessary here + _Emplace_one_at_back(*_UFirst); } _Guard._Target = nullptr; @@ -619,13 +720,13 @@ private: _CONSTEXPR20_CONTAINER vector(initializer_list<_Ty> _Ilist, const _Alloc& _Al = _Alloc()) : _Mypair(_One_then_variadic_args_t{}, _Al) { - _Asan_annotater _Asan_annotate(*this); + _ASAN_ANNOTATE(*this); _Construct_n(_Convert_size(_Ilist.size()), _Ilist.begin(), _Ilist.end()); } _CONSTEXPR20_CONTAINER vector(const vector& _Right) : _Mypair(_One_then_variadic_args_t{}, _Alty_traits::select_on_container_copy_construction(_Right._Getal())) { - _Asan_annotater _Asan_annotate(*this); + _ASAN_ANNOTATE(*this); const auto& _Right_data = _Right._Mypair._Myval2; const auto _Count = static_cast(_Right_data._Mylast - _Right_data._Myfirst); @@ -633,7 +734,7 @@ private: } _CONSTEXPR20_CONTAINER vector(const vector& _Right, const _Alloc& _Al) : _Mypair(_One_then_variadic_args_t{}, _Al) { - _Asan_annotater _Asan_annotate(*this); + _ASAN_ANNOTATE(*this); const auto& _Right_data = _Right._Mypair._Myval2; const auto _Count = static_cast(_Right_data._Mylast - _Right_data._Myfirst); @@ -662,7 +763,7 @@ private: if (_Al != _Right._Getal()) { const auto _Count = static_cast(_Right_data._Mylast - _Right_data._Myfirst); if (_Count != 0) { - _Asan_annotater _Asan_annotate(*this); + _ASAN_ANNOTATE(*this); _Buy_raw(_Count); _Tidy_guard _Guard{this}; _My_data._Mylast = @@ -709,7 +810,6 @@ private: 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; @@ -722,11 +822,7 @@ public: template _CONSTEXPR20_CONTAINER decltype(auto) emplace_back(_Valty&&... _Val) { // insert by perfectly forwarding into element at end, provide strong guarantee - auto& _My_data = _Mypair._Myval2; - pointer& _Myfirst = _My_data._Myfirst; - pointer& _Mylast = _My_data._Mylast; - - _Asan_annotater _Asan_annotate(*this, static_cast(_Mylast - _Myfirst) + 1); + _ASAN_ANNOTATE(*this, static_cast(_Mypair._Myval2._Mylast - _Mypair._Myval2._Myfirst) + 1); _Ty& _Result = _Emplace_one_at_back(_STD forward<_Valty>(_Val)...); #if _HAS_CXX17 @@ -806,7 +902,7 @@ public: "vector emplace iterator outside range"); #endif // _ITERATOR_DEBUG_LEVEL == 2 - _Asan_annotater _Asan_annotate(*this, static_cast(_Oldlast - _My_data._Myfirst) + 1); + _ASAN_ANNOTATE(*this, static_cast(_Oldlast - _My_data._Myfirst) + 1); if (_Oldlast != _My_data._Myend) { if (_Whereptr == _Oldlast) { // at back, provide strong guarantee @@ -852,7 +948,7 @@ public: "vector insert iterator outside range"); #endif // _ITERATOR_DEBUG_LEVEL == 2 - _Asan_annotater _Asan_annotate(*this, static_cast(_Oldlast - _Oldfirst) + _Count); + _ASAN_ANNOTATE(*this, static_cast(_Oldlast - _Oldfirst) + _Count); const auto _Whereoff = static_cast(_Whereptr - _Oldfirst); const auto _Unused_capacity = static_cast(_My_data._Myend - _Oldlast); @@ -957,7 +1053,7 @@ private: const pointer _Oldlast = _Mylast; const auto _Unused_capacity = static_cast(_My_data._Myend - _Oldlast); - _Asan_annotater _Asan_annotate(*this, static_cast(_Oldlast - _Oldfirst) + _Count); + _ASAN_ANNOTATE(*this, static_cast(_Oldlast - _Oldfirst) + _Count); if (_Count == 0) { // nothing to do, avoid invalidating iterators } else if (_Count > _Unused_capacity) { // reallocate @@ -1000,6 +1096,7 @@ private: } else { // Attempt to provide the strong guarantee for EmplaceConstructible failure. // If we encounter copy/move construction/assignment failure, provide the basic guarantee. // (For one-at-back, this provides the strong guarantee.) + const auto _Affected_elements = static_cast(_Oldlast - _Whereptr); if (_Count < _Affected_elements) { // some affected elements must be assigned @@ -1070,7 +1167,6 @@ public: #endif // _ITERATOR_DEBUG_LEVEL == 2 _Adl_verify_range(_First, _Last); - const auto _Whereoff = static_cast(_Whereptr - _Oldfirst); _Insert_range(_Where, _Get_unwrapped(_First), _Get_unwrapped(_Last), _Iter_cat_t<_Iter>{}); return _Make_iterator_offset(_Whereoff); @@ -1089,7 +1185,7 @@ public: _My_data._Orphan_all(); - _Asan_annotater _Asan_annotate(*this, _Newsize); + _ASAN_ANNOTATE(*this, _Newsize); auto _Oldsize = static_cast(_Mylast - _Myfirst); if (_Newsize > _Oldsize) { @@ -1153,7 +1249,7 @@ private: _My_data._Orphan_all(); - _Asan_annotater _Asan_annotate(*this, _Newsize); + _ASAN_ANNOTATE(*this, _Newsize); if constexpr (conjunction_v::_Trivially_copyable>, _Uses_default_construct<_Alty, _Ty*, decltype(*_First)>, @@ -1278,6 +1374,8 @@ private: template _CONSTEXPR20_CONTAINER void _Resize(const size_type _Newsize, const _Ty2& _Val) { // trim or append elements, provide strong guarantee + _ASAN_ANNOTATE(*this, _Newsize); + auto& _Al = _Getal(); auto& _My_data = _Mypair._Myval2; pointer& _Myfirst = _My_data._Myfirst; @@ -1314,13 +1412,11 @@ private: public: _CONSTEXPR20_CONTAINER void resize(_CRT_GUARDOVERFLOW const size_type _Newsize) { // trim or append value-initialized elements, provide strong guarantee - _Asan_annotater _Asan_annotate(*this, _Newsize); _Resize(_Newsize, _Value_init_tag{}); } _CONSTEXPR20_CONTAINER void resize(_CRT_GUARDOVERFLOW const size_type _Newsize, const _Ty& _Val) { // trim or append copies of _Val, provide strong guarantee - _Asan_annotater _Asan_annotate(*this, _Newsize); _Resize(_Newsize, _Val); } @@ -1408,7 +1504,7 @@ public: _Xlength(); } - _Asan_annotater _Asan_annotate(*this); + _ASAN_ANNOTATE(*this); _Reallocate_exactly(_Newcapacity); } } @@ -1421,7 +1517,7 @@ public: if (_Oldfirst == _Oldlast) { _Tidy(); } else { - _Asan_annotater _Asan_annotate(*this); + _ASAN_ANNOTATE(*this); _Reallocate_exactly(static_cast(_Oldlast - _Oldfirst)); } } @@ -1429,10 +1525,9 @@ public: _CONSTEXPR20_CONTAINER void pop_back() noexcept /* strengthened */ { auto& _My_data = _Mypair._Myval2; - pointer& _Myfirst = _My_data._Myfirst; pointer& _Mylast = _My_data._Mylast; - _Asan_annotater _Asan_annotate(*this, static_cast(_Mylast - _Myfirst) - 1); + _ASAN_ANNOTATE(*this, static_cast(_Mylast - _My_data._Myfirst) - 1); #if _ITERATOR_DEBUG_LEVEL == 2 _STL_VERIFY(_My_data._Myfirst != _Mylast, "vector empty before pop"); @@ -1456,7 +1551,7 @@ public: "vector erase iterator outside range"); #endif // _ITERATOR_DEBUG_LEVEL == 2 - _Asan_annotater _Asan_annotate(*this, static_cast(_Mylast - _Myfirst) - 1); + _ASAN_ANNOTATE(*this, static_cast(_Mylast - _Myfirst) - 1); _Orphan_range(_Whereptr, _Mylast); _Move_unchecked(_Whereptr + 1, _Mylast, _Whereptr); @@ -1470,7 +1565,6 @@ public: const pointer _Firstptr = _First._Ptr; const pointer _Lastptr = _Last._Ptr; auto& _My_data = _Mypair._Myval2; - pointer& _Myfirst = _My_data._Myfirst; pointer& _Mylast = _My_data._Mylast; #if _ITERATOR_DEBUG_LEVEL == 2 @@ -1483,7 +1577,7 @@ public: _Orphan_range(_Firstptr, _Mylast); const pointer _Newlast = _Move_unchecked(_Lastptr, _Mylast, _Firstptr); - _Asan_annotater _Asan_annotate(*this, static_cast(_Newlast - _Myfirst)); + _ASAN_ANNOTATE(*this, static_cast(_Newlast - _My_data._Myfirst)); _Destroy_range(_Newlast, _Mylast, _Getal()); _Mylast = _Newlast; } @@ -1496,7 +1590,7 @@ public: pointer& _Myfirst = _My_data._Myfirst; pointer& _Mylast = _My_data._Mylast; - _Asan_annotater _Asan_annotate(*this); + _ASAN_ANNOTATE(*this); _My_data._Orphan_all(); _Destroy_range(_Myfirst, _Mylast, _Getal()); diff --git a/tests/std/test.lst b/tests/std/test.lst index 38896e23e0..d04d219fcf 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -175,6 +175,7 @@ tests\GH_001411_core_headers tests\GH_001530_binomial_accuracy tests\GH_001541_case_sensitive_boolalpha tests\GH_001638_dllexport_derived_classes +tests\GH_002030_asan_annotate_vector tests\LWG2597_complex_branch_cut tests\LWG3018_shared_ptr_function tests\P0019R8_atomic_ref diff --git a/tests/std/tests/GH_002030_asan_annotate_vector/env.lst b/tests/std/tests/GH_002030_asan_annotate_vector/env.lst new file mode 100644 index 0000000000..f398a6df62 --- /dev/null +++ b/tests/std/tests/GH_002030_asan_annotate_vector/env.lst @@ -0,0 +1,6 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_matrix.lst +RUNALL_CROSSLIST +PM_CL="-fsanitize=address /Zi /fno-sanitize-address-vcasan-lib /wd4611" PM_LINK="/debug" diff --git a/tests/std/tests/GH_002030_asan_annotate_vector/test.cpp b/tests/std/tests/GH_002030_asan_annotate_vector/test.cpp new file mode 100644 index 0000000000..d8dfa22477 --- /dev/null +++ b/tests/std/tests/GH_002030_asan_annotate_vector/test.cpp @@ -0,0 +1,360 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +// REQUIRES: asan + +#include +#include +#include +#include +#include + +using namespace std; + +extern "C" int __sanitizer_verify_contiguous_container(const void *beg, const void *mid, const void *end); + +template +bool verify_vector(vector &vec) { + size_t buffer_size = vec.capacity() * sizeof(T); + void* buffer = static_cast(vec.data()); + void* aligned_start = align(8, 1, buffer, buffer_size); + + if (!aligned_start) { + return true; + } + + void* mid = static_cast(vec.data() + vec.size()); + mid = mid > aligned_start ? mid : aligned_start; + + return __sanitizer_verify_contiguous_container(aligned_start, mid, vec.data() + vec.capacity()) != 0; +} + +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& lhs, + const custom_test_allocator& rhs) noexcept +{ + return Stateless::value || (&lhs == &rhs); +} + +template +constexpr bool operator!=(const custom_test_allocator& lhs, + const custom_test_allocator& rhs) noexcept +{ + return !(lhs == rhs); +} + +template +struct aligned_allocator : public 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 : public 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 : public 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_case_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_case_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 = i; 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 = i; j < Stride && j + i < Size; ++j) { + v.pop_back(); + } + + v.shrink_to_fit(); + assert(verify_vector(v)); + } + + v.pop_back(); + v.shrink_to_fit(); + assert(verify_vector(v)); +} + +template +void test_case_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_case_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_case_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_case_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_case_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_case_insert_range() { + using T = typename Alloc::value_type; + + vector v1(1); + vector v2(N); + list l(N); + + 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(), l.begin(), l.end()); + assert(verify_vector(v1)); + v1.insert(v1.end(), l.begin(), l.end()); + assert(verify_vector(v1)); + v1.insert(v1.begin() + N, l.begin(), l.end()); + assert(verify_vector(v1)); +} + +template +void test_case_assign() { + using T = typename Alloc::value_type; + + vector v1(1); + vector v2(N + 1); + vector v3(N + 2); + list l1(N + 2); + list l2(N + 3); + + 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(l1.begin(), l1.end()); + assert(verify_vector(v1)); + v1.assign(l2.begin(), l2.end()); + assert(verify_vector(v1)); + v1.assign(l1.begin(), l1.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_case_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)); +} + +template +void run_tests() { + test_case_push_pop(); + test_case_reserve_shrink(); + test_case_emplace_pop(); + test_case_move_assign(); + test_case_copy_assign(); + test_case_constructors(); + test_case_insert_n(); + test_case_insert_range(); + test_case_assign(); + test_case_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() { + run_allocator_matrix(); + run_allocator_matrix(); + run_allocator_matrix(); + + // TODO: Test a type that throws. +} From 67b9a50bad550008194ae3780745509b602639b6 Mon Sep 17 00:00:00 2001 From: Curtis Bezault Date: Wed, 21 Jul 2021 12:31:59 -0700 Subject: [PATCH 04/59] Fixup merge error and env.lst --- stl/inc/vector | 6 ++-- .../GH_002030_asan_annotate_vector/env.lst | 29 ++++++++++++++++++- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/stl/inc/vector b/stl/inc/vector index efb61c1805..a426d526da 100644 --- a/stl/inc/vector +++ b/stl/inc/vector @@ -548,7 +548,7 @@ public: constexpr _Asan_annotater(const _Ty& _Target) : _Target_vec(_Target), _Old_last(nullptr), _Aligned_first(nullptr) {} - _CONSTEXPR20_CONTAINER _Asan_annotater(const _Ty& _Target, _Size_type _New_size) + _CONSTEXPR20 _Asan_annotater(const _Ty& _Target, _Size_type _New_size) : _Target_vec(_Target) { #ifdef __cpp_lib_constexpr_dynamic_alloc if (_STD is_constant_evaluated()) { @@ -582,7 +582,7 @@ public: } } - _CONSTEXPR20_CONTAINER ~_Asan_annotater() { + _CONSTEXPR20 ~_Asan_annotater() { #ifdef __cpp_lib_constexpr_dynamic_alloc if (_STD is_constant_evaluated()) { return; @@ -657,7 +657,7 @@ private: _Vec_iter_types<_Ty, size_type, difference_type, pointer, const_pointer, _Ty&, const _Ty&>>>; template - _CONSTEXPR20_CONTAINER _Ty& _Emplace_one_at_back(_Valty&&... _Val) { + _CONSTEXPR20 _Ty& _Emplace_one_at_back(_Valty&&... _Val) { auto& _My_data = _Mypair._Myval2; pointer& _Mylast = _My_data._Mylast; diff --git a/tests/std/tests/GH_002030_asan_annotate_vector/env.lst b/tests/std/tests/GH_002030_asan_annotate_vector/env.lst index f398a6df62..ee5c6357e1 100644 --- a/tests/std/tests/GH_002030_asan_annotate_vector/env.lst +++ b/tests/std/tests/GH_002030_asan_annotate_vector/env.lst @@ -1,6 +1,33 @@ # Copyright (c) Microsoft Corporation. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -RUNALL_INCLUDE ..\usual_matrix.lst +RUNALL_INCLUDE ..\prefix.lst +RUNALL_CROSSLIST +PM_CL="/BE /c /EHsc /MD /std:c++14 /w14640 /Zc:threadSafeInit-" +PM_CL="/BE /c /EHsc /MDd /std:c++17 /permissive- /w14640 /Zc:threadSafeInit-" +PM_CL="/BE /c /EHsc /MT /std:c++20 /permissive- /w14640 /Zc:threadSafeInit-" +PM_CL="/BE /c /EHsc /MTd /std:c++latest /permissive- /w14640 /Zc:threadSafeInit-" +PM_CL="/EHsc /MD /std:c++14 /w14640 /Zc:threadSafeInit-" +PM_CL="/EHsc /MD /std:c++17 /w14640 /Zc:threadSafeInit-" +PM_CL="/EHsc /MD /std:c++20 /w14640 /Zc:threadSafeInit-" +PM_CL="/EHsc /MD /std:c++latest /permissive- /Zc:char8_t- /w14640 /Zc:threadSafeInit- /Zc:preprocessor" +PM_CL="/EHsc /MD /std:c++latest /permissive- /w14640 /Zc:threadSafeInit- /Zc:noexceptTypes-" +PM_CL="/EHsc /MDd /std:c++14 /fp:except /w14640 /Zc:threadSafeInit- /Zc:preprocessor" +PM_CL="/EHsc /MDd /std:c++17 /permissive- /w14640 /Zc:threadSafeInit-" +PM_CL="/EHsc /MDd /std:c++20 /permissive- /w14640 /Zc:threadSafeInit-" +PM_CL="/EHsc /MDd /std:c++latest /permissive- /Zc:wchar_t- /w14640 /Zc:threadSafeInit-" +PM_CL="/EHsc /MDd /std:c++latest /permissive- /w14640 /Zc:threadSafeInit-" +PM_CL="/EHsc /MT /std:c++latest /permissive- /analyze:only /analyze:autolog- /w14640 /Zc:threadSafeInit-" +PM_CL="/EHsc /MT /std:c++latest /permissive- /w14640 /Zc:threadSafeInit-" +PM_CL="/EHsc /MTd /std:c++latest /permissive /w14640 /Zc:threadSafeInit-" +PM_CL="/EHsc /MTd /std:c++latest /permissive- /analyze:only /analyze:autolog- /w14640 /Zc:threadSafeInit-" +PM_CL="/EHsc /MTd /std:c++latest /permissive- /fp:strict /w14640 /Zc:threadSafeInit-" +PM_CL="/EHsc /MTd /std:c++latest /permissive- /w14640 /Zc:threadSafeInit-" +PM_CL="/Za /EHsc /MD /std:c++latest /permissive- /w14640 /Zc:threadSafeInit-" +PM_CL="/Za /EHsc /MDd /std:c++latest /permissive- /w14640 /Zc:threadSafeInit-" +PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing /EHsc /MD /std:c++14 /w14640 /Zc:threadSafeInit-" +PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing /EHsc /MD /std:c++17 /w14640 /Zc:threadSafeInit-" +PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing /EHsc /MT /std:c++latest /permissive- /w14640 /Zc:threadSafeInit-" +PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing /EHsc /MT /std:c++latest /permissive- /D_HAS_CXX23 /fp:strict /w14640 /Zc:threadSafeInit-" RUNALL_CROSSLIST PM_CL="-fsanitize=address /Zi /fno-sanitize-address-vcasan-lib /wd4611" PM_LINK="/debug" From a50a9e8f3cb4de0e92b6db6a8c9a99d6318dcabd Mon Sep 17 00:00:00 2001 From: Curtis Bezault Date: Wed, 21 Jul 2021 12:37:06 -0700 Subject: [PATCH 05/59] clang-format --- stl/inc/vector | 49 ++++++++++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/stl/inc/vector b/stl/inc/vector index a426d526da..44bc64e5cd 100644 --- a/stl/inc/vector +++ b/stl/inc/vector @@ -444,10 +444,13 @@ constexpr _Ty* _Unfancy_maybe_null(_Ty* _Ptr) noexcept { // do nothing for plain extern "C" void __sanitizer_annotate_contiguous_container(const void*, const void*, const void*, const void*); template -struct _Has_minimum_allocation_alignment : bool_constant= 8>, is_same>>> {}; +struct _Has_minimum_allocation_alignment + : bool_constant= 8>, + is_same>>> {}; template -struct _Has_minimum_allocation_alignment<_Vec, void_t> : bool_constant<_Vec::allocator_type::_Minimum_allocation_alignment >= 8> {}; +struct _Has_minimum_allocation_alignment<_Vec, void_t> + : bool_constant<_Vec::allocator_type::_Minimum_allocation_alignment >= 8> {}; template ::value> class _NODISCARD _Asan_annotater { @@ -458,13 +461,11 @@ private: using _Value_type = typename _Ty::value_type; public: - constexpr _Asan_annotater(const _Ty& _Target) - : _Target_vec(_Target), _Old_last(nullptr) {} + constexpr _Asan_annotater(const _Ty& _Target) : _Target_vec(_Target), _Old_last(nullptr) {} - _CONSTEXPR20 _Asan_annotater(const _Ty& _Target, _Size_type _New_size) - : _Target_vec(_Target) { + _CONSTEXPR20 _Asan_annotater(const _Ty& _Target, _Size_type _New_size) : _Target_vec(_Target) { #ifdef __cpp_lib_constexpr_dynamic_alloc - if (_STD is_constant_evaluated()) { + if (_STD is_constant_evaluated()) { return; } #endif // __cpp_lib_constexpr_dynamic_alloc @@ -477,7 +478,7 @@ public: _Size_type _Old_capacity = static_cast<_Size_type>(_Myend - _Myfirst); if (_New_size > _Old_capacity) { - _Old_last = nullptr; + _Old_last = nullptr; return; } @@ -545,13 +546,11 @@ private: } public: - constexpr _Asan_annotater(const _Ty& _Target) - : _Target_vec(_Target), _Old_last(nullptr), _Aligned_first(nullptr) {} + constexpr _Asan_annotater(const _Ty& _Target) : _Target_vec(_Target), _Old_last(nullptr), _Aligned_first(nullptr) {} - _CONSTEXPR20 _Asan_annotater(const _Ty& _Target, _Size_type _New_size) - : _Target_vec(_Target) { + _CONSTEXPR20 _Asan_annotater(const _Ty& _Target, _Size_type _New_size) : _Target_vec(_Target) { #ifdef __cpp_lib_constexpr_dynamic_alloc - if (_STD is_constant_evaluated()) { + if (_STD is_constant_evaluated()) { return; } #endif // __cpp_lib_constexpr_dynamic_alloc @@ -573,8 +572,8 @@ public: _Old_last = _Myend; } else { const void* _New_last = static_cast(_Myfirst + _New_size); - _New_last = _New_last > _Aligned_first ? _New_last : _Aligned_first; - _Old_last = _Mylast > _Aligned_first ? _Mylast : _Aligned_first; + _New_last = _New_last > _Aligned_first ? _New_last : _Aligned_first; + _Old_last = _Mylast > _Aligned_first ? _Mylast : _Aligned_first; if (_New_last > _Old_last) { __sanitizer_annotate_contiguous_container(_Aligned_first, _Myend, _Old_last, _New_last); @@ -589,22 +588,22 @@ public: } #endif // __cpp_lib_constexpr_dynamic_alloc - const auto& _My_data = _Target_vec._Mypair._Myval2; - const _Pointer& _Mylast = _My_data._Mylast; - const _Pointer& _Myend = _My_data._Myend; + const auto& _My_data = _Target_vec._Mypair._Myval2; + const _Pointer& _Mylast = _My_data._Mylast; + const _Pointer& _Myend = _My_data._Myend; if (!_Old_last) { _Aligned_first = _Get_aligned_start(); if (_Aligned_first) { const void* _New_last = static_cast(_Mylast); - _New_last = _New_last > _Aligned_first ? _New_last : _Aligned_first; + _New_last = _New_last > _Aligned_first ? _New_last : _Aligned_first; __sanitizer_annotate_contiguous_container(_Aligned_first, _Myend, _Myend, _New_last); } return; - } else if (_Aligned_first) { + } else if (_Aligned_first) { const void* _New_last = static_cast(_Mylast); - _New_last = _New_last > _Aligned_first ? _New_last : _Aligned_first; + _New_last = _New_last > _Aligned_first ? _New_last : _Aligned_first; if (_Old_last > _New_last) { __sanitizer_annotate_contiguous_container(_Aligned_first, _Myend, _Old_last, _New_last); @@ -615,8 +614,8 @@ public: private: const _Ty& _Target_vec; - const void *_Old_last; - const void *_Aligned_first; + const void* _Old_last; + const void* _Aligned_first; }; #define _ASAN_ANNOTATE(...) _Asan_annotater>> _Annotater(__VA_ARGS__) @@ -632,8 +631,8 @@ private: friend class _Vb_val; friend _Tidy_guard; - using _Alty = _Rebind_alloc_t<_Alloc, _Ty>; - using _Alty_traits = allocator_traits<_Alty>; + using _Alty = _Rebind_alloc_t<_Alloc, _Ty>; + using _Alty_traits = allocator_traits<_Alty>; public: static_assert(!_ENFORCE_MATCHING_ALLOCATORS || is_same_v<_Ty, typename _Alloc::value_type>, From ecca61cb171062dc19512263909fae267b84f95a Mon Sep 17 00:00:00 2001 From: Curtis Bezault Date: Wed, 21 Jul 2021 12:37:50 -0700 Subject: [PATCH 06/59] More clang-format --- .../GH_002030_asan_annotate_vector/test.cpp | 77 +++++++++---------- 1 file changed, 37 insertions(+), 40 deletions(-) diff --git a/tests/std/tests/GH_002030_asan_annotate_vector/test.cpp b/tests/std/tests/GH_002030_asan_annotate_vector/test.cpp index d8dfa22477..137e279250 100644 --- a/tests/std/tests/GH_002030_asan_annotate_vector/test.cpp +++ b/tests/std/tests/GH_002030_asan_annotate_vector/test.cpp @@ -11,12 +11,12 @@ using namespace std; -extern "C" int __sanitizer_verify_contiguous_container(const void *beg, const void *mid, const void *end); +extern "C" int __sanitizer_verify_contiguous_container(const void* beg, const void* mid, const void* end); template -bool verify_vector(vector &vec) { - size_t buffer_size = vec.capacity() * sizeof(T); - void* buffer = static_cast(vec.data()); +bool verify_vector(vector& vec) { + size_t buffer_size = vec.capacity() * sizeof(T); + void* buffer = static_cast(vec.data()); void* aligned_start = align(8, 1, buffer, buffer_size); if (!aligned_start) { @@ -24,40 +24,37 @@ bool verify_vector(vector &vec) { } void* mid = static_cast(vec.data() + vec.size()); - mid = mid > aligned_start ? mid : aligned_start; + mid = mid > aligned_start ? mid : aligned_start; return __sanitizer_verify_contiguous_container(aligned_start, mid, vec.data() + vec.capacity()) != 0; } template -struct custom_test_allocator -{ - using value_type = T; +struct custom_test_allocator { + using value_type = T; using propagate_on_container_move_assignment = Pocma; - using is_always_equal = Stateless; + using is_always_equal = Stateless; }; -template +template constexpr bool operator==(const custom_test_allocator& lhs, - const custom_test_allocator& rhs) noexcept -{ + const custom_test_allocator& rhs) noexcept { return Stateless::value || (&lhs == &rhs); } -template +template constexpr bool operator!=(const custom_test_allocator& lhs, - const custom_test_allocator& rhs) noexcept -{ + const custom_test_allocator& rhs) noexcept { return !(lhs == rhs); } template -struct aligned_allocator : public custom_test_allocator -{ +struct aligned_allocator : public custom_test_allocator { static constexpr size_t _Minimum_allocation_alignment = 8; aligned_allocator() = default; - template constexpr aligned_allocator (const aligned_allocator &) noexcept {} + template + constexpr aligned_allocator(const aligned_allocator&) noexcept {} T* allocate(size_t n) { return new T[n]; @@ -69,12 +66,12 @@ struct aligned_allocator : public custom_test_allocator }; template -struct explicit_allocator : public custom_test_allocator -{ +struct explicit_allocator : public custom_test_allocator { static constexpr size_t _Minimum_allocation_alignment = alignof(T); explicit_allocator() = default; - template constexpr explicit_allocator (const explicit_allocator &) noexcept {} + template + constexpr explicit_allocator(const explicit_allocator&) noexcept {} T* allocate(size_t n) { T* mem = new T[n + 1]; @@ -82,15 +79,15 @@ struct explicit_allocator : public custom_test_allocator } void deallocate(T* p, size_t) noexcept { - delete[] (p - 1); + delete[](p - 1); } }; template -struct implicit_allocator : public custom_test_allocator -{ +struct implicit_allocator : public custom_test_allocator { implicit_allocator() = default; - template constexpr implicit_allocator (const implicit_allocator &) noexcept {} + template + constexpr implicit_allocator(const implicit_allocator&) noexcept {} T* allocate(size_t n) { T* mem = new T[n + 1]; @@ -98,11 +95,11 @@ struct implicit_allocator : public custom_test_allocator } void deallocate(T* p, size_t) noexcept { - delete[] (p - 1); + delete[](p - 1); } }; -template +template void test_case_push_pop() { using T = typename Alloc::value_type; @@ -116,7 +113,7 @@ void test_case_push_pop() { assert(verify_vector(v)); } -template +template void test_case_reserve_shrink() { using T = typename Alloc::value_type; @@ -151,7 +148,7 @@ void test_case_reserve_shrink() { assert(verify_vector(v)); } -template +template void test_case_emplace_pop() { using T = typename Alloc::value_type; @@ -171,7 +168,7 @@ void test_case_emplace_pop() { assert(verify_vector(v)); } -template +template void test_case_move_assign() { using T = typename Alloc::value_type; @@ -188,7 +185,7 @@ void test_case_move_assign() { assert(verify_vector(v2)); } -template +template void test_case_copy_assign() { using T = typename Alloc::value_type; @@ -205,9 +202,9 @@ void test_case_copy_assign() { assert(verify_vector(v2)); } -template +template void test_case_constructors() { - using T = typename Alloc::value_type; + using T = typename Alloc::value_type; Alloc al = Alloc(); vector v1; @@ -235,7 +232,7 @@ void test_case_constructors() { assert(verify_vector(v10)); } -template +template void test_case_insert_n() { using T = typename Alloc::value_type; @@ -249,7 +246,7 @@ void test_case_insert_n() { assert(verify_vector(v)); } -template +template void test_case_insert_range() { using T = typename Alloc::value_type; @@ -272,7 +269,7 @@ void test_case_insert_range() { assert(verify_vector(v1)); } -template +template void test_case_assign() { using T = typename Alloc::value_type; @@ -310,7 +307,7 @@ void test_case_assign() { assert(verify_vector(v4)); } -template +template void test_case_resize() { using T = typename Alloc::value_type; @@ -321,7 +318,7 @@ void test_case_resize() { assert(verify_vector(v)); } -template +template void run_tests() { test_case_push_pop(); test_case_reserve_shrink(); @@ -335,7 +332,7 @@ void run_tests() { test_case_resize(); } -template class AllocT> +template class AllocT> void run_custom_allocator_matrix() { run_tests>(); run_tests>(); @@ -343,7 +340,7 @@ void run_custom_allocator_matrix() { run_tests>(); } -template +template void run_allocator_matrix() { run_tests>(); run_custom_allocator_matrix(); From 9b3bad02f7a2c84c449896582f0460425db3ac09 Mon Sep 17 00:00:00 2001 From: Curtis Bezault Date: Mon, 26 Jul 2021 19:29:18 -0400 Subject: [PATCH 07/59] Remove unused variable --- stl/inc/vector | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/stl/inc/vector b/stl/inc/vector index 44bc64e5cd..402bb3b8d1 100644 --- a/stl/inc/vector +++ b/stl/inc/vector @@ -1539,7 +1539,6 @@ public: is_nothrow_move_assignable_v) /* strengthened */ { const pointer _Whereptr = _Where._Ptr; auto& _My_data = _Mypair._Myval2; - pointer& _Myfirst = _My_data._Myfirst; pointer& _Mylast = _My_data._Mylast; #if _ITERATOR_DEBUG_LEVEL == 2 @@ -1548,7 +1547,7 @@ public: "vector erase iterator outside range"); #endif // _ITERATOR_DEBUG_LEVEL == 2 - _ASAN_ANNOTATE(*this, static_cast(_Mylast - _Myfirst) - 1); + _ASAN_ANNOTATE(*this, static_cast(_Mylast - _My_data._Myfirst) - 1); _Orphan_range(_Whereptr, _Mylast); _Move_unchecked(_Whereptr + 1, _Mylast, _Whereptr); From ae3ecd12e55d87772b60a9fbcf561722dae033bb Mon Sep 17 00:00:00 2001 From: Curtis Bezault Date: Tue, 27 Jul 2021 21:14:30 -0400 Subject: [PATCH 08/59] Add ASan to the CI image --- azure-devops/provision-image.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/azure-devops/provision-image.ps1 b/azure-devops/provision-image.ps1 index 6560622fb8..94ff8afa2a 100644 --- a/azure-devops/provision-image.ps1 +++ b/azure-devops/provision-image.ps1 @@ -123,6 +123,7 @@ if ([string]::IsNullOrEmpty($AdminUserPassword)) { } $Workloads = @( + 'Microsoft.VisualStudio.Component.VC.ASAN', 'Microsoft.VisualStudio.Component.VC.CLI.Support', 'Microsoft.VisualStudio.Component.VC.CMake.Project', 'Microsoft.VisualStudio.Component.VC.CoreIde', From 7a43662883c175d03f9a5f82e5b29ef9bec2e191 Mon Sep 17 00:00:00 2001 From: Curtis J Bezault Date: Tue, 27 Jul 2021 21:20:44 -0400 Subject: [PATCH 09/59] Update stl/inc/vector Co-authored-by: Michael Schellenberger Costa --- stl/inc/vector | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/vector b/stl/inc/vector index 402bb3b8d1..dfcf747bf0 100644 --- a/stl/inc/vector +++ b/stl/inc/vector @@ -464,7 +464,7 @@ public: constexpr _Asan_annotater(const _Ty& _Target) : _Target_vec(_Target), _Old_last(nullptr) {} _CONSTEXPR20 _Asan_annotater(const _Ty& _Target, _Size_type _New_size) : _Target_vec(_Target) { -#ifdef __cpp_lib_constexpr_dynamic_alloc +#if _HAS_CXX20 if (_STD is_constant_evaluated()) { return; } From 295cb8200ef5cee7148988759665d87368a08e63 Mon Sep 17 00:00:00 2001 From: Curtis J Bezault Date: Tue, 27 Jul 2021 21:21:24 -0400 Subject: [PATCH 10/59] Apply suggestions from code review Co-authored-by: Michael Schellenberger Costa --- stl/inc/vector | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/vector b/stl/inc/vector index dfcf747bf0..06163ad0d4 100644 --- a/stl/inc/vector +++ b/stl/inc/vector @@ -468,7 +468,7 @@ public: if (_STD is_constant_evaluated()) { return; } -#endif // __cpp_lib_constexpr_dynamic_alloc +#endif // _HAS_CXX20 const auto& _My_data = _Target_vec._Mypair._Myval2; const _Pointer& _Myfirst = _My_data._Myfirst; From 4bc1d3ee252e6fb7b8d3c4f0015e230110950435 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 27 Jul 2021 18:16:51 -0700 Subject: [PATCH 11/59] Update VMSS to add ASAN. --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 18cb975700..8b8848b5c2 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -8,7 +8,7 @@ variables: buildOutputLocation: 'D:\build' vcpkgLocation: '$(Build.SourcesDirectory)/vcpkg' -pool: 'StlBuild-2021-07-18' +pool: 'StlBuild-2021-07-27' stages: - stage: Code_Format From 201e707bc75bd121c0faf9368feb151af4de13de Mon Sep 17 00:00:00 2001 From: Curtis Bezault Date: Tue, 27 Jul 2021 21:57:24 -0400 Subject: [PATCH 12/59] Resolve most of Misco's comments --- stl/inc/vector | 50 +++++++++++++++++++++----------------------------- 1 file changed, 21 insertions(+), 29 deletions(-) diff --git a/stl/inc/vector b/stl/inc/vector index 06163ad0d4..d849dc8928 100644 --- a/stl/inc/vector +++ b/stl/inc/vector @@ -444,15 +444,14 @@ constexpr _Ty* _Unfancy_maybe_null(_Ty* _Ptr) noexcept { // do nothing for plain extern "C" void __sanitizer_annotate_contiguous_container(const void*, const void*, const void*, const void*); template -struct _Has_minimum_allocation_alignment - : bool_constant= 8>, - is_same>>> {}; +bool _Has_minimum_allocation_alignment = disjunction_v= 8>, + is_same>>::value; template -struct _Has_minimum_allocation_alignment<_Vec, void_t> - : bool_constant<_Vec::allocator_type::_Minimum_allocation_alignment >= 8> {}; +bool _Has_minimum_allocation_alignment<_Vec, void_t> = + _Vec::allocator_type::_Minimum_allocation_alignment >= 8; -template ::value> +template > class _NODISCARD _Asan_annotater { private: using _Alloc = typename _Ty::allocator_type; @@ -463,7 +462,8 @@ private: public: constexpr _Asan_annotater(const _Ty& _Target) : _Target_vec(_Target), _Old_last(nullptr) {} - _CONSTEXPR20 _Asan_annotater(const _Ty& _Target, _Size_type _New_size) : _Target_vec(_Target) { + _CONSTEXPR20 _Asan_annotater(const _Ty& _Target, const _Size_type _New_size) + : _Target_vec(_Target), _Old_last(nullptr) { #if _HAS_CXX20 if (_STD is_constant_evaluated()) { return; @@ -475,27 +475,23 @@ public: const _Pointer& _Mylast = _My_data._Mylast; const _Pointer& _Myend = _My_data._Myend; - _Size_type _Old_capacity = static_cast<_Size_type>(_Myend - _Myfirst); - - if (_New_size > _Old_capacity) { - _Old_last = nullptr; + if (_New_size > static_cast<_Size_type>(_Myend - _Myfirst)) { return; } - _Old_last = _Mylast; - _Size_type _Old_size = static_cast<_Size_type>(_Mylast - _Myfirst); + _Old_last = static_cast(_Mylast); - if (_New_size > _Old_size) { + if (_New_size > static_cast<_Size_type>(_Mylast - _Myfirst)) { __sanitizer_annotate_contiguous_container(_Myfirst, _Myend, _Old_last, _Myfirst + _New_size); } } _CONSTEXPR20 ~_Asan_annotater() { -#ifdef __cpp_lib_constexpr_dynamic_alloc +#ifdef _HAS_CXX20 if (_STD is_constant_evaluated()) { return; } -#endif // __cpp_lib_constexpr_dynamic_alloc +#endif // _HAS_CXX20 const auto& _My_data = _Target_vec._Mypair._Myval2; const _Pointer& _Myfirst = _My_data._Myfirst; @@ -504,15 +500,14 @@ public: if (!_Old_last) { __sanitizer_annotate_contiguous_container(_Myfirst, _Myend, _Myend, _Mylast); - } else if (_Mylast < _Old_last) { + } else if (static_cast(_Mylast) < _Old_last) { __sanitizer_annotate_contiguous_container(_Myfirst, _Myend, _Old_last, _Mylast); } } private: const _Ty& _Target_vec; - - _Pointer _Old_last; + const void* _Old_last; }; template @@ -548,28 +543,26 @@ private: public: constexpr _Asan_annotater(const _Ty& _Target) : _Target_vec(_Target), _Old_last(nullptr), _Aligned_first(nullptr) {} - _CONSTEXPR20 _Asan_annotater(const _Ty& _Target, _Size_type _New_size) : _Target_vec(_Target) { -#ifdef __cpp_lib_constexpr_dynamic_alloc + _CONSTEXPR20 _Asan_annotater(const _Ty& _Target, _Size_type _New_size) : _Target_vec(_Target), _Old_last(nullptr) { +#ifdef _HAS_CXX20 if (_STD is_constant_evaluated()) { return; } -#endif // __cpp_lib_constexpr_dynamic_alloc +#endif // _HAS_CXX20 const auto& _My_data = _Target_vec._Mypair._Myval2; const _Pointer& _Myfirst = _My_data._Myfirst; const _Pointer& _Mylast = _My_data._Mylast; const _Pointer& _Myend = _My_data._Myend; - const _Size_type _Old_capacity = static_cast<_Size_type>(_Myend - _Myfirst); - - if (_New_size > _Old_capacity) { - _Old_last = nullptr; + if (_New_size > static_cast<_Size_type>(_Myend - _Myfirst)) { return; } _Aligned_first = _Get_aligned_start(); if (!_Aligned_first) { _Old_last = _Myend; + return; } else { const void* _New_last = static_cast(_Myfirst + _New_size); _New_last = _New_last > _Aligned_first ? _New_last : _Aligned_first; @@ -582,11 +575,11 @@ public: } _CONSTEXPR20 ~_Asan_annotater() { -#ifdef __cpp_lib_constexpr_dynamic_alloc +#ifdef _HAS_CXX20 if (_STD is_constant_evaluated()) { return; } -#endif // __cpp_lib_constexpr_dynamic_alloc +#endif // _HAS_CXX20 const auto& _My_data = _Target_vec._Mypair._Myval2; const _Pointer& _Mylast = _My_data._Mylast; @@ -613,7 +606,6 @@ public: private: const _Ty& _Target_vec; - const void* _Old_last; const void* _Aligned_first; }; From 32ece64a9b3b835ea6c5ee7365f5b5bf0c01e556 Mon Sep 17 00:00:00 2001 From: Curtis Bezault Date: Tue, 27 Jul 2021 22:19:14 -0400 Subject: [PATCH 13/59] Address Alex's comments --- stl/inc/vector | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/stl/inc/vector b/stl/inc/vector index d849dc8928..13fdbea003 100644 --- a/stl/inc/vector +++ b/stl/inc/vector @@ -444,12 +444,13 @@ constexpr _Ty* _Unfancy_maybe_null(_Ty* _Ptr) noexcept { // do nothing for plain extern "C" void __sanitizer_annotate_contiguous_container(const void*, const void*, const void*, const void*); template -bool _Has_minimum_allocation_alignment = disjunction_v= 8>, - is_same>>::value; +constexpr bool _Has_minimum_allocation_alignment = disjunction_v= 8>, + is_same>>; template -bool _Has_minimum_allocation_alignment<_Vec, void_t> = - _Vec::allocator_type::_Minimum_allocation_alignment >= 8; +constexpr bool + _Has_minimum_allocation_alignment<_Vec, void_t> = + _Vec::allocator_type::_Minimum_allocation_alignment >= 8; template > class _NODISCARD _Asan_annotater { @@ -460,7 +461,7 @@ private: using _Value_type = typename _Ty::value_type; public: - constexpr _Asan_annotater(const _Ty& _Target) : _Target_vec(_Target), _Old_last(nullptr) {} + explicit constexpr _Asan_annotater(const _Ty& _Target) noexcept : _Target_vec(_Target), _Old_last(nullptr) {} _CONSTEXPR20 _Asan_annotater(const _Ty& _Target, const _Size_type _New_size) : _Target_vec(_Target), _Old_last(nullptr) { @@ -487,7 +488,7 @@ public: } _CONSTEXPR20 ~_Asan_annotater() { -#ifdef _HAS_CXX20 +#if _HAS_CXX20 if (_STD is_constant_evaluated()) { return; } @@ -541,10 +542,11 @@ private: } public: - constexpr _Asan_annotater(const _Ty& _Target) : _Target_vec(_Target), _Old_last(nullptr), _Aligned_first(nullptr) {} + explicit constexpr _Asan_annotater(const _Ty& _Target) noexcept + : _Target_vec(_Target), _Old_last(nullptr), _Aligned_first(nullptr) {} _CONSTEXPR20 _Asan_annotater(const _Ty& _Target, _Size_type _New_size) : _Target_vec(_Target), _Old_last(nullptr) { -#ifdef _HAS_CXX20 +#if _HAS_CXX20 if (_STD is_constant_evaluated()) { return; } @@ -575,7 +577,7 @@ public: } _CONSTEXPR20 ~_Asan_annotater() { -#ifdef _HAS_CXX20 +#if _HAS_CXX20 if (_STD is_constant_evaluated()) { return; } From 0e051862598546a4d60be2e06fbec7752219e3c4 Mon Sep 17 00:00:00 2001 From: Curtis Bezault Date: Tue, 27 Jul 2021 22:24:36 -0400 Subject: [PATCH 14/59] Require x86 or x64 --- tests/std/tests/GH_002030_asan_annotate_vector/test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/std/tests/GH_002030_asan_annotate_vector/test.cpp b/tests/std/tests/GH_002030_asan_annotate_vector/test.cpp index 137e279250..aca3d420b1 100644 --- a/tests/std/tests/GH_002030_asan_annotate_vector/test.cpp +++ b/tests/std/tests/GH_002030_asan_annotate_vector/test.cpp @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// REQUIRES: asan +// REQUIRES: asan, x64 || x86 #include #include From 4001372a953617b01573b44f9e34f2bd18aa6715 Mon Sep 17 00:00:00 2001 From: Curtis J Bezault Date: Thu, 29 Jul 2021 16:43:05 -0400 Subject: [PATCH 15/59] Apply suggestions from code review Co-authored-by: Casey Carter --- stl/inc/vector | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/stl/inc/vector b/stl/inc/vector index 13fdbea003..1561d0a02b 100644 --- a/stl/inc/vector +++ b/stl/inc/vector @@ -441,14 +441,14 @@ constexpr _Ty* _Unfancy_maybe_null(_Ty* _Ptr) noexcept { // do nothing for plain } #ifdef __SANITIZE_ADDRESS__ -extern "C" void __sanitizer_annotate_contiguous_container(const void*, const void*, const void*, const void*); +extern "C" void __sanitizer_annotate_contiguous_container(const void* _First, const void* _End, const void* _Old_last, const void* _New_last) noexcept; template -constexpr bool _Has_minimum_allocation_alignment = disjunction_v= 8>, +_INLINE_VAR constexpr bool _Has_minimum_allocation_alignment = disjunction_v= 8>, is_same>>; template -constexpr bool +_INLINE_VAR constexpr bool _Has_minimum_allocation_alignment<_Vec, void_t> = _Vec::allocator_type::_Minimum_allocation_alignment >= 8; @@ -477,13 +477,16 @@ public: const _Pointer& _Myend = _My_data._Myend; if (_New_size > static_cast<_Size_type>(_Myend - _Myfirst)) { + // New size is larger than current capacity; assume we're allocating a new block. return; } - _Old_last = static_cast(_Mylast); + // New size is less than current capacity; assume we're reusing the same block. + _Old_last = _Unfancy_maybe_null(_Mylast); if (_New_size > static_cast<_Size_type>(_Mylast - _Myfirst)) { - __sanitizer_annotate_contiguous_container(_Myfirst, _Myend, _Old_last, _Myfirst + _New_size); + const auto _Raw_first = _Unfancy(_Myfirst); + __sanitizer_annotate_contiguous_container(_Raw_first, _Unfancy(_Myend), _Old_last, _Raw_first + _New_size); } } @@ -563,7 +566,7 @@ public: _Aligned_first = _Get_aligned_start(); if (!_Aligned_first) { - _Old_last = _Myend; + _Old_last = _Unfancy(_Myend); return; } else { const void* _New_last = static_cast(_Myfirst + _New_size); @@ -643,7 +646,7 @@ public: private: #ifdef __SANITIZE_ADDRESS__ - friend _Asan_annotater>; + friend _Asan_annotater; #endif // __SANITIZE_ADDRESS__ using _Scary_val = _Vector_val, _Simple_types<_Ty>, @@ -1039,7 +1042,6 @@ private: auto& _My_data = _Mypair._Myval2; pointer& _Mylast = _My_data._Mylast; - const pointer _Oldfirst = _My_data._Myfirst; const pointer _Oldlast = _Mylast; const auto _Unused_capacity = static_cast(_My_data._Myend - _Oldlast); From 84bcc99f415e945edcd3f37b78cba57c8cfa9bff Mon Sep 17 00:00:00 2001 From: Curtis J Bezault Date: Fri, 30 Jul 2021 13:30:03 -0400 Subject: [PATCH 16/59] Update stl/inc/vector Co-authored-by: Casey Carter --- stl/inc/vector | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/stl/inc/vector b/stl/inc/vector index 1561d0a02b..b21e0f5237 100644 --- a/stl/inc/vector +++ b/stl/inc/vector @@ -503,9 +503,14 @@ public: const _Pointer& _Myend = _My_data._Myend; if (!_Old_last) { - __sanitizer_annotate_contiguous_container(_Myfirst, _Myend, _Myend, _Mylast); - } else if (static_cast(_Mylast) < _Old_last) { - __sanitizer_annotate_contiguous_container(_Myfirst, _Myend, _Old_last, _Mylast); + const _Raw_end = _Unfancy(_Myend); + __sanitizer_annotate_contiguous_container(_Myfirst, _Raw_end, _Raw_end, _Mylast); + return; + } + + const _Raw_last = _Unfancy(_Mylast); + if (static_cast(_Raw_last) < _Old_last) { + __sanitizer_annotate_contiguous_container(_Unfancy(_Myfirst), _Unfancy(_Myend), _Old_last, _Raw_last); } } From a1a004c28803985622145a3f802bbb055a2909c2 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Wed, 4 Aug 2021 15:41:24 -0700 Subject: [PATCH 17/59] clang-format. --- stl/inc/vector | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/stl/inc/vector b/stl/inc/vector index b21e0f5237..cb473ae9af 100644 --- a/stl/inc/vector +++ b/stl/inc/vector @@ -441,11 +441,13 @@ constexpr _Ty* _Unfancy_maybe_null(_Ty* _Ptr) noexcept { // do nothing for plain } #ifdef __SANITIZE_ADDRESS__ -extern "C" void __sanitizer_annotate_contiguous_container(const void* _First, const void* _End, const void* _Old_last, const void* _New_last) noexcept; +extern "C" void __sanitizer_annotate_contiguous_container( + const void* _First, const void* _End, const void* _Old_last, const void* _New_last) noexcept; template -_INLINE_VAR constexpr bool _Has_minimum_allocation_alignment = disjunction_v= 8>, - is_same>>; +_INLINE_VAR constexpr bool _Has_minimum_allocation_alignment = + disjunction_v= 8>, + is_same>>; template _INLINE_VAR constexpr bool From 632566b072cdc6f4f18d5ab79cdde30b6ce74709 Mon Sep 17 00:00:00 2001 From: Curtis Bezault Date: Thu, 12 Aug 2021 18:10:45 -0400 Subject: [PATCH 18/59] Fixup tests and annotations --- stl/inc/vector | 168 ++++++++++-------- .../GH_002030_asan_annotate_vector/test.cpp | 96 +++++++--- 2 files changed, 172 insertions(+), 92 deletions(-) diff --git a/stl/inc/vector b/stl/inc/vector index 91fcba589e..2fc4cfcb19 100644 --- a/stl/inc/vector +++ b/stl/inc/vector @@ -457,32 +457,40 @@ private: using _Value_type = typename _Ty::value_type; public: - explicit constexpr _Asan_annotater(const _Ty& _Target) noexcept : _Target_vec(_Target), _Old_last(nullptr) {} + _Asan_annotater(const _Asan_annotater&) = delete; + _Asan_annotater& operator=(const _Asan_annotater&) = delete; + + explicit constexpr _Asan_annotater(const _Ty& _Target) noexcept : _Target_vec(_Target), _Asan_last(nullptr) {} _CONSTEXPR20 _Asan_annotater(const _Ty& _Target, const _Size_type _New_size) - : _Target_vec(_Target), _Old_last(nullptr) { + : _Target_vec(_Target), _Asan_last(nullptr) { #if _HAS_CXX20 if (_STD is_constant_evaluated()) { return; } #endif // _HAS_CXX20 - const auto& _My_data = _Target_vec._Mypair._Myval2; - const _Pointer& _Myfirst = _My_data._Myfirst; - const _Pointer& _Mylast = _My_data._Mylast; - const _Pointer& _Myend = _My_data._Myend; + const auto& _My_data = _Target_vec._Mypair._Myval2; + const _Value_type* _Raw_first = _Unfancy_maybe_null(_My_data._Myfirst); + const _Value_type* _Raw_last = _Unfancy_maybe_null(_My_data._Mylast); + const _Value_type* _Raw_end = _Unfancy_maybe_null(_My_data._Myend); - if (_New_size > static_cast<_Size_type>(_Myend - _Myfirst)) { - // New size is larger than current capacity; assume we're allocating a new block. + if (_New_size > static_cast<_Size_type>(_Raw_end - _Raw_first)) { + if (_Raw_end != _Raw_first) { + // New size is larger than current capacity; assume we're allocating a new block. + __sanitizer_annotate_contiguous_container(_Raw_first, _Raw_end, _Raw_last, _Raw_end); + } return; } // New size is less than current capacity; assume we're reusing the same block. - _Old_last = _Unfancy_maybe_null(_Mylast); + const _Value_type* _New_last = _Raw_first + _New_size; + _Asan_last = _Raw_last; - if (_New_size > static_cast<_Size_type>(_Mylast - _Myfirst)) { - const auto _Raw_first = _Unfancy(_Myfirst); - __sanitizer_annotate_contiguous_container(_Raw_first, _Unfancy(_Myend), _Old_last, _Raw_first + _New_size); + if (_Asan_last < _New_last) { + // If the new size is larger than the current size extend the legal range of the container. + __sanitizer_annotate_contiguous_container(_Raw_first, _Raw_end, _Asan_last, _New_last); + _Asan_last = _New_last; } } @@ -493,90 +501,102 @@ public: } #endif // _HAS_CXX20 - const auto& _My_data = _Target_vec._Mypair._Myval2; - const _Pointer& _Myfirst = _My_data._Myfirst; - const _Pointer& _Mylast = _My_data._Mylast; - const _Pointer& _Myend = _My_data._Myend; + const auto& _My_data = _Target_vec._Mypair._Myval2; + const _Value_type* _Raw_first = _Unfancy(_My_data._Myfirst); + const _Value_type* _Raw_last = _Unfancy(_My_data._Mylast); + const _Value_type* _Raw_end = _Unfancy(_My_data._Myend); - if (!_Old_last) { - const _Raw_end = _Unfancy(_Myend); - __sanitizer_annotate_contiguous_container(_Myfirst, _Raw_end, _Raw_end, _Mylast); + if (!_Asan_last) { + // New size was larger than current capacity; pessimisticly re-annotate the whole illegal range of the container. + __sanitizer_annotate_contiguous_container(_Raw_first, _Raw_end, _Raw_end, _Raw_last); return; } - const _Raw_last = _Unfancy(_Mylast); - if (static_cast(_Raw_last) < _Old_last) { - __sanitizer_annotate_contiguous_container(_Unfancy(_Myfirst), _Unfancy(_Myend), _Old_last, _Raw_last); + if (_Raw_last != _Asan_last) { + __sanitizer_annotate_contiguous_container(_Raw_first, _Raw_end, _Asan_last, _Raw_last); } } private: const _Ty& _Target_vec; - const void* _Old_last; + const _Value_type* _Asan_last; }; template class _NODISCARD _Asan_annotater<_Ty, false> { private: using _Alloc = typename _Ty::allocator_type; - using _Pointer = typename _Ty::pointer; using _Size_type = typename _Ty::size_type; using _Value_type = typename _Ty::value_type; const void* _Get_aligned_start() { - const auto& _My_data = _Target_vec._Mypair._Myval2; - const _Pointer& _Myfirst = _My_data._Myfirst; - const _Pointer& _Myend = _My_data._Myend; - const _Size_type _Capacity = static_cast<_Size_type>(_Myend - _Myfirst); - - if (_Capacity * sizeof(_Value_type) < 8) { - uintptr_t _Alignment_offset = reinterpret_cast(_Myfirst) & 0x7; - if (_Alignment_offset) { - _Alignment_offset = 8 - _Alignment_offset; - } + const auto& _My_data = _Target_vec._Mypair._Myval2; + const _Value_type* _Raw_first = _Unfancy_maybe_null(_My_data._Myfirst); + const _Value_type* _Raw_end = _Unfancy_maybe_null(_My_data._Myend); + const _Size_type _Capacity = static_cast<_Size_type>(_Raw_end - _Raw_first); - if (_Capacity * sizeof(_Value_type) > _Alignment_offset) { - return reinterpret_cast(_Myfirst) + _Alignment_offset; - } else { - return nullptr; - } - } else { - return reinterpret_cast((reinterpret_cast(_Myfirst) + 7) & ~7); + if (_Capacity * sizeof(_Value_type) >= 8) { + // We are guaranteed to have sufficient space to find a legal aligned address. + return reinterpret_cast((reinterpret_cast(_Raw_first) + 7) & ~7); + } + + uintptr_t _Alignment_offset = reinterpret_cast(_Raw_first) & 7; + if (_Alignment_offset) { + _Alignment_offset = 8 - _Alignment_offset; } + + if (_Capacity * sizeof(_Value_type) > _Alignment_offset) { + return reinterpret_cast(_Raw_first) + _Alignment_offset; + } + + return nullptr; } public: + _Asan_annotater(const _Asan_annotater&) = delete; + _Asan_annotater& operator=(const _Asan_annotater&) = delete; + explicit constexpr _Asan_annotater(const _Ty& _Target) noexcept - : _Target_vec(_Target), _Old_last(nullptr), _Aligned_first(nullptr) {} + : _Target_vec(_Target), _Asan_last(nullptr), _Aligned_first(nullptr) {} - _CONSTEXPR20 _Asan_annotater(const _Ty& _Target, _Size_type _New_size) : _Target_vec(_Target), _Old_last(nullptr) { + _CONSTEXPR20 _Asan_annotater(const _Ty& _Target, _Size_type _New_size) : _Target_vec(_Target), _Asan_last(nullptr) { #if _HAS_CXX20 if (_STD is_constant_evaluated()) { return; } #endif // _HAS_CXX20 - const auto& _My_data = _Target_vec._Mypair._Myval2; - const _Pointer& _Myfirst = _My_data._Myfirst; - const _Pointer& _Mylast = _My_data._Mylast; - const _Pointer& _Myend = _My_data._Myend; - - if (_New_size > static_cast<_Size_type>(_Myend - _Myfirst)) { - return; - } + const auto& _My_data = _Target_vec._Mypair._Myval2; + const _Value_type* _Raw_first = _Unfancy_maybe_null(_My_data._Myfirst); + const _Value_type* _Raw_last = _Unfancy_maybe_null(_My_data._Mylast); + const _Value_type* _Raw_end = _Unfancy_maybe_null(_My_data._Myend); _Aligned_first = _Get_aligned_start(); if (!_Aligned_first) { - _Old_last = _Unfancy(_Myend); + // There is no aligned address within the underlying buffer. The whole region is legal. + _Asan_last = _Raw_end; return; - } else { - const void* _New_last = static_cast(_Myfirst + _New_size); - _New_last = _New_last > _Aligned_first ? _New_last : _Aligned_first; - _Old_last = _Mylast > _Aligned_first ? _Mylast : _Aligned_first; + } + + // New size is less than current capacity; assume we're reusing the same block. + const void* _New_last = _Raw_first + _New_size; + _New_last = _New_last < _Aligned_first ? _Aligned_first : _New_last; + _Asan_last = _Raw_last < _Aligned_first ? _Aligned_first : _Raw_last; - if (_New_last > _Old_last) { - __sanitizer_annotate_contiguous_container(_Aligned_first, _Myend, _Old_last, _New_last); + if (_New_size > static_cast<_Size_type>(_Raw_end - _Raw_first)) { + // New size is larger than current capacity; assume we're allocating a new block. + if (_Raw_end != _Raw_first) { + __sanitizer_annotate_contiguous_container(_Aligned_first, _Raw_end, _Asan_last, _Raw_end); } + + _Asan_last = nullptr; + return; + } + + if (_Asan_last < _New_last) { + // If the new size is larger than the current size extend the legal range of the container. + __sanitizer_annotate_contiguous_container(_Aligned_first, _Raw_end, _Asan_last, _New_last); + _Asan_last = _New_last; } } @@ -587,32 +607,31 @@ public: } #endif // _HAS_CXX20 - const auto& _My_data = _Target_vec._Mypair._Myval2; - const _Pointer& _Mylast = _My_data._Mylast; - const _Pointer& _Myend = _My_data._Myend; + const auto& _My_data = _Target_vec._Mypair._Myval2; + const _Value_type* _Raw_last = _Unfancy(_My_data._Mylast); + const _Value_type* _Raw_end = _Unfancy(_My_data._Myend); - if (!_Old_last) { + if (!_Asan_last) { + // New size was larger than current capacity; pessimisticly re-annotate the whole illegal range of the container. _Aligned_first = _Get_aligned_start(); if (_Aligned_first) { - const void* _New_last = static_cast(_Mylast); - _New_last = _New_last > _Aligned_first ? _New_last : _Aligned_first; + _Asan_last = _Raw_last > _Aligned_first ? _Raw_last : _Aligned_first; - __sanitizer_annotate_contiguous_container(_Aligned_first, _Myend, _Myend, _New_last); + __sanitizer_annotate_contiguous_container(_Aligned_first, _Raw_end, _Raw_end, _Asan_last); } return; } else if (_Aligned_first) { - const void* _New_last = static_cast(_Mylast); - _New_last = _New_last > _Aligned_first ? _New_last : _Aligned_first; + const void *_New_last = _Raw_last < _Aligned_first ? _Aligned_first : _Raw_last; - if (_Old_last > _New_last) { - __sanitizer_annotate_contiguous_container(_Aligned_first, _Myend, _Old_last, _New_last); + if (_New_last != _Asan_last) { + __sanitizer_annotate_contiguous_container(_Aligned_first, _Raw_end, _Asan_last, _New_last); } } } private: const _Ty& _Target_vec; - const void* _Old_last; + const void* _Asan_last; const void* _Aligned_first; }; @@ -1021,6 +1040,10 @@ private: const auto _Whereoff = static_cast(_Where._Ptr - _Myfirst); const auto _Oldsize = static_cast(_Mylast - _Myfirst); + // Pessimistically assume that we will fill the vector's capacity. + // This causes the entire underlying buffer to initially be marked as legal. + _ASAN_ANNOTATE(*this, static_cast(_My_data._Myend - _Myfirst) + 1); + // For one-at-back, provide strong guarantee. // Otherwise, provide basic guarantee (despite N4659 26.3.11.5 [vector.modifiers]/1). @@ -1208,6 +1231,10 @@ private: pointer& _Myfirst = _My_data._Myfirst; pointer& _Mylast = _My_data._Mylast; + // Pessimistically assume that we will fill the vector's capacity. + // This causes the entire underlying buffer to initially be marked as legal. + _ASAN_ANNOTATE(*this, static_cast(_My_data._Myend - _Myfirst) + 1); + _My_data._Orphan_all(); pointer _Next = _Myfirst; @@ -2171,6 +2198,7 @@ constexpr typename vector<_Ty, _Alloc>::size_type erase_if(vector<_Ty, _Alloc>& } #endif // _HAS_CXX20 +// CLASS TEMPLATE vector AND FRIENDS template struct _Wrap_alloc { // TRANSITION, ABI compat, preserves symbol names of vector::iterator using _Alloc = _Alloc0; diff --git a/tests/std/tests/GH_002030_asan_annotate_vector/test.cpp b/tests/std/tests/GH_002030_asan_annotate_vector/test.cpp index aca3d420b1..025e198094 100644 --- a/tests/std/tests/GH_002030_asan_annotate_vector/test.cpp +++ b/tests/std/tests/GH_002030_asan_annotate_vector/test.cpp @@ -4,7 +4,6 @@ // REQUIRES: asan, x64 || x86 #include -#include #include #include #include @@ -13,17 +12,69 @@ using namespace std; extern "C" int __sanitizer_verify_contiguous_container(const void* beg, const void* mid, const void* end); +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 = T*; + using reference = T&; + + 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) { size_t buffer_size = vec.capacity() * sizeof(T); - void* buffer = static_cast(vec.data()); + void* buffer = vec.data(); void* aligned_start = align(8, 1, buffer, buffer_size); if (!aligned_start) { return true; } - void* mid = static_cast(vec.data() + vec.size()); + void* mid = vec.data() + vec.size(); mid = mid > aligned_start ? mid : aligned_start; return __sanitizer_verify_contiguous_container(aligned_start, mid, vec.data() + vec.capacity()) != 0; @@ -36,16 +87,16 @@ struct custom_test_allocator { using is_always_equal = Stateless; }; -template -constexpr bool operator==(const custom_test_allocator& lhs, - const custom_test_allocator& rhs) noexcept { - return Stateless::value || (&lhs == &rhs); +template +constexpr bool operator==(const custom_test_allocator&, + const custom_test_allocator&) noexcept { + return Stateless::value; } -template -constexpr bool operator!=(const custom_test_allocator& lhs, - const custom_test_allocator& rhs) noexcept { - return !(lhs == rhs); +template +constexpr bool operator!=(const custom_test_allocator&, + const custom_test_allocator&) noexcept { + return !Stateless::value; } template @@ -124,7 +175,7 @@ void test_case_reserve_shrink() { assert(verify_vector(v)); for (int i = 0; i < Size; i += Stride) { - for (int j = i; j < Stride && j + i < Size; ++j) { + for (int j = 0; j < Stride && j + i < Size; ++j) { v.push_back(T()); } @@ -135,7 +186,7 @@ void test_case_reserve_shrink() { assert(verify_vector(v)); for (int i = 0; i < Size; i += Stride) { - for (int j = i; j < Stride && j + i < Size; ++j) { + for (int j = 0; j < Stride && j + i < Size; ++j) { v.pop_back(); } @@ -144,6 +195,7 @@ void test_case_reserve_shrink() { } v.pop_back(); + assert(verify_vector(v)); v.shrink_to_fit(); assert(verify_vector(v)); } @@ -252,7 +304,7 @@ void test_case_insert_range() { vector v1(1); vector v2(N); - list l(N); + input_iterator_tester t; v1.insert(v1.begin(), v2.begin(), v2.end()); assert(verify_vector(v1)); @@ -261,11 +313,11 @@ void test_case_insert_range() { v1.insert(v1.begin() + N, v2.begin(), v2.end()); assert(verify_vector(v1)); - v1.insert(v1.begin(), l.begin(), l.end()); + v1.insert(v1.begin(), t.begin(), t.end()); assert(verify_vector(v1)); - v1.insert(v1.end(), l.begin(), l.end()); + v1.insert(v1.end(), t.begin(), t.end()); assert(verify_vector(v1)); - v1.insert(v1.begin() + N, l.begin(), l.end()); + v1.insert(v1.begin() + N, t.begin(), t.end()); assert(verify_vector(v1)); } @@ -276,8 +328,8 @@ void test_case_assign() { vector v1(1); vector v2(N + 1); vector v3(N + 2); - list l1(N + 2); - list l2(N + 3); + input_iterator_tester t1; + input_iterator_tester t2; v1.assign(N, T()); assert(verify_vector(v1)); @@ -285,11 +337,11 @@ void test_case_assign() { assert(verify_vector(v1)); v1.assign(v3.begin(), v3.end()); assert(verify_vector(v1)); - v1.assign(l1.begin(), l1.end()); + v1.assign(t1.begin(), t1.end()); assert(verify_vector(v1)); - v1.assign(l2.begin(), l2.end()); + v1.assign(t2.begin(), t2.end()); assert(verify_vector(v1)); - v1.assign(l1.begin(), l1.end()); + v1.assign(t1.begin(), t1.end()); assert(verify_vector(v1)); v1.assign(v3.begin(), v3.end()); assert(verify_vector(v1)); From 3a09cd9029dbcef6b4be44514db7adb5feb50302 Mon Sep 17 00:00:00 2001 From: Curtis Bezault Date: Thu, 12 Aug 2021 18:28:28 -0400 Subject: [PATCH 19/59] Remove unused types --- stl/inc/vector | 3 --- 1 file changed, 3 deletions(-) diff --git a/stl/inc/vector b/stl/inc/vector index 2fc4cfcb19..3e26698159 100644 --- a/stl/inc/vector +++ b/stl/inc/vector @@ -451,8 +451,6 @@ _INLINE_VAR constexpr bool template > class _NODISCARD _Asan_annotater { private: - using _Alloc = typename _Ty::allocator_type; - using _Pointer = typename _Ty::pointer; using _Size_type = typename _Ty::size_type; using _Value_type = typename _Ty::value_type; @@ -525,7 +523,6 @@ private: template class _NODISCARD _Asan_annotater<_Ty, false> { private: - using _Alloc = typename _Ty::allocator_type; using _Size_type = typename _Ty::size_type; using _Value_type = typename _Ty::value_type; From 138752a3ee514d19d0a85b3e7028fb5bd398f41d Mon Sep 17 00:00:00 2001 From: Curtis Bezault Date: Tue, 17 Aug 2021 13:56:15 -0400 Subject: [PATCH 20/59] Resolve PR comments --- stl/inc/vector | 37 +- .../GH_002030_asan_annotate_vector/test.cpp | 387 ++++++++++++++++-- 2 files changed, 384 insertions(+), 40 deletions(-) diff --git a/stl/inc/vector b/stl/inc/vector index 3e26698159..fe335e0490 100644 --- a/stl/inc/vector +++ b/stl/inc/vector @@ -483,7 +483,7 @@ public: // New size is less than current capacity; assume we're reusing the same block. const _Value_type* _New_last = _Raw_first + _New_size; - _Asan_last = _Raw_last; + _Asan_last = _Raw_last; if (_Asan_last < _New_last) { // If the new size is larger than the current size extend the legal range of the container. @@ -504,8 +504,14 @@ public: const _Value_type* _Raw_last = _Unfancy(_My_data._Mylast); const _Value_type* _Raw_end = _Unfancy(_My_data._Myend); + if (!_Raw_first) { + // The operation did not allocate any memory. Do nothing. + return; + } + if (!_Asan_last) { - // New size was larger than current capacity; pessimisticly re-annotate the whole illegal range of the container. + // New size was larger than current capacity; pessimisticly re-annotate the whole illegal range of the + // container. __sanitizer_annotate_contiguous_container(_Raw_first, _Raw_end, _Raw_end, _Raw_last); return; } @@ -530,7 +536,7 @@ private: const auto& _My_data = _Target_vec._Mypair._Myval2; const _Value_type* _Raw_first = _Unfancy_maybe_null(_My_data._Myfirst); const _Value_type* _Raw_end = _Unfancy_maybe_null(_My_data._Myend); - const _Size_type _Capacity = static_cast<_Size_type>(_Raw_end - _Raw_first); + const _Size_type _Capacity = static_cast<_Size_type>(_Raw_end - _Raw_first); if (_Capacity * sizeof(_Value_type) >= 8) { // We are guaranteed to have sufficient space to find a legal aligned address. @@ -568,6 +574,11 @@ public: const _Value_type* _Raw_last = _Unfancy_maybe_null(_My_data._Mylast); const _Value_type* _Raw_end = _Unfancy_maybe_null(_My_data._Myend); + if (!_Raw_first) { + // The operation did not allocate any memory. Do nothing. + return; + } + _Aligned_first = _Get_aligned_start(); if (!_Aligned_first) { // There is no aligned address within the underlying buffer. The whole region is legal. @@ -609,7 +620,8 @@ public: const _Value_type* _Raw_end = _Unfancy(_My_data._Myend); if (!_Asan_last) { - // New size was larger than current capacity; pessimisticly re-annotate the whole illegal range of the container. + // New size was larger than current capacity; pessimisticly re-annotate the whole illegal range of the + // container. _Aligned_first = _Get_aligned_start(); if (_Aligned_first) { _Asan_last = _Raw_last > _Aligned_first ? _Raw_last : _Aligned_first; @@ -617,8 +629,10 @@ public: __sanitizer_annotate_contiguous_container(_Aligned_first, _Raw_end, _Raw_end, _Asan_last); } return; - } else if (_Aligned_first) { - const void *_New_last = _Raw_last < _Aligned_first ? _Aligned_first : _Raw_last; + } + + if (_Aligned_first) { + const void* _New_last = _Raw_last < _Aligned_first ? _Aligned_first : _Raw_last; if (_New_last != _Asan_last) { __sanitizer_annotate_contiguous_container(_Aligned_first, _Raw_end, _Asan_last, _New_last); @@ -671,6 +685,7 @@ private: template _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; @@ -723,7 +738,8 @@ public: _Tidy_guard _Guard{this}; for (; _UFirst != _ULast; ++_UFirst) { - _Emplace_one_at_back(*_UFirst); + _Emplace_one_at_back(*_UFirst); // performance note: except for one-at-back, emplace_back()'s strong + // guarantee is unnecessary here. } _Guard._Target = nullptr; @@ -740,7 +756,6 @@ public: _CONSTEXPR20 vector(const vector& _Right) : _Mypair(_One_then_variadic_args_t{}, _Alty_traits::select_on_container_copy_construction(_Right._Getal())) { _ASAN_ANNOTATE(*this); - const auto& _Right_data = _Right._Mypair._Myval2; const auto _Count = static_cast(_Right_data._Mylast - _Right_data._Myfirst); _Construct_n(_Count, _Right_data._Myfirst, _Right_data._Mylast); @@ -817,7 +832,7 @@ public: private: template - _CONSTEXPR20 decltype(auto) _Emplace_back_with_unused_capacity(_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; @@ -1043,6 +1058,7 @@ 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_one_at_back()'s strong guarantee is unnecessary here. for (; _First != _Last; ++_First) { _Emplace_one_at_back(*_First); @@ -1251,7 +1267,8 @@ private: // Append. for (; _First != _Last; ++_First) { - _Emplace_one_at_back(*_First); + _Emplace_one_at_back( + *_First); // perfromance note: _Emplace_one_at_back()'s strong guarantee is unnecessary here } } diff --git a/tests/std/tests/GH_002030_asan_annotate_vector/test.cpp b/tests/std/tests/GH_002030_asan_annotate_vector/test.cpp index 025e198094..a9ca64a055 100644 --- a/tests/std/tests/GH_002030_asan_annotate_vector/test.cpp +++ b/tests/std/tests/GH_002030_asan_annotate_vector/test.cpp @@ -12,6 +12,30 @@ using namespace std; extern "C" int __sanitizer_verify_contiguous_container(const void* beg, const void* mid, const void* end); +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; + } +}; + +struct throw_on_copy { + throw_on_copy() = default; + throw_on_copy(const throw_on_copy&) { + throw 0; + } + throw_on_copy(throw_on_copy&&) {} +}; + template class input_iterator_tester { private: @@ -24,12 +48,12 @@ class input_iterator_tester { public: using iterator_category = input_iterator_tag; - using value_type = T; - using difference_type = ptrdiff_t; - using pointer = T*; - using reference = T&; + using value_type = T; + using difference_type = ptrdiff_t; + using pointer = T*; + using reference = T&; - iterator(T* start) : curr(start) { } + iterator(T* start) : curr(start) {} reference operator*() const { return *curr; @@ -88,14 +112,14 @@ struct custom_test_allocator { }; template -constexpr bool operator==(const custom_test_allocator&, - const custom_test_allocator&) noexcept { +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 { +constexpr bool operator!=( + const custom_test_allocator&, const custom_test_allocator&) noexcept { return !Stateless::value; } @@ -151,7 +175,7 @@ struct implicit_allocator : public custom_test_allocator { }; template -void test_case_push_pop() { +void test_push_pop() { using T = typename Alloc::value_type; vector v; @@ -165,7 +189,7 @@ void test_case_push_pop() { } template -void test_case_reserve_shrink() { +void test_reserve_shrink() { using T = typename Alloc::value_type; vector v; @@ -201,7 +225,7 @@ void test_case_reserve_shrink() { } template -void test_case_emplace_pop() { +void test_emplace_pop() { using T = typename Alloc::value_type; vector v; @@ -221,7 +245,7 @@ void test_case_emplace_pop() { } template -void test_case_move_assign() { +void test_move_assign() { using T = typename Alloc::value_type; vector v1; @@ -238,7 +262,7 @@ void test_case_move_assign() { } template -void test_case_copy_assign() { +void test_copy_assign() { using T = typename Alloc::value_type; vector v1; @@ -255,7 +279,7 @@ void test_case_copy_assign() { } template -void test_case_constructors() { +void test_constructors() { using T = typename Alloc::value_type; Alloc al = Alloc(); @@ -285,7 +309,7 @@ void test_case_constructors() { } template -void test_case_insert_n() { +void test_insert_n() { using T = typename Alloc::value_type; vector v(1); @@ -299,7 +323,7 @@ void test_case_insert_n() { } template -void test_case_insert_range() { +void test_insert_range() { using T = typename Alloc::value_type; vector v1(1); @@ -322,7 +346,7 @@ void test_case_insert_range() { } template -void test_case_assign() { +void test_assign() { using T = typename Alloc::value_type; vector v1(1); @@ -360,7 +384,7 @@ void test_case_assign() { } template -void test_case_resize() { +void test_resize() { using T = typename Alloc::value_type; vector v; @@ -370,18 +394,316 @@ void test_case_resize() { 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(0); + } catch (int) { + assert(verify_vector(v)); + } + } + { + vector v; + + throw_on_construction t(false); + try { + v.push_back(t); + assert(0); + } catch (int) { + assert(verify_vector(v)); + } + } + { + vector v; + v.reserve(1); + + try { + v.push_back(throw_on_construction(false)); + assert(0); + } catch (int) { + assert(verify_vector(v)); + } + } + { + vector v; + + try { + v.push_back(throw_on_construction(false)); + assert(0); + } catch (int) { + assert(verify_vector(v)); + } + } +} + +void test_emplace_back_throw() { + + { + vector v; + v.reserve(1); + + try { + v.emplace_back(true); + assert(0); + } catch (int) { + assert(verify_vector(v)); + } + } + { + vector v; + + try { + v.emplace_back(true); + assert(0); + } 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(0); + } 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(0); + } 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(0); + } catch (int) { + assert(verify_vector(v)); + } + } + + { + vector v; + + try { + v.insert(v.end(), {throw_on_construction(false), throw_on_construction(false)}); + assert(0); + } 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(0); + } 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(0); + } catch (int) { + assert(verify_vector(v)); + } + } + + { + vector v; + + v.reserve(1); + + try { + v.insert(v.end(), throw_on_construction(false)); + assert(0); + } catch (int) { + assert(verify_vector(v)); + } + } + + { + vector v; + + try { + v.insert(v.end(), throw_on_construction(false)); + assert(0); + } 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(0); + } 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(0); + } catch (int) { + assert(verify_vector(v)); + } + } + + { + vector v; + + v.reserve(1); + + try { + v.emplace(v.end(), true); + assert(0); + } catch (int) { + assert(verify_vector(v)); + } + } + + { + vector v; + + try { + v.emplace(v.end(), true); + assert(0); + } catch (int) { + assert(verify_vector(v)); + } + } +} + +void test_resize_throw() { + { + vector v; + + v.reserve(2); + v.emplace_back(false); + + try { + v.resize(2); + assert(0); + } catch (int) { + assert(verify_vector(v)); + } + } + + { + vector v; + + v.reserve(1); + v.emplace_back(false); + + try { + v.resize(2); + assert(0); + } 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(0); + } 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(0); + } catch (int) { + assert(verify_vector(v)); + } + } +} + template void run_tests() { - test_case_push_pop(); - test_case_reserve_shrink(); - test_case_emplace_pop(); - test_case_move_assign(); - test_case_copy_assign(); - test_case_constructors(); - test_case_insert_n(); - test_case_insert_range(); - test_case_assign(); - test_case_resize(); + 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> @@ -405,5 +727,10 @@ int main() { run_allocator_matrix(); run_allocator_matrix(); - // TODO: Test a type that throws. + test_push_back_throw(); + test_emplace_back_throw(); + test_insert_range_throw(); + test_insert_throw(); + test_emplace_throw(); + test_resize_throw(); } From d977cf9a30b74c20186f6f3b7286adc731b4bdc7 Mon Sep 17 00:00:00 2001 From: Curtis Bezault Date: Tue, 17 Aug 2021 14:45:30 -0400 Subject: [PATCH 21/59] sigh, missed a file --- .../GH_002030_asan_annotate_vector/env.lst | 57 ++++++++++--------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/tests/std/tests/GH_002030_asan_annotate_vector/env.lst b/tests/std/tests/GH_002030_asan_annotate_vector/env.lst index ee5c6357e1..96295c867f 100644 --- a/tests/std/tests/GH_002030_asan_annotate_vector/env.lst +++ b/tests/std/tests/GH_002030_asan_annotate_vector/env.lst @@ -1,33 +1,36 @@ # Copyright (c) Microsoft Corporation. # 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. +# 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 targetting /M[DT]d. RUNALL_INCLUDE ..\prefix.lst RUNALL_CROSSLIST -PM_CL="/BE /c /EHsc /MD /std:c++14 /w14640 /Zc:threadSafeInit-" -PM_CL="/BE /c /EHsc /MDd /std:c++17 /permissive- /w14640 /Zc:threadSafeInit-" -PM_CL="/BE /c /EHsc /MT /std:c++20 /permissive- /w14640 /Zc:threadSafeInit-" -PM_CL="/BE /c /EHsc /MTd /std:c++latest /permissive- /w14640 /Zc:threadSafeInit-" -PM_CL="/EHsc /MD /std:c++14 /w14640 /Zc:threadSafeInit-" -PM_CL="/EHsc /MD /std:c++17 /w14640 /Zc:threadSafeInit-" -PM_CL="/EHsc /MD /std:c++20 /w14640 /Zc:threadSafeInit-" -PM_CL="/EHsc /MD /std:c++latest /permissive- /Zc:char8_t- /w14640 /Zc:threadSafeInit- /Zc:preprocessor" -PM_CL="/EHsc /MD /std:c++latest /permissive- /w14640 /Zc:threadSafeInit- /Zc:noexceptTypes-" -PM_CL="/EHsc /MDd /std:c++14 /fp:except /w14640 /Zc:threadSafeInit- /Zc:preprocessor" -PM_CL="/EHsc /MDd /std:c++17 /permissive- /w14640 /Zc:threadSafeInit-" -PM_CL="/EHsc /MDd /std:c++20 /permissive- /w14640 /Zc:threadSafeInit-" -PM_CL="/EHsc /MDd /std:c++latest /permissive- /Zc:wchar_t- /w14640 /Zc:threadSafeInit-" -PM_CL="/EHsc /MDd /std:c++latest /permissive- /w14640 /Zc:threadSafeInit-" -PM_CL="/EHsc /MT /std:c++latest /permissive- /analyze:only /analyze:autolog- /w14640 /Zc:threadSafeInit-" -PM_CL="/EHsc /MT /std:c++latest /permissive- /w14640 /Zc:threadSafeInit-" -PM_CL="/EHsc /MTd /std:c++latest /permissive /w14640 /Zc:threadSafeInit-" -PM_CL="/EHsc /MTd /std:c++latest /permissive- /analyze:only /analyze:autolog- /w14640 /Zc:threadSafeInit-" -PM_CL="/EHsc /MTd /std:c++latest /permissive- /fp:strict /w14640 /Zc:threadSafeInit-" -PM_CL="/EHsc /MTd /std:c++latest /permissive- /w14640 /Zc:threadSafeInit-" -PM_CL="/Za /EHsc /MD /std:c++latest /permissive- /w14640 /Zc:threadSafeInit-" -PM_CL="/Za /EHsc /MDd /std:c++latest /permissive- /w14640 /Zc:threadSafeInit-" -PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing /EHsc /MD /std:c++14 /w14640 /Zc:threadSafeInit-" -PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing /EHsc /MD /std:c++17 /w14640 /Zc:threadSafeInit-" -PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing /EHsc /MT /std:c++latest /permissive- /w14640 /Zc:threadSafeInit-" -PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing /EHsc /MT /std:c++latest /permissive- /D_HAS_CXX23 /fp:strict /w14640 /Zc:threadSafeInit-" +PM_CL="-fsanitize=address /Zi /fno-sanitize-address-vcasan-lib /wd4611 /w14640 /Zc:threadSafeInit-" PM_LINK="/debug" RUNALL_CROSSLIST -PM_CL="-fsanitize=address /Zi /fno-sanitize-address-vcasan-lib /wd4611" PM_LINK="/debug" +PM_CL="/BE /c /EHsc /MD /std:c++14" +PM_CL="/BE /c /EHsc /MDd /std:c++17 /permissive-" +PM_CL="/BE /c /EHsc /MT /std:c++20 /permissive-" +PM_CL="/BE /c /EHsc /MTd /std:c++latest /permissive-" +PM_CL="/EHsc /MD /std:c++14" +PM_CL="/EHsc /MD /std:c++17" +PM_CL="/EHsc /MD /std:c++20" +PM_CL="/EHsc /MD /std:c++latest /permissive- /Zc:char8_t- /Zc:preprocessor" +PM_CL="/EHsc /MD /std:c++latest /permissive- /Zc:noexceptTypes-" +PM_CL="/EHsc /MDd /std:c++14 /fp:except /Zc:preprocessor" +PM_CL="/EHsc /MDd /std:c++17 /permissive-" +PM_CL="/EHsc /MDd /std:c++20 /permissive-" +PM_CL="/EHsc /MDd /std:c++latest /permissive- /Zc:wchar_t-" +PM_CL="/EHsc /MDd /std:c++latest /permissive-" +PM_CL="/EHsc /MT /std:c++latest /permissive- /analyze:only /analyze:autolog-" +PM_CL="/EHsc /MT /std:c++latest /permissive-" +PM_CL="/EHsc /MTd /std:c++latest /permissive" +PM_CL="/EHsc /MTd /std:c++latest /permissive- /analyze:only /analyze:autolog-" +PM_CL="/EHsc /MTd /std:c++latest /permissive- /fp:strict" +PM_CL="/EHsc /MTd /std:c++latest /permissive-" +PM_CL="/Za /EHsc /MD /std:c++latest /permissive-" +PM_CL="/Za /EHsc /MDd /std:c++latest /permissive-" +PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing /EHsc /MD /std:c++14" +PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing /EHsc /MD /std:c++17" +PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing /EHsc /MT /std:c++latest /permissive-" +PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing /EHsc /MT /std:c++latest /permissive- /D_HAS_CXX23 /fp:strict" From 9dd9d6cb7dd059b11716749bc1d165a09270c133 Mon Sep 17 00:00:00 2001 From: Curtis Bezault Date: Tue, 17 Aug 2021 15:42:51 -0400 Subject: [PATCH 22/59] Remove shouty banners from bad merge --- stl/inc/vector | 2 -- 1 file changed, 2 deletions(-) diff --git a/stl/inc/vector b/stl/inc/vector index fe335e0490..7235958a66 100644 --- a/stl/inc/vector +++ b/stl/inc/vector @@ -651,7 +651,6 @@ private: #define _ASAN_ANNOTATE(...) #endif // !__SANITIZE_ADDRESS__ -// CLASS TEMPLATE vector template > class vector { // varying size array of values private: @@ -2212,7 +2211,6 @@ constexpr typename vector<_Ty, _Alloc>::size_type erase_if(vector<_Ty, _Alloc>& } #endif // _HAS_CXX20 -// CLASS TEMPLATE vector AND FRIENDS template struct _Wrap_alloc { // TRANSITION, ABI compat, preserves symbol names of vector::iterator using _Alloc = _Alloc0; From 3d43d92142dce45bc5ef31b0bf7d9a2a83832dea Mon Sep 17 00:00:00 2001 From: Curtis Bezault Date: Wed, 18 Aug 2021 11:42:34 -0400 Subject: [PATCH 23/59] Resolve comments --- stl/inc/vector | 52 ++++++++++--------- .../GH_002030_asan_annotate_vector/env.lst | 2 +- .../GH_002030_asan_annotate_vector/test.cpp | 23 ++++++-- 3 files changed, 48 insertions(+), 29 deletions(-) diff --git a/stl/inc/vector b/stl/inc/vector index 7235958a66..37592f364e 100644 --- a/stl/inc/vector +++ b/stl/inc/vector @@ -470,14 +470,18 @@ public: const auto& _My_data = _Target_vec._Mypair._Myval2; const _Value_type* _Raw_first = _Unfancy_maybe_null(_My_data._Myfirst); - const _Value_type* _Raw_last = _Unfancy_maybe_null(_My_data._Mylast); - const _Value_type* _Raw_end = _Unfancy_maybe_null(_My_data._Myend); + + if (!_Raw_first) { + // The vector is empty. We'll extend the accessible range in the destructor. + return; + } + + const _Value_type* _Raw_last = _Unfancy(_My_data._Mylast); + const _Value_type* _Raw_end = _Unfancy(_My_data._Myend); if (_New_size > static_cast<_Size_type>(_Raw_end - _Raw_first)) { - if (_Raw_end != _Raw_first) { - // New size is larger than current capacity; assume we're allocating a new block. - __sanitizer_annotate_contiguous_container(_Raw_first, _Raw_end, _Raw_last, _Raw_end); - } + // New size is larger than current capacity; assume we're allocating a new block. + __sanitizer_annotate_contiguous_container(_Raw_first, _Raw_end, _Raw_last, _Raw_end); return; } @@ -486,7 +490,7 @@ public: _Asan_last = _Raw_last; if (_Asan_last < _New_last) { - // If the new size is larger than the current size extend the legal range of the container. + // If the new size is larger than the current size extend the accessible range of the container. __sanitizer_annotate_contiguous_container(_Raw_first, _Raw_end, _Asan_last, _New_last); _Asan_last = _New_last; } @@ -500,17 +504,18 @@ public: #endif // _HAS_CXX20 const auto& _My_data = _Target_vec._Mypair._Myval2; - const _Value_type* _Raw_first = _Unfancy(_My_data._Myfirst); - const _Value_type* _Raw_last = _Unfancy(_My_data._Mylast); - const _Value_type* _Raw_end = _Unfancy(_My_data._Myend); + const _Value_type* _Raw_first = _Unfancy_maybe_null(_My_data._Myfirst); if (!_Raw_first) { // The operation did not allocate any memory. Do nothing. return; } + const _Value_type* _Raw_last = _Unfancy(_My_data._Mylast); + const _Value_type* _Raw_end = _Unfancy(_My_data._Myend); + if (!_Asan_last) { - // New size was larger than current capacity; pessimisticly re-annotate the whole illegal range of the + // New size was larger than current capacity; pessimisticly re-annotate the whole inaccessible range of the // container. __sanitizer_annotate_contiguous_container(_Raw_first, _Raw_end, _Raw_end, _Raw_last); return; @@ -539,7 +544,7 @@ private: const _Size_type _Capacity = static_cast<_Size_type>(_Raw_end - _Raw_first); if (_Capacity * sizeof(_Value_type) >= 8) { - // We are guaranteed to have sufficient space to find a legal aligned address. + // We are guaranteed to have sufficient space to find an aligned address. return reinterpret_cast((reinterpret_cast(_Raw_first) + 7) & ~7); } @@ -571,38 +576,35 @@ public: const auto& _My_data = _Target_vec._Mypair._Myval2; const _Value_type* _Raw_first = _Unfancy_maybe_null(_My_data._Myfirst); - const _Value_type* _Raw_last = _Unfancy_maybe_null(_My_data._Mylast); - const _Value_type* _Raw_end = _Unfancy_maybe_null(_My_data._Myend); if (!_Raw_first) { // The operation did not allocate any memory. Do nothing. return; } + const _Value_type* _Raw_last = _Unfancy(_My_data._Mylast); + const _Value_type* _Raw_end = _Unfancy(_My_data._Myend); + _Aligned_first = _Get_aligned_start(); if (!_Aligned_first) { - // There is no aligned address within the underlying buffer. The whole region is legal. + // There is no aligned address within the underlying buffer. Allow access to the while region. _Asan_last = _Raw_end; return; } - // New size is less than current capacity; assume we're reusing the same block. const void* _New_last = _Raw_first + _New_size; _New_last = _New_last < _Aligned_first ? _Aligned_first : _New_last; _Asan_last = _Raw_last < _Aligned_first ? _Aligned_first : _Raw_last; if (_New_size > static_cast<_Size_type>(_Raw_end - _Raw_first)) { // New size is larger than current capacity; assume we're allocating a new block. - if (_Raw_end != _Raw_first) { - __sanitizer_annotate_contiguous_container(_Aligned_first, _Raw_end, _Asan_last, _Raw_end); - } - + __sanitizer_annotate_contiguous_container(_Aligned_first, _Raw_end, _Asan_last, _Raw_end); _Asan_last = nullptr; return; } if (_Asan_last < _New_last) { - // If the new size is larger than the current size extend the legal range of the container. + // If the new size is larger than the current size extend the accessible range of the container. __sanitizer_annotate_contiguous_container(_Aligned_first, _Raw_end, _Asan_last, _New_last); _Asan_last = _New_last; } @@ -620,7 +622,7 @@ public: const _Value_type* _Raw_end = _Unfancy(_My_data._Myend); if (!_Asan_last) { - // New size was larger than current capacity; pessimisticly re-annotate the whole illegal range of the + // New size was larger than current capacity; pessimisticly re-annotate the whole inaccessible range of the // container. _Aligned_first = _Get_aligned_start(); if (_Aligned_first) { @@ -1052,7 +1054,7 @@ private: const auto _Oldsize = static_cast(_Mylast - _Myfirst); // Pessimistically assume that we will fill the vector's capacity. - // This causes the entire underlying buffer to initially be marked as legal. + // This causes the entire underlying buffer to initially be marked as accessible. _ASAN_ANNOTATE(*this, static_cast(_My_data._Myend - _Myfirst) + 1); // For one-at-back, provide strong guarantee. @@ -1244,7 +1246,7 @@ private: pointer& _Mylast = _My_data._Mylast; // Pessimistically assume that we will fill the vector's capacity. - // This causes the entire underlying buffer to initially be marked as legal. + // This causes the entire underlying buffer to initially be marked as accessible. _ASAN_ANNOTATE(*this, static_cast(_My_data._Myend - _Myfirst) + 1); _My_data._Orphan_all(); @@ -1267,7 +1269,7 @@ private: // Append. for (; _First != _Last; ++_First) { _Emplace_one_at_back( - *_First); // perfromance note: _Emplace_one_at_back()'s strong guarantee is unnecessary here + *_First); // performance note: _Emplace_one_at_back()'s strong guarantee is unnecessary here } } diff --git a/tests/std/tests/GH_002030_asan_annotate_vector/env.lst b/tests/std/tests/GH_002030_asan_annotate_vector/env.lst index 96295c867f..7c112bb842 100644 --- a/tests/std/tests/GH_002030_asan_annotate_vector/env.lst +++ b/tests/std/tests/GH_002030_asan_annotate_vector/env.lst @@ -2,7 +2,7 @@ # 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. -# 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. +# 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 targetting /M[DT]d. RUNALL_INCLUDE ..\prefix.lst RUNALL_CROSSLIST diff --git a/tests/std/tests/GH_002030_asan_annotate_vector/test.cpp b/tests/std/tests/GH_002030_asan_annotate_vector/test.cpp index a9ca64a055..e8ecd72571 100644 --- a/tests/std/tests/GH_002030_asan_annotate_vector/test.cpp +++ b/tests/std/tests/GH_002030_asan_annotate_vector/test.cpp @@ -10,7 +10,7 @@ using namespace std; -extern "C" int __sanitizer_verify_contiguous_container(const void* beg, const void* mid, const void* end); +extern "C" int __sanitizer_verify_contiguous_container(const void* beg, const void* mid, const void* end) noexcept; struct throw_on_construction { throw_on_construction() { @@ -50,10 +50,10 @@ class input_iterator_tester { using iterator_category = input_iterator_tag; using value_type = T; using difference_type = ptrdiff_t; - using pointer = T*; + using pointer = void; using reference = T&; - iterator(T* start) : curr(start) {} + explicit iterator(T* start) : curr(start) {} reference operator*() const { return *curr; @@ -692,6 +692,22 @@ void test_resize_throw() { } } +void test_insert_n_throw() { + { + vector v; + + v.reserve(1); + v.push_back(throw_on_copy()); + + try { + v.resize(2, throw_on_copy()); + assert(0); + } catch (int) { + assert(verify_vector(v)); + } + } +} + template void run_tests() { test_push_pop(); @@ -733,4 +749,5 @@ int main() { test_insert_throw(); test_emplace_throw(); test_resize_throw(); + test_insert_n_throw(); } From 0a6d34f2b30a5a9aa59b21f15c1c17f116fb733f Mon Sep 17 00:00:00 2001 From: Curtis Bezault Date: Wed, 18 Aug 2021 11:46:55 -0400 Subject: [PATCH 24/59] Resolve more comments --- stl/inc/vector | 4 ++-- tests/std/tests/GH_002030_asan_annotate_vector/test.cpp | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/stl/inc/vector b/stl/inc/vector index 37592f364e..537d4f2841 100644 --- a/stl/inc/vector +++ b/stl/inc/vector @@ -515,7 +515,7 @@ public: const _Value_type* _Raw_end = _Unfancy(_My_data._Myend); if (!_Asan_last) { - // New size was larger than current capacity; pessimisticly re-annotate the whole inaccessible range of the + // New size is larger than prior capacity; pessimistically re-annotate the whole accessible range of the // container. __sanitizer_annotate_contiguous_container(_Raw_first, _Raw_end, _Raw_end, _Raw_last); return; @@ -622,7 +622,7 @@ public: const _Value_type* _Raw_end = _Unfancy(_My_data._Myend); if (!_Asan_last) { - // New size was larger than current capacity; pessimisticly re-annotate the whole inaccessible range of the + // New size is larger than prior capacity; pessimistically re-annotate the whole accessible range of the // container. _Aligned_first = _Get_aligned_start(); if (_Aligned_first) { diff --git a/tests/std/tests/GH_002030_asan_annotate_vector/test.cpp b/tests/std/tests/GH_002030_asan_annotate_vector/test.cpp index e8ecd72571..1dd8169f3b 100644 --- a/tests/std/tests/GH_002030_asan_annotate_vector/test.cpp +++ b/tests/std/tests/GH_002030_asan_annotate_vector/test.cpp @@ -442,7 +442,6 @@ void test_push_back_throw() { } void test_emplace_back_throw() { - { vector v; v.reserve(1); From 56a3d839ef5bfb62934f9f978ce0080e32834a8c Mon Sep 17 00:00:00 2001 From: Curtis Bezault Date: Wed, 18 Aug 2021 14:30:13 -0400 Subject: [PATCH 25/59] Resolve PR comments --- stl/inc/vector | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/stl/inc/vector b/stl/inc/vector index 537d4f2841..436ad12d39 100644 --- a/stl/inc/vector +++ b/stl/inc/vector @@ -481,11 +481,12 @@ public: if (_New_size > static_cast<_Size_type>(_Raw_end - _Raw_first)) { // New size is larger than current capacity; assume we're allocating a new block. + // Clean up the shadow memory annotations for the buffer being freed. __sanitizer_annotate_contiguous_container(_Raw_first, _Raw_end, _Raw_last, _Raw_end); return; } - // New size is less than current capacity; assume we're reusing the same block. + // New size first in current capacity; assume we're reusing the same block. const _Value_type* _New_last = _Raw_first + _New_size; _Asan_last = _Raw_last; @@ -598,6 +599,7 @@ public: if (_New_size > static_cast<_Size_type>(_Raw_end - _Raw_first)) { // New size is larger than current capacity; assume we're allocating a new block. + // Clean up the shadow memory annotations for the buffer being freed. __sanitizer_annotate_contiguous_container(_Aligned_first, _Raw_end, _Asan_last, _Raw_end); _Asan_last = nullptr; return; @@ -1055,7 +1057,7 @@ private: // Pessimistically assume that we will fill the vector's capacity. // This causes the entire underlying buffer to initially be marked as accessible. - _ASAN_ANNOTATE(*this, static_cast(_My_data._Myend - _Myfirst) + 1); + _ASAN_ANNOTATE(*this, ~size_type{0}); // For one-at-back, provide strong guarantee. // Otherwise, provide basic guarantee (despite N4659 26.3.11.5 [vector.modifiers]/1). @@ -1247,7 +1249,7 @@ private: // Pessimistically assume that we will fill the vector's capacity. // This causes the entire underlying buffer to initially be marked as accessible. - _ASAN_ANNOTATE(*this, static_cast(_My_data._Myend - _Myfirst) + 1); + _ASAN_ANNOTATE(*this, ~size_type{0}); _My_data._Orphan_all(); From 873dd34ebfa32e2b1c0719141be4b320fcf29bc0 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Thu, 26 Aug 2021 14:41:46 +0200 Subject: [PATCH 26/59] Expand the allocator aware tests to also run at compile time and fix vector again --- stl/inc/vector | 2 +- .../test.cpp | 79 ++++++++++--------- 2 files changed, 42 insertions(+), 39 deletions(-) diff --git a/stl/inc/vector b/stl/inc/vector index 503a506f83..e2991641c4 100644 --- a/stl/inc/vector +++ b/stl/inc/vector @@ -1697,7 +1697,7 @@ private: _Clear_and_reserve_geometric(_Newsize); } - _Mylast = _Refancy(_Copy_memmove(_Unfancy(_First), _Unfancy(_Last), _Unfancy(_Myfirst))); + _Mylast = _Uninitialized_move(_First, _Last, _Myfirst, _Al); } else { auto _Oldsize = static_cast(_Mylast - _Myfirst); diff --git a/tests/std/tests/VSO_0000000_allocator_propagation/test.cpp b/tests/std/tests/VSO_0000000_allocator_propagation/test.cpp index f4cbcb8ee8..505024f2a7 100644 --- a/tests/std/tests/VSO_0000000_allocator_propagation/test.cpp +++ b/tests/std/tests/VSO_0000000_allocator_propagation/test.cpp @@ -24,72 +24,78 @@ using namespace std; template -void assert_equal(const C& c, initializer_list il) { +_CONSTEXPR20 void assert_equal(const C& c, initializer_list il) { assert(equal(c.begin(), c.end(), il.begin(), il.end())); } template -void assert_is_permutation(const C& c, initializer_list il) { +_CONSTEXPR20 void assert_is_permutation(const C& c, initializer_list il) { assert(is_permutation(c.begin(), c.end(), il.begin(), il.end())); } template class MyAlloc { private: - size_t m_offset; + size_t _offset; - size_t allocation_offset() const noexcept { - return is_always_equal::value ? 10 : m_offset; + [[nodiscard]] _CONSTEXPR20 size_t allocation_offset() const noexcept { + return is_always_equal::value ? 10 : _offset; } public: - size_t offset() const noexcept { - return m_offset; + [[nodiscard]] _CONSTEXPR20 size_t offset() const noexcept { + return _offset; } - typedef T value_type; + using value_type = T; - typedef POCCA propagate_on_container_copy_assignment; - typedef POCMA propagate_on_container_move_assignment; - typedef POCS propagate_on_container_swap; - typedef EQUAL is_always_equal; + using propagate_on_container_copy_assignment = POCCA; + using propagate_on_container_move_assignment = POCMA; + using propagate_on_container_swap = POCS; + using is_always_equal = EQUAL; - explicit MyAlloc(const size_t off) : m_offset(off) {} + _CONSTEXPR20 explicit MyAlloc(const size_t off) : _offset(off) {} template - MyAlloc(const MyAlloc& other) noexcept : m_offset(other.offset()) {} + _CONSTEXPR20 MyAlloc(const MyAlloc& other) noexcept : _offset(other.offset()) {} template - bool operator==(const MyAlloc& other) const noexcept { + [[nodiscard]] _CONSTEXPR20 bool operator==(const MyAlloc& other) const noexcept { return allocation_offset() == other.allocation_offset(); } template - bool operator!=(const MyAlloc& other) const noexcept { + [[nodiscard]] _CONSTEXPR20 bool operator!=(const MyAlloc& other) const noexcept { return allocation_offset() != other.allocation_offset(); } - T* allocate(const size_t n) { + _CONSTEXPR20 T* allocate(const size_t n) { if (n == 0) { return nullptr; } const auto allocationOffset = allocation_offset(); + const auto _allocated = n + allocationOffset; // Production code should check for integer overflow. - void* const pv = malloc((n + allocationOffset) * sizeof(T)); + auto pv = allocator{}.allocate(_allocated); if (!pv) { throw bad_alloc(); } - memset(pv, 0xAB, (n + allocationOffset) * sizeof(T)); +#if _HAS_CXX20 + if (!is_constant_evaluated()) +#endif // _HAS_CXX20 + { + memset(pv, 0xAB, (_allocated) * sizeof(T)); + } - return static_cast(pv) + allocationOffset; + return pv + allocationOffset; } - void deallocate(T* const p, size_t) noexcept { + _CONSTEXPR20 void deallocate(T* const p, const size_t size) noexcept { if (p) { - free(p - allocation_offset()); + allocator{}.deallocate(p - allocation_offset(), size + allocation_offset()); } } }; @@ -111,8 +117,7 @@ using SwapEqualAlloc = MyAlloc; template