`<vector>`: Add ASan annotations. (#2071)

Co-authored-by: Casey Carter <cartec69@gmail.com>
Co-authored-by: Michael Schellenberger Costa <mschellenbergercosta@googlemail.com>
Co-authored-by: Stephan T. Lavavej <stl@microsoft.com>
This commit is contained in:
Curtis J Bezault 2021-12-10 20:03:29 -08:00 коммит произвёл GitHub
Родитель 5e3574b979
Коммит 2f9f567ffe
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
16 изменённых файлов: 1455 добавлений и 53 удалений

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

@ -250,6 +250,7 @@ endforeach()
# Objs that exist in both libcpmt[d][01].lib and msvcprt[d].lib.
set(IMPLIB_SOURCES
${CMAKE_CURRENT_LIST_DIR}/src/asan_noop.cpp
${CMAKE_CURRENT_LIST_DIR}/src/filesystem.cpp
${CMAKE_CURRENT_LIST_DIR}/src/format.cpp
${CMAKE_CURRENT_LIST_DIR}/src/locale0_implib.cpp
@ -392,6 +393,10 @@ set(SOURCES
${CMAKE_CURRENT_LIST_DIR}/src/xwstoxfl.cpp
)
set(ASAN_SOURCES
${CMAKE_CURRENT_LIST_DIR}/src/asan.cpp
)
set(EHA_SOURCES
${CMAKE_CURRENT_LIST_DIR}/src/excptptr.cpp
)
@ -543,11 +548,11 @@ add_stl_dlls("d" "_DEBUG" "${VCLIBS_DEBUG_OPTIONS}" "" "/opt:ref,noicf")
function(add_stl_statics FLAVOR_SUFFIX THIS_CONFIG_DEFINITIONS THIS_CONFIG_COMPILE_OPTIONS)
add_library(libcpmt${FLAVOR_SUFFIX}_eha OBJECT ${EHA_SOURCES})
target_compile_definitions(libcpmt${FLAVOR_SUFFIX}_eha PRIVATE "${THIS_CONFIG_DEFINITIONS}")
target_compile_definitions(libcpmt${FLAVOR_SUFFIX}_eha PRIVATE "${THIS_CONFIG_DEFINITIONS};_ANNOTATE_VECTOR")
target_compile_options(libcpmt${FLAVOR_SUFFIX}_eha PRIVATE "${THIS_CONFIG_COMPILE_OPTIONS};/EHa")
add_library(libcpmt${FLAVOR_SUFFIX} STATIC ${HEADERS} ${IMPLIB_SOURCES} ${SOURCES} ${INITIALIZER_SOURCES} ${STATIC_SOURCES})
target_compile_definitions(libcpmt${FLAVOR_SUFFIX} PRIVATE "${THIS_CONFIG_DEFINITIONS}")
target_compile_definitions(libcpmt${FLAVOR_SUFFIX} PRIVATE "${THIS_CONFIG_DEFINITIONS};_ANNOTATE_VECTOR")
target_compile_options(libcpmt${FLAVOR_SUFFIX} PRIVATE "${THIS_CONFIG_COMPILE_OPTIONS};/EHsc")
target_link_libraries(libcpmt${FLAVOR_SUFFIX} PRIVATE Boost::math libcpmt${FLAVOR_SUFFIX}_eha std_init_once_begin_initialize std_init_once_complete)
endfunction()
@ -557,3 +562,5 @@ add_stl_statics("1" "_ITERATOR_DEBUG_LEVEL=1" "${VCLIBS_RELEASE_OPTIONS}")
add_stl_statics("d" "_DEBUG;_ITERATOR_DEBUG_LEVEL=2" "${VCLIBS_DEBUG_OPTIONS}")
add_stl_statics("d1" "_DEBUG;_ITERATOR_DEBUG_LEVEL=1" "${VCLIBS_DEBUG_OPTIONS}")
add_stl_statics("d0" "_DEBUG;_ITERATOR_DEBUG_LEVEL=0" "${VCLIBS_DEBUG_OPTIONS}")
add_library(stl_asan STATIC ${ASAN_SOURCES})

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

@ -428,6 +428,54 @@ constexpr _Ty* _Unfancy_maybe_null(_Ty* _Ptr) noexcept { // do nothing for plain
return _Ptr;
}
#if !defined(_M_CEE_PURE) && !defined(_DISABLE_VECTOR_ANNOTATION)
#if defined(__SANITIZE_ADDRESS__)
#define _ACTIVATE_VECTOR_ANNOTATION
#define _INSERT_VECTOR_ANNOTATION
#elif defined(__clang__) && defined(__has_feature) // ^^^ __SANITIZE_ADDRESS__ ^^^ // vvv __clang__ vvv
#if __has_feature(address_sanitizer)
#define _ACTIVATE_VECTOR_ANNOTATION
#define _INSERT_VECTOR_ANNOTATION
#pragma comment(linker, "/INFERASANLIBS")
#endif // __has_feature(address_sanitizer)
#elif defined(_ANNOTATE_VECTOR) // ^^^ __clang__ ^^^ // vvv _ANNOTATE_VECTOR vvv
#define _INSERT_VECTOR_ANNOTATION
#endif // _ANNOTATE_VECTOR
#endif // !_M_CEE_PURE && !_DISABLE_VECTOR_ANNOTATION
#ifdef _ACTIVATE_VECTOR_ANNOTATION
#pragma comment(lib, "stl_asan")
#pragma detect_mismatch("annotate_vector", "1")
#endif // _ACTIVATE_VECTOR_ANNOTATION
#ifdef _INSERT_VECTOR_ANNOTATION
extern "C" {
void __cdecl __sanitizer_annotate_contiguous_container(
const void* _First, const void* _End, const void* _Old_last, const void* _New_last) noexcept;
extern const bool _Asan_vector_should_annotate;
}
#if defined(_M_IX86)
#pragma comment(linker, \
"/alternatename:___sanitizer_annotate_contiguous_container=___sanitizer_annotate_contiguous_container_default")
#pragma comment(linker, "/alternatename:__Asan_vector_should_annotate=__Asan_vector_should_annotate_default")
#elif defined(_M_X64) || defined(_M_ARM) || defined(_M_ARM64)
#pragma comment(linker, \
"/alternatename:__sanitizer_annotate_contiguous_container=__sanitizer_annotate_contiguous_container_default")
#pragma comment(linker, "/alternatename:_Asan_vector_should_annotate=_Asan_vector_should_annotate_default")
#endif
template <class _Vec, class = void>
_INLINE_VAR constexpr bool _Has_minimum_allocation_alignment = alignof(typename _Vec::value_type) >= _Asan_granularity;
template <class _Vec>
_INLINE_VAR constexpr bool
_Has_minimum_allocation_alignment<_Vec, void_t<decltype(_Vec::allocator_type::_Minimum_allocation_alignment)>> =
_Vec::allocator_type::_Minimum_allocation_alignment >= _Asan_granularity;
#else // ^^^ _INSERT_VECTOR_ANNOTATION ^^^ // vvv !_INSERT_VECTOR_ANNOTATION vvv
#pragma detect_mismatch("annotate_vector", "0")
#endif // !_INSERT_VECTOR_ANNOTATION
template <class _Ty, class _Alloc = allocator<_Ty>>
class vector { // varying size array of values
private:
@ -452,6 +500,142 @@ public:
using difference_type = typename _Alty_traits::difference_type;
private:
#ifdef _INSERT_VECTOR_ANNOTATION
_CONSTEXPR20 void _Create_annotation() const noexcept {
// Annotates the shadow memory of the valid range
auto& _My_data = _Mypair._Myval2;
_Apply_annotation(_My_data._Myfirst, _My_data._Myend, _My_data._Myend, _My_data._Mylast);
}
_CONSTEXPR20 void _Remove_annotation() const noexcept {
// Removes the shadow memory annotation of the range
auto& _My_data = _Mypair._Myval2;
_Apply_annotation(_My_data._Myfirst, _My_data._Myend, _My_data._Mylast, _My_data._Myend);
}
_CONSTEXPR20 void _Modify_annotation(const difference_type _Count) const noexcept {
// Extends/shrinks the annotated range by _Count
auto& _My_data = _Mypair._Myval2;
_Apply_annotation(_My_data._Myfirst, _My_data._Myend, _My_data._Mylast, _My_data._Mylast + _Count);
}
_NODISCARD static const void* _Get_aligned_first(const void* _First, const void* _End) noexcept {
const auto _CFirst = reinterpret_cast<const char*>(_First);
const auto _CEnd = reinterpret_cast<const char*>(_End);
const size_t _Capacity = static_cast<size_t>(_CEnd - _CFirst);
if (_Capacity >= _Asan_granularity) {
// We are guaranteed to have sufficient space to find an aligned address.
return reinterpret_cast<const void*>(
(reinterpret_cast<uintptr_t>(_CFirst) + (_Asan_granularity - 1)) & ~(_Asan_granularity - 1));
}
uintptr_t _Alignment_offset = reinterpret_cast<uintptr_t>(_CFirst) & (_Asan_granularity - 1);
if (_Alignment_offset != 0) {
_Alignment_offset = _Asan_granularity - _Alignment_offset;
}
if (_Capacity > _Alignment_offset) {
return _CFirst + _Alignment_offset;
}
return nullptr;
}
static _CONSTEXPR20 void _Apply_annotation(
pointer _First_, pointer _End_, pointer _Old_last_, pointer _New_last_) noexcept {
#if _HAS_CXX20
if (_STD is_constant_evaluated()) {
return;
}
#endif // _HAS_CXX20
if (!_Asan_vector_should_annotate) {
return;
}
const auto _First = reinterpret_cast<const char*>(_Unfancy_maybe_null(_First_));
const auto _End = reinterpret_cast<const char*>(_Unfancy_maybe_null(_End_));
const auto _Old_last = reinterpret_cast<const char*>(_Unfancy_maybe_null(_Old_last_));
const auto _New_last = reinterpret_cast<const char*>(_Unfancy_maybe_null(_New_last_));
if constexpr (_Has_minimum_allocation_alignment<vector>) {
__sanitizer_annotate_contiguous_container(_First, _End, _Old_last, _New_last);
} else {
const void* const _Aligned_first = _Get_aligned_first(_First, _End);
if (!_Aligned_first) {
// There is no aligned address within the underlying buffer; nothing to do.
return;
}
const void* const _Aligned_old_last = _Old_last < _Aligned_first ? _Aligned_first : _Old_last;
const void* const _Aligned_new_last = _New_last < _Aligned_first ? _Aligned_first : _New_last;
const void* const _Aligned_end = _End < _Aligned_first ? _Aligned_first : _End;
__sanitizer_annotate_contiguous_container(
_Aligned_first, _Aligned_end, _Aligned_old_last, _Aligned_new_last);
}
}
class _NODISCARD _Asan_extend_guard {
public:
_Asan_extend_guard(const _Asan_extend_guard&) = delete;
_Asan_extend_guard& operator=(const _Asan_extend_guard&) = delete;
constexpr explicit _Asan_extend_guard(vector* _Myvec_, size_type _Target_size_) noexcept
: _Myvec(_Myvec_), _Target_size(_Target_size_) {
_STL_INTERNAL_CHECK(_Myvec != nullptr);
auto& _My_data = _Myvec->_Mypair._Myval2;
_Apply_annotation(_My_data._Myfirst, _My_data._Myend, _My_data._Mylast, _My_data._Myfirst + _Target_size);
}
_CONSTEXPR20 ~_Asan_extend_guard() {
if (!_Myvec) { // Operation succeeded, no modification to the shadow memory required.
return;
}
// Shrinks the shadow memory to the current size of the vector.
auto& _My_data = _Myvec->_Mypair._Myval2;
_Apply_annotation(_My_data._Myfirst, _My_data._Myend, _My_data._Myfirst + _Target_size, _My_data._Mylast);
}
_CONSTEXPR20 void _Release() noexcept {
_Myvec = nullptr;
}
private:
vector* _Myvec;
size_type _Target_size;
};
class _NODISCARD _Asan_create_guard {
public:
_Asan_create_guard(const _Asan_create_guard&) = delete;
_Asan_create_guard& operator=(const _Asan_create_guard&) = delete;
constexpr explicit _Asan_create_guard(const vector& _Myvec_) noexcept : _Myvec(_Myvec_) {}
_CONSTEXPR20 ~_Asan_create_guard() {
_Myvec._Create_annotation();
}
private:
const vector& _Myvec;
};
#define _ASAN_VECTOR_MODIFY(n) _Modify_annotation((n))
#define _ASAN_VECTOR_REMOVE _Remove_annotation()
#define _ASAN_VECTOR_CREATE _Create_annotation()
#define _ASAN_VECTOR_CREATE_GUARD _Asan_create_guard _Annotator(*this)
#define _ASAN_VECTOR_EXTEND_GUARD(n) _Asan_extend_guard _Annotator(this, (n))
#define _ASAN_VECTOR_RELEASE_GUARD _Annotator._Release()
#else // ^^^ _INSERT_VECTOR_ANNOTATION ^^^ // vvv !_INSERT_VECTOR_ANNOTATION vvv
#define _ASAN_VECTOR_MODIFY(n)
#define _ASAN_VECTOR_REMOVE
#define _ASAN_VECTOR_CREATE
#define _ASAN_VECTOR_CREATE_GUARD
#define _ASAN_VECTOR_EXTEND_GUARD(n)
#define _ASAN_VECTOR_RELEASE_GUARD
#endif // !_INSERT_VECTOR_ANNOTATION
using _Scary_val = _Vector_val<conditional_t<_Is_simple_alloc_v<_Alty>, _Simple_types<_Ty>,
_Vec_iter_types<_Ty, size_type, difference_type, pointer, const_pointer, _Ty&, const _Ty&>>>;
@ -494,7 +678,8 @@ public:
_Tidy_guard<vector> _Guard{this};
for (; _UFirst != _ULast; ++_UFirst) {
emplace_back(*_UFirst); // performance note: emplace_back()'s strong guarantee is unnecessary here
// performance note: _Emplace_one_at_back's strong guarantee is unnecessary here.
_Emplace_one_at_back(*_UFirst);
}
_Guard._Target = nullptr;
@ -547,6 +732,8 @@ public:
_Tidy_guard<vector> _Guard{this};
_My_data._Mylast =
_Uninitialized_move(_Right_data._Myfirst, _Right_data._Mylast, _My_data._Myfirst, _Al);
_ASAN_VECTOR_CREATE;
_Guard._Target = nullptr;
}
_Proxy._Release();
@ -599,47 +786,39 @@ public:
private:
template <class... _Valty>
_CONSTEXPR20 decltype(auto) _Emplace_back_with_unused_capacity(_Valty&&... _Val) {
_CONSTEXPR20 _Ty& _Emplace_one_at_back(_Valty&&... _Val) {
// insert by perfectly forwarding into element at end, provide strong guarantee
auto& _My_data = _Mypair._Myval2;
pointer& _Mylast = _My_data._Mylast;
_STL_INTERNAL_CHECK(_Mylast != _My_data._Myend); // check that we have unused capacity
_Alty_traits::construct(_Getal(), _Unfancy(_Mylast), _STD forward<_Valty>(_Val)...);
_Orphan_range(_Mylast, _Mylast);
_Ty& _Result = *_Mylast;
++_Mylast;
#if _HAS_CXX17
return _Result;
#else // ^^^ _HAS_CXX17 ^^^ // vvv !_HAS_CXX17 vvv
(void) _Result;
#endif // _HAS_CXX17
}
public:
template <class... _Valty>
_CONSTEXPR20 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)...);
}
_Ty& _Result = *_Emplace_reallocate(_Mylast, _STD forward<_Valty>(_Val)...);
#if _HAS_CXX17
return *_Emplace_reallocate(_Mylast, _STD forward<_Valty>(_Val)...);
}
template <class... _Valty>
_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;
_STL_INTERNAL_CHECK(_Mylast != _My_data._Myend); // check that we have unused capacity
if constexpr (conjunction_v<is_nothrow_constructible<_Ty, _Valty...>,
_Uses_default_construct<_Alloc, _Ty*, _Valty...>>) {
_ASAN_VECTOR_MODIFY(1);
_Construct_in_place(*_Mylast, _STD forward<_Valty>(_Val)...);
} else {
_ASAN_VECTOR_EXTEND_GUARD(static_cast<size_type>(_Mylast - _My_data._Myfirst) + 1);
_Alty_traits::construct(_Getal(), _Unfancy(_Mylast), _STD forward<_Valty>(_Val)...);
_ASAN_VECTOR_RELEASE_GUARD;
}
_Orphan_range(_Mylast, _Mylast);
_Ty& _Result = *_Mylast;
++_Mylast;
return _Result;
#else // ^^^ _HAS_CXX17 ^^^ // vvv !_HAS_CXX17 vvv
(void) _Result;
#endif // _HAS_CXX17
}
_CONSTEXPR20 void push_back(const _Ty& _Val) { // insert element at end, provide strong guarantee
emplace_back(_Val);
}
_CONSTEXPR20 void push_back(_Ty&& _Val) {
// insert by moving into element at end, provide strong guarantee
emplace_back(_STD move(_Val));
}
template <class... _Valty>
@ -691,6 +870,27 @@ public:
return _Newvec + _Whereoff;
}
public:
template <class... _Valty>
_CONSTEXPR20 decltype(auto) emplace_back(_Valty&&... _Val) {
// insert by perfectly forwarding into element at end, provide strong guarantee
_Ty& _Result = _Emplace_one_at_back(_STD forward<_Valty>(_Val)...);
#if _HAS_CXX17
return _Result;
#else // ^^^ _HAS_CXX17 ^^^ // vvv !_HAS_CXX17 vvv
(void) _Result;
#endif // _HAS_CXX17
}
_CONSTEXPR20 void push_back(const _Ty& _Val) { // insert element at end, provide strong guarantee
emplace_back(_Val);
}
_CONSTEXPR20 void push_back(_Ty&& _Val) {
// insert by moving into element at end, provide strong guarantee
emplace_back(_STD move(_Val));
}
template <class... _Valty>
_CONSTEXPR20 iterator emplace(const_iterator _Where, _Valty&&... _Val) {
// insert by perfectly forwarding _Val at _Where
@ -711,7 +911,9 @@ public:
_Alloc_temporary2<_Alty> _Obj(_Al, _STD forward<_Valty>(_Val)...); // handle aliasing
// after constructing _Obj, provide basic guarantee
_Orphan_range(_Whereptr, _Oldlast);
_ASAN_VECTOR_EXTEND_GUARD(static_cast<size_type>(_Oldlast - _My_data._Myfirst) + 1);
_Alty_traits::construct(_Al, _Unfancy(_Oldlast), _STD move(_Oldlast[-1]));
_ASAN_VECTOR_RELEASE_GUARD;
++_My_data._Mylast;
_Move_backward_unchecked(_Whereptr, _Oldlast - 1, _Oldlast);
*_Whereptr = _STD move(_Obj._Get_value());
@ -794,6 +996,7 @@ public:
const auto _Affected_elements = static_cast<size_type>(_Oldlast - _Whereptr);
_Orphan_range(_Whereptr, _Oldlast);
_ASAN_VECTOR_EXTEND_GUARD(static_cast<size_type>(_Oldlast - _My_data._Myfirst) + _Count);
if (_Count > _Affected_elements) { // new stuff spills off end
_Mylast = _Uninitialized_fill_n(_Oldlast, _Count - _Affected_elements, _Tmp, _Al);
_Mylast = _Uninitialized_move(_Whereptr, _Oldlast, _Mylast, _Al);
@ -803,6 +1006,7 @@ public:
_Move_backward_unchecked(_Whereptr, _Oldlast - _Count, _Oldlast);
_STD fill(_Whereptr, _Whereptr + _Count, _Tmp);
}
_ASAN_VECTOR_RELEASE_GUARD;
}
return _Make_iterator_offset(_Whereoff);
@ -824,10 +1028,10 @@ private:
// For one-at-back, provide strong guarantee.
// Otherwise, provide basic guarantee (despite N4659 26.3.11.5 [vector.modifiers]/1).
// Performance note: except for one-at-back, emplace_back()'s strong guarantee is unnecessary here.
// Performance note: except for one-at-back, _Emplace_one_at_back()'s strong guarantee is unnecessary here.
for (; _First != _Last; ++_First) {
emplace_back(*_First);
_Emplace_one_at_back(*_First);
}
_Orphan_range(_Myfirst + _Whereoff, _Myfirst + _Oldsize);
@ -893,6 +1097,7 @@ private:
const auto _Affected_elements = static_cast<size_type>(_Oldlast - _Whereptr);
_ASAN_VECTOR_EXTEND_GUARD(static_cast<size_type>(_Oldlast - _Oldfirst) + _Count);
if (_Count < _Affected_elements) { // some affected elements must be assigned
_Mylast = _Uninitialized_move(_Oldlast - _Count, _Oldlast, _Oldlast, _Al);
_Move_backward_unchecked(_Whereptr, _Oldlast - _Count, _Oldlast);
@ -945,6 +1150,7 @@ private:
}
_Orphan_range(_Whereptr, _Oldlast);
_ASAN_VECTOR_RELEASE_GUARD;
}
}
@ -977,22 +1183,40 @@ public:
pointer& _Myfirst = _My_data._Myfirst;
pointer& _Mylast = _My_data._Mylast;
constexpr bool _Nothrow_construct =
conjunction_v<is_nothrow_copy_constructible<_Ty>, _Uses_default_construct<_Alloc, _Ty*, const _Ty&>>;
_My_data._Orphan_all();
const auto _Oldcapacity = static_cast<size_type>(_My_data._Myend - _Myfirst);
if (_Newsize > _Oldcapacity) { // reallocate
_Clear_and_reserve_geometric(_Newsize);
_Mylast = _Uninitialized_fill_n(_Myfirst, _Newsize, _Val, _Al);
if constexpr (_Nothrow_construct) {
_Mylast = _Uninitialized_fill_n(_Myfirst, _Newsize, _Val, _Al);
_ASAN_VECTOR_CREATE;
} else {
_ASAN_VECTOR_CREATE_GUARD;
_Mylast = _Uninitialized_fill_n(_Myfirst, _Newsize, _Val, _Al);
}
return;
}
const auto _Oldsize = static_cast<size_type>(_Mylast - _Myfirst);
if (_Newsize > _Oldsize) {
_STD fill(_Myfirst, _Mylast, _Val);
_Mylast = _Uninitialized_fill_n(_Mylast, _Newsize - _Oldsize, _Val, _Al);
if constexpr (_Nothrow_construct) {
_ASAN_VECTOR_MODIFY(static_cast<difference_type>(_Newsize - _Oldsize));
_Mylast = _Uninitialized_fill_n(_Mylast, _Newsize - _Oldsize, _Val, _Al);
} else {
_ASAN_VECTOR_EXTEND_GUARD(static_cast<difference_type>(_Newsize - _Oldsize));
_Mylast = _Uninitialized_fill_n(_Mylast, _Newsize - _Oldsize, _Val, _Al);
_ASAN_VECTOR_RELEASE_GUARD;
}
} else {
const pointer _Newlast = _Myfirst + _Newsize;
_STD fill(_Myfirst, _Newlast, _Val);
_Destroy_range(_Newlast, _Mylast, _Al);
_ASAN_VECTOR_MODIFY(static_cast<difference_type>(_Newsize - _Oldsize));
_Mylast = _Newlast;
}
}
@ -1008,7 +1232,6 @@ private:
_My_data._Orphan_all();
pointer _Next = _Myfirst;
for (; _First != _Last && _Next != _Mylast; ++_First, (void) ++_Next) {
*_Next = *_First;
}
@ -1020,11 +1243,13 @@ private:
// Trim.
_Destroy_range(_Next, _Mylast, _Getal());
_ASAN_VECTOR_MODIFY(static_cast<difference_type>(_Next - _Mylast));
_Mylast = _Next;
// Append.
for (; _First != _Last; ++_First) {
emplace_back(*_First); // performance note: emplace_back()'s strong guarantee is unnecessary here
// performance note: _Emplace_one_at_back()'s strong guarantee is unnecessary here
_Emplace_one_at_back(*_First);
}
}
@ -1038,11 +1263,20 @@ private:
pointer& _Mylast = _My_data._Mylast;
pointer& _Myend = _My_data._Myend;
constexpr bool _Nothrow_construct = conjunction_v<is_nothrow_constructible<_Ty, _Iter_ref_t<_Iter>>,
_Uses_default_construct<_Alloc, _Ty*, _Iter_ref_t<_Iter>>>;
_My_data._Orphan_all();
const auto _Oldcapacity = static_cast<size_type>(_Myend - _Myfirst);
if (_Newsize > _Oldcapacity) {
_Clear_and_reserve_geometric(_Newsize);
_Mylast = _Uninitialized_copy(_First, _Last, _Myfirst, _Al);
if constexpr (_Nothrow_construct) {
_Mylast = _Uninitialized_copy(_First, _Last, _Myfirst, _Al);
_ASAN_VECTOR_CREATE;
} else {
_ASAN_VECTOR_CREATE_GUARD;
_Mylast = _Uninitialized_copy(_First, _Last, _Myfirst, _Al);
}
return;
}
@ -1051,11 +1285,20 @@ private:
// performance note: traversing [_First, _Mid) twice
const _Iter _Mid = _STD next(_First, static_cast<difference_type>(_Oldsize));
_Copy_unchecked(_First, _Mid, _Myfirst);
_Mylast = _Uninitialized_copy(_Mid, _Last, _Mylast, _Al);
if constexpr (_Nothrow_construct) {
_ASAN_VECTOR_MODIFY(static_cast<difference_type>(_Newsize - _Oldsize));
_Mylast = _Uninitialized_copy(_Mid, _Last, _Mylast, _Al);
} else {
_ASAN_VECTOR_EXTEND_GUARD(static_cast<difference_type>(_Newsize - _Oldsize));
_Mylast = _Uninitialized_copy(_Mid, _Last, _Mylast, _Al);
_ASAN_VECTOR_RELEASE_GUARD;
}
} else {
const pointer _Newlast = _Myfirst + _Newsize;
_Copy_unchecked(_First, _Last, _Myfirst);
_Destroy_range(_Newlast, _Mylast, _Al);
_ASAN_VECTOR_MODIFY(static_cast<difference_type>(_Newsize - _Oldsize));
_Mylast = _Newlast;
}
}
@ -1151,6 +1394,7 @@ private:
const pointer _Newlast = _Myfirst + _Newsize;
_Orphan_range(_Newlast, _Mylast);
_Destroy_range(_Newlast, _Mylast, _Al);
_ASAN_VECTOR_MODIFY(static_cast<difference_type>(_Newsize - _Oldsize));
_Mylast = _Newlast;
return;
}
@ -1162,6 +1406,7 @@ private:
return;
}
_ASAN_VECTOR_EXTEND_GUARD(_Newsize - _Oldsize);
const pointer _Oldlast = _Mylast;
if constexpr (is_same_v<_Ty2, _Ty>) {
_Mylast = _Uninitialized_fill_n(_Oldlast, _Newsize - _Oldsize, _Val, _Al);
@ -1169,6 +1414,7 @@ private:
_STL_INTERNAL_STATIC_ASSERT(is_same_v<_Ty2, _Value_init_tag>);
_Mylast = _Uninitialized_value_construct_n(_Oldlast, _Newsize - _Oldsize, _Al);
}
_ASAN_VECTOR_RELEASE_GUARD;
_Orphan_range(_Oldlast, _Oldlast);
}
@ -1252,6 +1498,7 @@ private:
if (_Myfirst) { // destroy and deallocate old array
_Destroy_range(_Myfirst, _Mylast, _Al);
_ASAN_VECTOR_REMOVE;
_Al.deallocate(_Myfirst, static_cast<size_type>(_Myend - _Myfirst));
_Myfirst = nullptr;
@ -1297,6 +1544,7 @@ public:
_Orphan_range(_Mylast - 1, _Mylast);
_Alty_traits::destroy(_Getal(), _Unfancy(_Mylast - 1));
_ASAN_VECTOR_MODIFY(-1);
--_Mylast;
}
@ -1315,6 +1563,7 @@ public:
_Orphan_range(_Whereptr, _Mylast);
_Move_unchecked(_Whereptr + 1, _Mylast, _Whereptr);
_Alty_traits::destroy(_Getal(), _Unfancy(_Mylast - 1));
_ASAN_VECTOR_MODIFY(-1);
--_Mylast;
return iterator(_Whereptr, _STD addressof(_My_data));
}
@ -1337,6 +1586,7 @@ public:
const pointer _Newlast = _Move_unchecked(_Lastptr, _Mylast, _Firstptr);
_Destroy_range(_Newlast, _Mylast, _Getal());
_ASAN_VECTOR_MODIFY(static_cast<difference_type>(_Newlast - _Mylast));
_Mylast = _Newlast;
}
@ -1350,6 +1600,7 @@ public:
_My_data._Orphan_all();
_Destroy_range(_Myfirst, _Mylast, _Getal());
_ASAN_VECTOR_MODIFY(static_cast<difference_type>(_Mylast - _Myfirst));
_Mylast = _Myfirst;
}
@ -1599,12 +1850,14 @@ private:
if (_Myfirst) { // destroy and deallocate old array
_Destroy_range(_Myfirst, _Mylast, _Al);
_ASAN_VECTOR_REMOVE;
_Al.deallocate(_Myfirst, static_cast<size_type>(_Myend - _Myfirst));
}
_Myfirst = _Newvec;
_Mylast = _Newvec + _Newsize;
_Myend = _Newvec + _Newcapacity;
_ASAN_VECTOR_CREATE;
}
_CONSTEXPR20 void _Tidy() noexcept { // free all storage
@ -1618,6 +1871,7 @@ private:
if (_Myfirst) { // destroy and deallocate old array
_Destroy_range(_Myfirst, _Mylast, _Al);
_ASAN_VECTOR_REMOVE;
_Al.deallocate(_Myfirst, static_cast<size_type>(_Myend - _Myfirst));
_Myfirst = nullptr;
@ -1649,6 +1903,7 @@ private:
} else {
static_assert(_Always_false<_Ty>, "Should be unreachable");
}
_ASAN_VECTOR_CREATE;
_Guard._Target = nullptr;
}
@ -1667,11 +1922,20 @@ private:
pointer& _Myfirst = _My_data._Myfirst;
pointer& _Mylast = _My_data._Mylast;
constexpr bool _Nothrow_construct =
conjunction_v<is_nothrow_move_constructible<_Ty>, _Uses_default_construct<_Alloc, _Ty*, _Ty>>;
_My_data._Orphan_all();
const auto _Oldcapacity = static_cast<size_type>(_My_data._Myend - _Myfirst);
if (_Newsize > _Oldcapacity) {
_Clear_and_reserve_geometric(_Newsize);
_Mylast = _Uninitialized_move(_First, _Last, _Myfirst, _Al);
if constexpr (_Nothrow_construct) {
_Mylast = _Uninitialized_move(_First, _Last, _Myfirst, _Al);
_ASAN_VECTOR_CREATE;
} else {
_ASAN_VECTOR_CREATE_GUARD;
_Mylast = _Uninitialized_move(_First, _Last, _Myfirst, _Al);
}
return;
}
@ -1679,11 +1943,20 @@ private:
if (_Newsize > _Oldsize) {
const pointer _Mid = _First + _Oldsize;
_Move_unchecked(_First, _Mid, _Myfirst);
_Mylast = _Uninitialized_move(_Mid, _Last, _Mylast, _Al);
if constexpr (_Nothrow_construct) {
_ASAN_VECTOR_MODIFY(static_cast<difference_type>(_Newsize - _Oldsize));
_Mylast = _Uninitialized_move(_Mid, _Last, _Mylast, _Al);
} else {
_ASAN_VECTOR_EXTEND_GUARD(_Newsize - _Oldsize);
_Mylast = _Uninitialized_move(_Mid, _Last, _Mylast, _Al);
_ASAN_VECTOR_RELEASE_GUARD;
}
} else {
const pointer _Newlast = _Myfirst + _Newsize;
_Move_unchecked(_First, _Last, _Myfirst);
_Destroy_range(_Newlast, _Mylast, _Al);
_ASAN_VECTOR_MODIFY(static_cast<difference_type>(_Newsize - _Oldsize));
_Mylast = _Newlast;
}
}
@ -3150,6 +3423,15 @@ _NODISCARD _CONSTEXPR20 _Iter_diff_t<_VbIt> _Count_vbool(_VbIt _First, const _Vb
return _Count;
});
}
#undef _ASAN_VECTOR_MODIFY
#undef _ASAN_VECTOR_REMOVE
#undef _ASAN_VECTOR_CREATE
#undef _ASAN_VECTOR_CREATE_GUARD
#undef _ASAN_VECTOR_EXTEND_GUARD
#undef _ASAN_VECTOR_RELEASE_GUARD
#undef _ACTIVATE_VECTOR_ANNOTATION
#undef _INSERT_VECTOR_ANNOTATION
_STD_END
#pragma pop_macro("new")

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

@ -774,6 +774,9 @@ _NODISCARD constexpr allocation_result<typename allocator_traits<_Alloc>::pointe
#endif // __cpp_lib_concepts
#endif // _HAS_CXX23
// The number of user bytes a single byte of ASAN shadow memory can track.
_INLINE_VAR constexpr size_t _Asan_granularity = 8;
template <class _Ty>
class allocator {
public:
@ -857,6 +860,8 @@ public:
return static_cast<size_t>(-1) / sizeof(_Ty);
}
#endif // _HAS_DEPRECATED_ALLOCATOR_MEMBERS
static constexpr size_t _Minimum_allocation_alignment = _Asan_granularity;
};
template <>

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

@ -0,0 +1,14 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!--
Copyright (c) Microsoft Corporation.
SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-->
<Import Project="$(MSBuildThisFileDirectory)..\..\..\..\crt-common.settings.targets" />
<ItemGroup Condition="'$(BuildExePhase)' == '1'">
<ProjectFile Include="stl_asan.nativeproj" />
</ItemGroup>
<Import Project="$(_NTDRIVE)$(_NTROOT)\tools\Microsoft.DevDiv.Traversal.targets" />
</Project>

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

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="Dogfood">
<!--
Copyright (c) Microsoft Corporation.
SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-->
<ItemGroup>
<BuildFiles Include="
$(CrtRoot)\github\stl\src\asan.cpp;
">
<BuildAs>nativecpp</BuildAs>
</BuildFiles>
</ItemGroup>
</Project>

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

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="Dogfood">
<!--
Copyright (c) Microsoft Corporation.
SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-->
<PropertyGroup>
<Arm64X>true</Arm64X>
</PropertyGroup>
<Import Project="$(MSBuildThisFileDirectory)stl_asan.settings.targets"/>
</Project>

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

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="Dogfood">
<!--
Copyright (c) Microsoft Corporation.
SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-->
<PropertyGroup>
<FinalBinary>p_stl_asan</FinalBinary>
<TargetType>LIBRARY</TargetType>
<Arm64CombinedPdb>true</Arm64CombinedPdb>
</PropertyGroup>
<Import Project="$(MSBuildThisFileDirectory)..\..\..\..\crt_build.settings.targets"/>
<PropertyGroup>
<OutputName>stl_asan</OutputName>
</PropertyGroup>
<PropertyGroup>
<ClProgramDataBaseFileName>$(OutputLibPdbPath)$(OutputName)$(PdbVerName).pdb</ClProgramDataBaseFileName>
</PropertyGroup>
<Import Project="$(MSBuildThisFileDirectory)stl_asan.files.settings.targets"/>
<Import Project="$(VCToolsRootPath)\crt\crt_build.targets"/>
<Target Name="GetBaseAddress"/>
</Project>

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

@ -27,7 +27,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
<PropertyGroup>
<ClProgramDataBaseFileName>$(OutputLibPdbPath)$(OutputName)$(PdbVerName).pdb</ClProgramDataBaseFileName>
<ClDefines Condition="'$(DependsOnConcRT)' == 'true'">$(ClDefines);_STL_CONCRT_SUPPORT</ClDefines>
<ClDefines>$(ClDefines);_VCRT_ALLOW_INTERNALS</ClDefines>
<ClDefines>$(ClDefines);_VCRT_ALLOW_INTERNALS;_ANNOTATE_VECTOR</ClDefines>
</PropertyGroup>
<Import Project="$(MSBuildThisFileDirectory)\stl.files.settings.targets"/>

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

@ -160,6 +160,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
<!-- Objs that exist in both libcpmt[d][01].lib and msvcprt[d].lib
(controlled by IncludeInLink and IncludeInImportLib). -->
<BuildFiles Include="
$(CrtRoot)\github\stl\src\asan_noop.cpp;
$(CrtRoot)\github\stl\src\filesystem.cpp;
$(CrtRoot)\github\stl\src\format.cpp;
$(CrtRoot)\github\stl\src\locale0_implib.cpp;

8
stl/src/asan.cpp Normal file
Просмотреть файл

@ -0,0 +1,8 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
namespace std {
extern "C" {
extern const bool _Asan_vector_should_annotate = true;
}
} // namespace std

9
stl/src/asan_noop.cpp Normal file
Просмотреть файл

@ -0,0 +1,9 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
extern "C" {
extern const bool _Asan_vector_should_annotate_default = false;
void __cdecl __sanitizer_annotate_contiguous_container_default(
const void*, const void*, const void*, const void*) noexcept {}
}

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

@ -186,6 +186,7 @@ tests\GH_001638_dllexport_derived_classes
tests\GH_001850_clog_tied_to_cout
tests\GH_001858_iostream_exception
tests\GH_001914_cached_position
tests\GH_002030_asan_annotate_vector
tests\GH_002039_byte_is_not_trivially_swappable
tests\GH_002058_debug_iterator_race
tests\GH_002120_streambuf_seekpos_and_seekoff

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

@ -0,0 +1,60 @@
# 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.
# TRANSITION, VSO-1350252
# Due to a bug in the ASan libs using ASan with /MD or /MT requires IDL==0 and using /MDd or /MTd requires IDL==2.
# clang-cl does not currently support targeting /MDd or /MTd.
RUNALL_INCLUDE ..\prefix.lst
RUNALL_CROSSLIST
PM_CL="/Zi /wd4611 /w14640 /Zc:threadSafeInit-" PM_LINK="/debug"
RUNALL_CROSSLIST
PM_CL="-fsanitize=address /BE /c /EHsc /MD /std:c++14 /fno-sanitize-address-vcasan-lib"
PM_CL="-fsanitize=address /BE /c /EHsc /MDd /std:c++17 /permissive- /fno-sanitize-address-vcasan-lib"
PM_CL="-fsanitize=address /BE /c /EHsc /MT /std:c++20 /permissive- /fno-sanitize-address-vcasan-lib"
PM_CL="-fsanitize=address /BE /c /EHsc /MTd /std:c++latest /permissive- /fno-sanitize-address-vcasan-lib"
PM_CL="-fsanitize=address /EHsc /MD /std:c++14 /fno-sanitize-address-vcasan-lib"
PM_CL="-fsanitize=address /EHsc /MD /std:c++17 /fno-sanitize-address-vcasan-lib"
PM_CL="-fsanitize=address /EHsc /MD /std:c++20 /fno-sanitize-address-vcasan-lib"
PM_CL="-fsanitize=address /EHsc /MD /std:c++latest /permissive- /Zc:char8_t- /Zc:preprocessor /fno-sanitize-address-vcasan-lib"
PM_CL="-fsanitize=address /EHsc /MD /std:c++latest /permissive- /Zc:noexceptTypes- /fno-sanitize-address-vcasan-lib"
PM_CL="-fsanitize=address /EHsc /MDd /std:c++14 /fp:except /Zc:preprocessor /fno-sanitize-address-vcasan-lib"
PM_CL="-fsanitize=address /EHsc /MDd /std:c++17 /permissive- /fno-sanitize-address-vcasan-lib"
PM_CL="-fsanitize=address /EHsc /MDd /std:c++20 /permissive- /fno-sanitize-address-vcasan-lib"
PM_CL="-fsanitize=address /EHsc /MDd /std:c++latest /permissive- /Zc:wchar_t- /fno-sanitize-address-vcasan-lib"
PM_CL="-fsanitize=address /EHsc /MDd /std:c++latest /permissive- /fno-sanitize-address-vcasan-lib"
PM_CL="-fsanitize=address /EHsc /MT /std:c++latest /permissive- /analyze:only /analyze:autolog- /fno-sanitize-address-vcasan-lib"
PM_CL="-fsanitize=address /EHsc /MT /std:c++latest /permissive- /fno-sanitize-address-vcasan-lib"
PM_CL="-fsanitize=address /EHsc /MTd /std:c++latest /permissive /fno-sanitize-address-vcasan-lib"
PM_CL="-fsanitize=address /EHsc /MTd /std:c++latest /permissive- /analyze:only /analyze:autolog- /fno-sanitize-address-vcasan-lib"
PM_CL="-fsanitize=address /EHsc /MTd /std:c++latest /permissive- /fp:strict /fno-sanitize-address-vcasan-lib"
PM_CL="-fsanitize=address /EHsc /MTd /std:c++latest /permissive- /fno-sanitize-address-vcasan-lib"
PM_CL="-fsanitize=address /Za /EHsc /MD /std:c++latest /permissive- /fno-sanitize-address-vcasan-lib"
PM_CL="-fsanitize=address /Za /EHsc /MDd /std:c++latest /permissive- /fno-sanitize-address-vcasan-lib"
PM_CL="/D_ANNOTATE_VECTOR /BE /c /EHsc /MD /std:c++14 /fno-sanitize-address-vcasan-lib"
PM_CL="/D_ANNOTATE_VECTOR /BE /c /EHsc /MDd /std:c++17 /permissive- /fno-sanitize-address-vcasan-lib"
PM_CL="/D_ANNOTATE_VECTOR /BE /c /EHsc /MT /std:c++20 /permissive- /fno-sanitize-address-vcasan-lib"
PM_CL="/D_ANNOTATE_VECTOR /BE /c /EHsc /MTd /std:c++latest /permissive- /fno-sanitize-address-vcasan-lib"
PM_CL="/D_ANNOTATE_VECTOR /EHsc /MD /std:c++14 /fno-sanitize-address-vcasan-lib"
PM_CL="/D_ANNOTATE_VECTOR /EHsc /MD /std:c++17 /fno-sanitize-address-vcasan-lib"
PM_CL="/D_ANNOTATE_VECTOR /EHsc /MD /std:c++20 /fno-sanitize-address-vcasan-lib"
PM_CL="/D_ANNOTATE_VECTOR /EHsc /MD /std:c++latest /permissive- /Zc:char8_t- /Zc:preprocessor /fno-sanitize-address-vcasan-lib"
PM_CL="/D_ANNOTATE_VECTOR /EHsc /MD /std:c++latest /permissive- /Zc:noexceptTypes- /fno-sanitize-address-vcasan-lib"
PM_CL="/D_ANNOTATE_VECTOR /EHsc /MDd /std:c++14 /fp:except /Zc:preprocessor /fno-sanitize-address-vcasan-lib"
PM_CL="/D_ANNOTATE_VECTOR /EHsc /MDd /std:c++17 /permissive- /fno-sanitize-address-vcasan-lib"
PM_CL="/D_ANNOTATE_VECTOR /EHsc /MDd /std:c++20 /permissive- /fno-sanitize-address-vcasan-lib"
PM_CL="/D_ANNOTATE_VECTOR /EHsc /MDd /std:c++latest /permissive- /Zc:wchar_t- /fno-sanitize-address-vcasan-lib"
PM_CL="/D_ANNOTATE_VECTOR /EHsc /MDd /std:c++latest /permissive- /fno-sanitize-address-vcasan-lib"
PM_CL="/D_ANNOTATE_VECTOR /EHsc /MT /std:c++latest /permissive- /analyze:only /analyze:autolog- /fno-sanitize-address-vcasan-lib"
PM_CL="/D_ANNOTATE_VECTOR /EHsc /MT /std:c++latest /permissive- /fno-sanitize-address-vcasan-lib"
PM_CL="/D_ANNOTATE_VECTOR /EHsc /MTd /std:c++latest /permissive /fno-sanitize-address-vcasan-lib"
PM_CL="/D_ANNOTATE_VECTOR /EHsc /MTd /std:c++latest /permissive- /analyze:only /analyze:autolog- /fno-sanitize-address-vcasan-lib"
PM_CL="/D_ANNOTATE_VECTOR /EHsc /MTd /std:c++latest /permissive- /fp:strict /fno-sanitize-address-vcasan-lib"
PM_CL="/D_ANNOTATE_VECTOR /EHsc /MTd /std:c++latest /permissive- /fno-sanitize-address-vcasan-lib"
PM_CL="/D_ANNOTATE_VECTOR /Za /EHsc /MD /std:c++latest /permissive- /fno-sanitize-address-vcasan-lib"
PM_CL="/D_ANNOTATE_VECTOR /Za /EHsc /MDd /std:c++latest /permissive- /fno-sanitize-address-vcasan-lib"
# TRANSITION, clang-cl does not support /alternatename so we cannot test /D_ANNOTATE_VECTOR without -fsanitize=address
PM_COMPILER="clang-cl" PM_CL="-fsanitize=address -fno-ms-compatibility -fno-delayed-template-parsing /EHsc /MD /std:c++14"
PM_COMPILER="clang-cl" PM_CL="-fsanitize=address -fno-ms-compatibility -fno-delayed-template-parsing /EHsc /MD /std:c++17"
PM_COMPILER="clang-cl" PM_CL="-fsanitize=address -fno-ms-compatibility -fno-delayed-template-parsing /EHsc /MT /std:c++latest /permissive-"
PM_COMPILER="clang-cl" PM_CL="-fsanitize=address -fno-ms-compatibility -fno-delayed-template-parsing /EHsc /MT /std:c++latest /permissive- /D_HAS_CXX23 /fp:strict"

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

@ -0,0 +1,917 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// REQUIRES: x64 || x86
#include <cassert>
#include <cstddef>
#include <iostream>
#include <iterator>
#include <memory>
#include <type_traits>
#include <utility>
#include <vector>
using namespace std;
#ifndef __SANITIZE_ADDRESS__
#if defined(__clang__) && defined(__has_feature)
#if __has_feature(address_sanitizer)
#define __SANITIZE_ADDRESS__
#endif
#endif
#endif
#ifdef __SANITIZE_ADDRESS__
extern "C" {
void* __sanitizer_contiguous_container_find_bad_address(const void* beg, const void* mid, const void* end) noexcept;
void __asan_describe_address(void*) noexcept;
}
#endif // ASan instrumentation enabled
struct non_trivial_can_throw {
non_trivial_can_throw() {}
};
struct non_trivial_cannot_throw {
non_trivial_cannot_throw() noexcept {}
};
struct throw_on_construction {
throw_on_construction() {
throw 0;
}
explicit throw_on_construction(bool should_throw) {
if (should_throw) {
throw 0;
}
}
throw_on_construction(const throw_on_construction&) {
throw 0;
}
[[noreturn]] throw_on_construction& operator=(const throw_on_construction&) {
throw 0;
}
};
struct throw_on_copy {
throw_on_copy() = default;
throw_on_copy(const throw_on_copy&) {
throw 0;
}
throw_on_copy(throw_on_copy&&) {}
[[noreturn]] throw_on_copy& operator=(const throw_on_copy&) {
throw 0;
}
};
template <class T, int N>
class input_iterator_tester {
private:
T data[N] = {};
public:
class iterator {
private:
T* curr;
public:
using iterator_category = input_iterator_tag;
using value_type = T;
using difference_type = ptrdiff_t;
using pointer = void;
using reference = T&;
explicit iterator(T* start) : curr(start) {}
reference operator*() const {
return *curr;
}
iterator& operator++() {
++curr;
return *this;
}
iterator operator++(int) {
auto tmp = *this;
++curr;
return tmp;
}
bool operator==(const iterator& that) const {
return curr == that.curr;
}
bool operator!=(const iterator& that) const {
return !(*this == that);
}
};
iterator begin() {
return iterator(data);
}
iterator end() {
return iterator(data + N);
}
};
template <class T, class Alloc>
bool verify_vector(vector<T, Alloc>& vec) {
#ifdef __SANITIZE_ADDRESS__
size_t buffer_bytes = vec.capacity() * sizeof(T);
void* buffer = vec.data();
void* aligned_start = align(8, 1, buffer, buffer_bytes);
if (!aligned_start) {
return true;
}
void* mid = vec.data() + vec.size();
mid = mid > aligned_start ? mid : aligned_start;
void* bad_address =
__sanitizer_contiguous_container_find_bad_address(aligned_start, mid, vec.data() + vec.capacity());
if (bad_address == nullptr) {
return true;
}
if (bad_address < mid) {
cout << bad_address << " was marked as poisoned when it should not be." << endl;
} else {
cout << bad_address << " was not marked as poisoned when it should be." << endl;
}
cout << "Vector State:" << endl;
cout << " begin: " << buffer << endl;
cout << " aligned begin: " << aligned_start << endl;
cout << " last: " << reinterpret_cast<void*>(vec.data() + vec.size()) << endl;
cout << " end: " << reinterpret_cast<void*>(vec.data() + vec.capacity()) << endl;
__asan_describe_address(bad_address);
return false;
#else // ^^^ ASan instrumentation enabled ^^^ // vvv ASan instrumentation disabled vvv
(void) vec;
return true;
#endif // Asan instrumentation disabled
}
// Note: This class does not satisfy all the allocator requirements but is sufficient for this test.
template <class T, class Pocma, class Stateless>
struct custom_test_allocator {
using value_type = T;
using propagate_on_container_move_assignment = Pocma;
using is_always_equal = Stateless;
};
template <class T1, class T2, class Pocma, class Stateless>
constexpr bool operator==(
const custom_test_allocator<T1, Pocma, Stateless>&, const custom_test_allocator<T2, Pocma, Stateless>&) noexcept {
return Stateless::value;
}
template <class T1, class T2, class Pocma, class Stateless>
constexpr bool operator!=(
const custom_test_allocator<T1, Pocma, Stateless>&, const custom_test_allocator<T2, Pocma, Stateless>&) noexcept {
return !Stateless::value;
}
template <class T, class Pocma = true_type, class Stateless = true_type>
struct aligned_allocator : custom_test_allocator<T, Pocma, Stateless> {
static constexpr size_t _Minimum_allocation_alignment = 8;
aligned_allocator() = default;
template <class U>
constexpr aligned_allocator(const aligned_allocator<U, Pocma, Stateless>&) noexcept {}
T* allocate(size_t n) {
return new T[n];
}
void deallocate(T* p, size_t) noexcept {
delete[] p;
}
};
template <class T, class Pocma = true_type, class Stateless = true_type>
struct explicit_allocator : custom_test_allocator<T, Pocma, Stateless> {
static constexpr size_t _Minimum_allocation_alignment = alignof(T);
explicit_allocator() = default;
template <class U>
constexpr explicit_allocator(const explicit_allocator<U, Pocma, Stateless>&) 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 <class T, class Pocma = true_type, class Stateless = true_type>
struct implicit_allocator : custom_test_allocator<T, Pocma, Stateless> {
implicit_allocator() = default;
template <class U>
constexpr implicit_allocator(const implicit_allocator<U, Pocma, Stateless>&) 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 <class Alloc>
void test_push_pop() {
using T = typename Alloc::value_type;
vector<T, Alloc> v;
assert(verify_vector(v));
v.push_back(T());
assert(verify_vector(v));
v.pop_back();
assert(verify_vector(v));
}
template <class Alloc, int Size = 1024, int Stride = 128>
void test_reserve_shrink() {
using T = typename Alloc::value_type;
vector<T, Alloc> v;
assert(verify_vector(v));
v.reserve(Size);
assert(verify_vector(v));
for (int i = 0; i < Size; i += Stride) {
for (int j = 0; j < Stride && j + i < Size; ++j) {
v.push_back(T());
}
assert(verify_vector(v));
}
v.push_back(T());
assert(verify_vector(v));
for (int i = 0; i < Size; i += Stride) {
for (int j = 0; j < Stride && j + i < Size; ++j) {
v.pop_back();
}
v.shrink_to_fit();
assert(verify_vector(v));
}
v.pop_back();
assert(verify_vector(v));
v.shrink_to_fit();
assert(verify_vector(v));
}
template <class Alloc>
void test_emplace_pop() {
using T = typename Alloc::value_type;
vector<T, Alloc> 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 <class Alloc>
void test_move_assign() {
using T = typename Alloc::value_type;
vector<T, Alloc> v1;
vector<T, Alloc> 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 <class Alloc>
void test_copy_assign() {
using T = typename Alloc::value_type;
vector<T, Alloc> v1;
vector<T, Alloc> 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 <class Alloc, int N = 128>
void test_constructors() {
using T = typename Alloc::value_type;
Alloc al = Alloc();
vector<T, Alloc> v1;
vector<T, Alloc> v2(al);
vector<T, Alloc> v3(N, T());
vector<T, Alloc> v4(N);
assert(verify_vector(v1));
assert(verify_vector(v2));
assert(verify_vector(v3));
assert(verify_vector(v4));
vector<T, Alloc> v5(v3.begin(), v3.end());
vector<T, Alloc> v6(v3);
vector<T, Alloc> v7(v3, al);
assert(verify_vector(v5));
assert(verify_vector(v6));
assert(verify_vector(v7));
vector<T, Alloc> v8(move(v3));
vector<T, Alloc> v9(move(v4), al);
assert(verify_vector(v8));
assert(verify_vector(v9));
vector<T, Alloc> v10({T(), T()});
assert(verify_vector(v10));
}
template <class Alloc, int N = 128>
void test_insert_n() {
using T = typename Alloc::value_type;
vector<T, Alloc> 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 <class Alloc, int N = 128>
void test_insert_range() {
using T = typename Alloc::value_type;
vector<T, Alloc> v1(1);
vector<T, Alloc> v2(N);
input_iterator_tester<T, N> t;
v1.insert(v1.begin(), v2.begin(), v2.end());
assert(verify_vector(v1));
v1.insert(v1.end(), v2.begin(), v2.end());
assert(verify_vector(v1));
v1.insert(v1.begin() + N, v2.begin(), v2.end());
assert(verify_vector(v1));
v1.insert(v1.begin(), t.begin(), t.end());
assert(verify_vector(v1));
v1.insert(v1.end(), t.begin(), t.end());
assert(verify_vector(v1));
v1.insert(v1.begin() + N, t.begin(), t.end());
assert(verify_vector(v1));
}
template <class Alloc, int N = 128>
void test_assign() {
using T = typename Alloc::value_type;
vector<T, Alloc> v1(1);
vector<T, Alloc> v2(N + 1);
vector<T, Alloc> v3(N + 2);
input_iterator_tester<T, N + 2> t1;
input_iterator_tester<T, N + 3> t2;
v1.assign(N, T());
assert(verify_vector(v1));
v1.assign(v2.begin(), v2.end());
assert(verify_vector(v1));
v1.assign(v3.begin(), v3.end());
assert(verify_vector(v1));
v1.assign(t1.begin(), t1.end());
assert(verify_vector(v1));
v1.assign(t2.begin(), t2.end());
assert(verify_vector(v1));
v1.assign(t1.begin(), t1.end());
assert(verify_vector(v1));
v1.assign(v3.begin(), v3.end());
assert(verify_vector(v1));
v1.assign(v2.begin(), v2.end());
assert(verify_vector(v1));
v1.assign(N, T());
assert(verify_vector(v1));
vector<T, Alloc> v4;
v4.assign({T()});
assert(verify_vector(v4));
v4.assign({T(), T()});
assert(verify_vector(v4));
v4.assign({T()});
assert(verify_vector(v4));
}
template <class Alloc, int N = 128>
void test_resize() {
using T = typename Alloc::value_type;
vector<T, Alloc> v;
v.resize(N, T());
assert(verify_vector(v));
v.resize(1, T());
assert(verify_vector(v));
}
void test_push_back_throw() {
{
vector<throw_on_construction> v;
v.reserve(1);
throw_on_construction t(false);
try {
v.push_back(t);
assert(false);
} catch (int) {
assert(verify_vector(v));
}
}
{
vector<throw_on_construction> v;
throw_on_construction t(false);
try {
v.push_back(t);
assert(false);
} catch (int) {
assert(verify_vector(v));
}
}
{
vector<throw_on_construction> v;
v.reserve(1);
try {
v.push_back(throw_on_construction(false));
assert(false);
} catch (int) {
assert(verify_vector(v));
}
}
{
vector<throw_on_construction> v;
try {
v.push_back(throw_on_construction(false));
assert(false);
} catch (int) {
assert(verify_vector(v));
}
}
}
void test_emplace_back_throw() {
{
vector<throw_on_construction> v;
v.reserve(1);
try {
v.emplace_back(true);
assert(false);
} catch (int) {
assert(verify_vector(v));
}
}
{
vector<throw_on_construction> v;
try {
v.emplace_back(true);
assert(false);
} catch (int) {
assert(verify_vector(v));
}
}
}
void test_insert_range_throw() {
{
vector<throw_on_construction> v;
v.reserve(4);
v.emplace_back(false);
v.emplace_back(false);
try {
v.insert(v.begin(), {throw_on_construction(false), throw_on_construction(false)});
assert(false);
} catch (int) {
assert(verify_vector(v));
}
}
{
vector<throw_on_construction> v;
v.reserve(2);
v.emplace_back(false);
v.emplace_back(false);
try {
v.insert(v.begin(), {throw_on_construction(false), throw_on_construction(false)});
assert(false);
} catch (int) {
assert(verify_vector(v));
}
}
{
vector<throw_on_construction> v;
v.reserve(2);
try {
v.insert(v.end(), {throw_on_construction(false), throw_on_construction(false)});
assert(false);
} catch (int) {
assert(verify_vector(v));
}
}
{
vector<throw_on_construction> v;
try {
v.insert(v.end(), {throw_on_construction(false), throw_on_construction(false)});
assert(false);
} catch (int) {
assert(verify_vector(v));
}
}
}
void test_insert_throw() {
{
vector<throw_on_construction> v;
v.reserve(3);
v.emplace_back(false);
v.emplace_back(false);
try {
v.insert(v.begin(), throw_on_construction(false));
assert(false);
} catch (int) {
assert(verify_vector(v));
}
}
{
vector<throw_on_construction> v;
v.reserve(2);
v.emplace_back(false);
v.emplace_back(false);
try {
v.insert(v.begin(), throw_on_construction(false));
assert(false);
} catch (int) {
assert(verify_vector(v));
}
}
{
vector<throw_on_construction> v;
v.reserve(1);
try {
v.insert(v.end(), throw_on_construction(false));
assert(false);
} catch (int) {
assert(verify_vector(v));
}
}
{
vector<throw_on_construction> v;
try {
v.insert(v.end(), throw_on_construction(false));
assert(false);
} catch (int) {
assert(verify_vector(v));
}
}
}
void test_emplace_throw() {
{
vector<throw_on_construction> v;
v.reserve(3);
v.emplace_back(false);
v.emplace_back(false);
try {
v.emplace(v.begin(), false);
assert(false);
} catch (int) {
assert(verify_vector(v));
}
}
{
vector<throw_on_construction> v;
v.reserve(2);
v.emplace_back(false);
v.emplace_back(false);
try {
v.emplace(v.begin(), true);
assert(false);
} catch (int) {
assert(verify_vector(v));
}
}
{
vector<throw_on_construction> v;
v.reserve(1);
try {
v.emplace(v.end(), true);
assert(false);
} catch (int) {
assert(verify_vector(v));
}
}
{
vector<throw_on_construction> v;
try {
v.emplace(v.end(), true);
assert(false);
} catch (int) {
assert(verify_vector(v));
}
}
}
void test_resize_throw() {
{
vector<throw_on_construction> v;
v.reserve(2);
v.emplace_back(false);
try {
v.resize(2);
assert(false);
} catch (int) {
assert(verify_vector(v));
}
}
{
vector<throw_on_construction> v;
v.reserve(1);
v.emplace_back(false);
try {
v.resize(2);
assert(false);
} catch (int) {
assert(verify_vector(v));
}
}
{
vector<throw_on_copy> v;
v.reserve(2);
v.push_back(throw_on_copy());
try {
v.resize(2, throw_on_copy());
assert(false);
} catch (int) {
assert(verify_vector(v));
}
}
{
vector<throw_on_copy> v;
v.reserve(1);
v.push_back(throw_on_copy());
try {
v.resize(2, throw_on_copy());
assert(false);
} catch (int) {
assert(verify_vector(v));
}
}
}
void test_insert_n_throw() {
{
vector<throw_on_construction> v;
v.reserve(2);
v.emplace_back(false);
try {
v.insert(v.begin(), 2, throw_on_construction(false));
assert(false);
} catch (int) {
assert(verify_vector(v));
}
}
{
vector<throw_on_construction> v;
v.reserve(2);
v.emplace_back(false);
try {
v.insert(v.end(), 2, throw_on_construction(false));
assert(false);
} catch (int) {
assert(verify_vector(v));
}
}
{
vector<throw_on_construction> v;
v.reserve(1);
v.emplace_back(false);
try {
v.insert(v.begin(), 2, throw_on_construction(false));
assert(false);
} catch (int) {
assert(verify_vector(v));
}
}
{
vector<throw_on_construction> v;
v.reserve(1);
v.emplace_back(false);
try {
v.insert(v.end(), 2, throw_on_construction(false));
assert(false);
} catch (int) {
assert(verify_vector(v));
}
}
{
vector<throw_on_copy> v;
v.reserve(2);
v.push_back(throw_on_copy());
try {
v.insert(v.begin(), 2, throw_on_copy());
assert(false);
} catch (int) {
assert(verify_vector(v));
}
}
{
vector<throw_on_copy> v;
v.reserve(2);
v.push_back(throw_on_copy());
try {
v.insert(v.end(), 2, throw_on_copy());
assert(false);
} catch (int) {
assert(verify_vector(v));
}
}
{
vector<throw_on_copy> v;
v.reserve(1);
v.push_back(throw_on_copy());
try {
v.insert(v.begin(), 2, throw_on_copy());
assert(false);
} catch (int) {
assert(verify_vector(v));
}
}
{
vector<throw_on_copy> v;
v.reserve(1);
v.push_back(throw_on_copy());
try {
v.insert(v.end(), 2, throw_on_copy());
assert(false);
} catch (int) {
assert(verify_vector(v));
}
}
}
template <class Alloc>
void run_tests() {
test_push_pop<Alloc>();
test_reserve_shrink<Alloc>();
test_emplace_pop<Alloc>();
test_move_assign<Alloc>();
test_copy_assign<Alloc>();
test_constructors<Alloc>();
test_insert_n<Alloc>();
test_insert_range<Alloc>();
test_assign<Alloc>();
test_resize<Alloc>();
}
template <class T, template <class, class, class> class AllocT>
void run_custom_allocator_matrix() {
run_tests<AllocT<T, true_type, true_type>>();
run_tests<AllocT<T, true_type, false_type>>();
run_tests<AllocT<T, false_type, true_type>>();
run_tests<AllocT<T, false_type, false_type>>();
}
template <class T>
void run_allocator_matrix() {
run_tests<allocator<T>>();
run_custom_allocator_matrix<T, aligned_allocator>();
run_custom_allocator_matrix<T, explicit_allocator>();
run_custom_allocator_matrix<T, implicit_allocator>();
}
int main() {
// Do some work even when we aren't instrumented
run_allocator_matrix<char>();
#ifdef __SANITIZE_ADDRESS__
run_allocator_matrix<int>();
run_allocator_matrix<double>();
run_allocator_matrix<non_trivial_can_throw>();
run_allocator_matrix<non_trivial_cannot_throw>();
// TRANSITION, LLVM-35365
#ifndef __clang__
test_push_back_throw();
test_emplace_back_throw();
test_insert_range_throw();
test_insert_throw();
test_emplace_throw();
test_resize_throw();
test_insert_n_throw();
#endif // !__clang__
#endif // ASan instrumentation enabled
}

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

@ -15,6 +15,7 @@ import itertools
import os
import re
import shutil
import sys
import lit
@ -173,13 +174,14 @@ class STLTestFormat:
if TestType.COMPILE in test.testType:
cmd = [test.cxx, '-c', test.getSourcePath(), *test.flags, *test.compileFlags]
yield TestStep(cmd, shared.execDir, shared.env, shouldFail)
elif TestType.LINK in test.testType:
elif TestType.LINK in test.testType or \
('clang' in test.config.available_features and 'asan' in test.config.available_features):
objFile = tmpBase + '.o'
cmd = [test.cxx, '-c', test.getSourcePath(), *test.flags, *test.compileFlags, '-Fo' + objFile]
yield TestStep(cmd, shared.execDir, shared.env, False)
exeFile = tmpBase + '.exe'
cmd = [test.cxx, objFile, *test.flags, '-Fe' + exeFile, '-link', *test.linkFlags]
shared.execFile = tmpBase + '.exe'
cmd = ['link.exe', objFile, *test.flags, '-out:' + shared.execFile, *test.linkFlags]
yield TestStep(cmd, shared.execDir, shared.env, shouldFail)
elif TestType.RUN in test.testType:
shared.execFile = tmpBase + '.exe'
@ -239,7 +241,10 @@ class STLTestFormat:
return (lit.Test.PASS, '')
except Exception as e:
litConfig.error(repr(e))
_, _, exception_traceback = sys.exc_info()
filename = exception_traceback.tb_frame.f_code.co_filename
line_number = exception_traceback.tb_lineno
litConfig.error(repr(e) + ' at ' + filename + ':' + str(line_number))
class LibcxxTestFormat(STLTestFormat):

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

@ -53,7 +53,7 @@ class STLTest(Test):
return result
self._parseTest()
self._parseFlags()
self._parseFlags(litConfig)
missing_required_features = self.getMissingRequiredFeatures()
if missing_required_features:
@ -204,6 +204,8 @@ class STLTest(Test):
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 @@ class STLTest(Test):
return Result(UNSUPPORTED, 'clang targeting arm is not supported')
elif (targetArch == 'arm64'.casefold()):
self.compileFlags.append('--target=arm64-pc-windows-msvc')
elif ('nvcc'.casefold() in os.path.basename(cxx).casefold()):
self._addCustomFeature('nvcc')
if ('nvcc'.casefold() in os.path.basename(cxx).casefold()):
# nvcc only supports targeting x64
self.requires.append('x64')
else:
self._addCustomFeature('cl')
self.cxx = os.path.normpath(cxx)
return None
@ -226,8 +231,9 @@ class STLTest(Test):
for action in actions:
action.applyTo(self.config)
def _parseFlags(self):
def _parseFlags(self, litConfig):
foundStd = False
foundCRT = False
for flag in chain(self.flags, self.compileFlags, self.linkFlags):
if flag[1:5] == 'std:':
foundStd = True
@ -251,13 +257,41 @@ class STLTest(Test):
self.requires.append('arch_ia32') # available for x86, see features.py
elif flag[1:] == 'arch:VFPv4':
self.requires.append('arch_vfpv4') # available for arm, see features.py
elif flag[1:] == 'fsanitize=address':
self._addCustomFeature('asan')
elif flag[1:] == 'MDd':
self._addCustomFeature('MDd')
self._addCustomFeature('debug_CRT')
self._addCustomFeature('dynamic_CRT')
foundCRT = True
elif flag[1:] == 'MD':
self._addCustomFeature('MD')
self._addCustomFeature('dynamic_CRT')
foundCRT = True
elif flag[1:] == 'MTd':
self._addCustomFeature('MTd')
self._addCustomFeature('debug_CRT')
self._addCustomFeature('static_CRT')
foundCRT = True
elif flag[1:] == 'MT':
self._addCustomFeature('MT')
self._addCustomFeature('static_CRT')
foundCRT = True
if not foundStd:
self._addCustomFeature('c++14')
if not foundCRT:
self._addCustomFeature('MT')
self._addCustomFeature('static_CRT')
self._addCustomFeature('non-lockfree-atomics') # we always support non-lockfree-atomics
self._addCustomFeature('is-lockfree-runtime-function') # Ditto
# clang doesn't know how to link in the VS version of the asan runtime automatically
if 'asan' in self.config.available_features and 'clang' in self.config.available_features:
self.linkFlags.append("/INFERASANLIBS")
class LibcxxTest(STLTest):
def getTestName(self):