This revision is a part of a series of patches extending AddressSanitizer C++ container overflow detection capabilities by adding annotations, similar to those existing in std::vector, to std::string and `std::deque` collections. These changes allow ASan to detect cases when the instrumented program accesses memory which is internally allocated by the collection but is still not in-use (accesses before or after the stored elements for `std::deque`, or between the size and capacity bounds for `std::string`).
The motivation for the research and those changes was a bug, found by Trail of Bits, in a real code where an out-of-bounds read could happen as two strings were compared via a std::equals function that took `iter1_begin`, `iter1_end`, `iter2_begin` iterators (with a custom comparison function). When object `iter1` was longer than `iter2`, read out-of-bounds on `iter2` could happen. Container sanitization would detect it.
This revision introduces annotations for `std::deque`. Each chunk of the container can now be annotated using the `__sanitizer_annotate_double_ended_contiguous_container` function, which was added in the rG1c5ad6d2c01294a0decde43a88e9c27d7437d157. Any attempt to access poisoned memory will trigger an ASan error. Although false negatives are rare, they are possible due to limitations in the ASan API, where a few (usually up to 7) bytes before the container may remain unpoisoned. There are no false positives in the same way as with `std::vector` annotations.
This patch only supports objects (deques) that use the standard allocator. However, it can be easily extended to support all allocators, as suggested in the D146815 revision.
Furthermore, the patch includes the addition of the `is_double_ended_contiguous_container_asan_correct` function to libcxx/test/support/asan_testing.h. This function can be used to verify whether a `std::deque` object has been correctly annotated.
Finally, the patch extends the unit tests to verify ASan annotations (added LIBCPP_ASSERTs).
If a program is compiled without ASan, all helper functions will be no-ops. In binaries with ASan, there is a negligible performance impact since the code from the change is only executed when the deque container changes in size and it’s proportional to the change. It is important to note that regardless of whether or not these changes are in use, every access to the container's memory is instrumented.
Reviewed By: #libc, philnik
Spies: vitalybuka, hans, mikhail.ramalho, Enna1, #sanitizers, philnik, libcxx-commits
Differential Revision: https://reviews.llvm.org/D132092
# ifndef _LIBCPP_HAS_NO_ASAN
extern "C" _LIBCPP_FUNC_VIS void
__sanitizer_annotate_contiguous_container(const void*, const void*, const void*, const void*);
+# if _LIBCPP_CLANG_VER >= 1600
+extern "C" _LIBCPP_FUNC_VIS void __sanitizer_annotate_double_ended_contiguous_container(
+ const void*, const void*, const void*, const void*, const void*, const void*);
+extern "C" _LIBCPP_FUNC_VIS int
+__sanitizer_verify_double_ended_contiguous_container(const void*, const void*, const void*, const void*);
+# endif
# endif
// Try to find out if RTTI is disabled.
template <class _Tp, class _Allocator /*= allocator<_Tp>*/>
class _LIBCPP_TEMPLATE_VIS deque
{
+private:
+ using __default_allocator_type = allocator<_Tp>;
+
public:
// types:
using __map_alloc_traits = allocator_traits<__pointer_allocator>;
using __map_pointer = typename __map_alloc_traits::pointer;
using __map_const_pointer = typename allocator_traits<__const_pointer_allocator>::const_pointer;
+ using __map_const_iterator = typename __map::const_iterator;
using reference = value_type&;
using const_reference = const value_type&;
// construct/copy/destroy:
_LIBCPP_HIDE_FROM_ABI
deque() _NOEXCEPT_(is_nothrow_default_constructible<allocator_type>::value)
- : __start_(0), __size_(0, __default_init_tag()) {}
+ : __start_(0), __size_(0, __default_init_tag()) {
+ __annotate_new(0);
+ }
_LIBCPP_HIDE_FROM_ABI ~deque() {
clear();
+ __annotate_delete();
typename __map::iterator __i = __map_.begin();
typename __map::iterator __e = __map_.end();
for (; __i != __e; ++__i)
}
_LIBCPP_HIDE_FROM_ABI explicit deque(const allocator_type& __a)
- : __map_(__pointer_allocator(__a)), __start_(0), __size_(0, __a) {}
+ : __map_(__pointer_allocator(__a)), __start_(0), __size_(0, __a) {
+ __annotate_new(0);
+ }
explicit _LIBCPP_HIDE_FROM_ABI deque(size_type __n);
#if _LIBCPP_STD_VER >= 14
_LIBCPP_HIDE_FROM_ABI deque(size_type __n, const value_type& __v, const allocator_type& __a)
: __map_(__pointer_allocator(__a)), __start_(0), __size_(0, __a)
{
+ __annotate_new(0);
if (__n > 0)
__append(__n, __v);
}
return false;
if (__map_.size() >= size_type(-1) / __block_size)
return false;
- for (typename __map::const_iterator __i = __map_.begin(), __e = __map_.end();
+ for (__map_const_iterator __i = __map_.begin(), __e = __map_.end();
__i != __e; ++__i)
if (*__i == nullptr)
return false;
}
private:
+ enum __asan_annotation_type {
+ __asan_unposion,
+ __asan_poison
+ };
+
+ enum __asan_annotation_place {
+ __asan_front_moved,
+ __asan_back_moved,
+ };
+
+// The following functions are no-ops outside of AddressSanitizer mode.
+// We call annotations only for the default Allocator.
+#if !defined(_LIBCPP_HAS_NO_ASAN) && _LIBCPP_CLANG_VER >= 1600
+ // TODO LLVM18: Remove the special-casing
+ _LIBCPP_HIDE_FROM_ABI void __annotate_double_ended_contiguous_container(
+ const void* __beg,
+ const void* __end,
+ const void* __old_con_beg,
+ const void* __old_con_end,
+ const void* __new_con_beg,
+ const void* __new_con_end) const {
+ if (__beg && is_same<allocator_type, __default_allocator_type>::value)
+ __sanitizer_annotate_double_ended_contiguous_container(
+ __beg, __end, __old_con_beg, __old_con_end, __new_con_beg, __new_con_end);
+ }
+#else
+ _LIBCPP_HIDE_FROM_ABI void __annotate_double_ended_contiguous_container(
+ const void*, const void*, const void*, const void*, const void*, const void*) const _NOEXCEPT {}
+#endif // !defined(_LIBCPP_HAS_NO_ASAN) && _LIBCPP_CLANG_VER >= 1600
+
+ _LIBCPP_HIDE_FROM_ABI
+ void __annotate_from_to(size_type __beg, size_type __end, __asan_annotation_type __annotation_type, __asan_annotation_place __place) const _NOEXCEPT {
+ // __beg - index of the first item to annotate
+ // __end - index behind the last item to annotate (so last item + 1)
+ // __annotation_type - __asan_unposion or __asan_poison
+ // __place - __asan_front_moved or __asan_back_moved
+ // Note: All indexes in __map_
+ if (__beg == __end)
+ return;
+ // __annotations_beg_map - first chunk which annotations we want to modify
+ // __annotations_end_map - last chunk which annotations we want to modify
+ // NOTE: if __end % __block_size == 0, __annotations_end_map points at the next block, which may not exist
+ __map_const_iterator __annotations_beg_map = __map_.begin() + __beg / __block_size;
+ __map_const_iterator __annotations_end_map = __map_.begin() + __end / __block_size;
+
+ bool const __poisoning = __annotation_type == __asan_poison;
+ // __old_c_beg_index - index of the first element in old container
+ // __old_c_end_index - index of the end of old container (last + 1)
+ // Note: may be outside the area we are annotating
+ size_t __old_c_beg_index = (__poisoning && __place == __asan_front_moved) ? __beg : __start_;
+ size_t __old_c_end_index = (__poisoning && __place == __asan_back_moved) ? __end : __start_ + size();
+ bool const __front = __place == __asan_front_moved;
+
+ if (__poisoning && empty()) {
+ // Special case: we shouldn't trust __start_
+ __old_c_beg_index = __beg;
+ __old_c_end_index = __end;
+ }
+ // __old_c_beg_map - memory block (chunk) with first element
+ // __old_c_end_map - memory block (chunk) with end of old container
+ // Note: if __old_c_end_index % __block_size == 0, __old_c_end_map points at the next block,
+ // which may not exist
+ __map_const_iterator __old_c_beg_map = __map_.begin() + __old_c_beg_index / __block_size;
+ __map_const_iterator __old_c_end_map = __map_.begin() + __old_c_end_index / __block_size;
+
+ // One edge (front/end) of the container was moved and one was not modified.
+ // __new_edge_index - index of new edge
+ // __new_edge_map - memory block (chunk) with new edge, it always equals to
+ // __annotations_beg_map or __annotations_end_map
+ // __old_edge_map - memory block (chunk) with old edge, it always equals to
+ // __old_c_beg_map or __old_c_end_map
+ size_t __new_edge_index = (__poisoning ^ __front) ? __beg : __end;
+ __map_const_iterator __new_edge_map = __map_.begin() + __new_edge_index / __block_size;
+ __map_const_iterator __old_edge_map = __front ? __old_c_end_map : __old_c_beg_map;
+
+ // We iterate over map pointers (chunks) and fully poison all memory blocks between the first and the last.
+ // First and last chunk may be partially poisoned.
+ // __annotate_end_map may point at not existing chunk, therefore we have to have a check for it.
+ for (__map_const_iterator __map_it = __annotations_beg_map; __map_it <= __annotations_end_map; ++__map_it) {
+ if (__map_it == __annotations_end_map && __end % __block_size == 0)
+ // Chunk may not exist, but nothing to do here anyway
+ break;
+
+ // The beginning and the end of the current memory block
+ const void* __mem_beg = std::__to_address(*__map_it);
+ const void* __mem_end = std::__to_address(*__map_it + __block_size);
+
+ // The beginning of memory-in-use in the memory block before container modification
+ const void* __old_beg =
+ (__map_it == __old_c_beg_map) ? std::__to_address(*__map_it + (__old_c_beg_index % __block_size)) : __mem_beg;
+
+ // The end of memory-in-use in the memory block before container modification
+ const void* __old_end;
+ if (__map_it < __old_c_beg_map || __map_it > __old_c_end_map || (!__poisoning && empty()))
+ __old_end = __old_beg;
+ else
+ __old_end = (__map_it == __old_c_end_map) ? std::__to_address(*__map_it + (__old_c_end_index % __block_size))
+ : __mem_end;
+
+ // New edge of the container in current memory block
+ // If the edge is in a different chunk it points on corresponding end of the memory block
+ const void* __new_edge;
+ if (__map_it == __new_edge_map)
+ __new_edge = std::__to_address(*__map_it + (__new_edge_index % __block_size));
+ else
+ __new_edge = (__poisoning ^ __front) ? __mem_beg : __mem_end;
+
+ // Not modified edge of the container
+ // If the edge is in a different chunk it points on corresponding end of the memory block
+ const void* __old_edge;
+ if (__map_it == __old_edge_map)
+ __old_edge = __front ? __old_end : __old_beg;
+ else
+ __old_edge = __front ? __mem_end : __mem_beg;
+
+ // __new_beg - the beginning of memory-in-use in the memory block after container modification
+ // __new_end - the end of memory-in-use in the memory block after container modification
+ const void* __new_beg = __front ? __new_edge : __old_edge;
+ const void* __new_end = __front ? __old_edge : __new_edge;
+
+ __annotate_double_ended_contiguous_container(__mem_beg, __mem_end, __old_beg, __old_end, __new_beg, __new_end);
+ }
+ }
+
+ _LIBCPP_HIDE_FROM_ABI
+ void __annotate_new(size_type __current_size) const _NOEXCEPT {
+ if (__current_size == 0)
+ __annotate_from_to(0, __map_.size() * __block_size, __asan_poison, __asan_back_moved);
+ else {
+ __annotate_from_to(0, __start_, __asan_poison, __asan_front_moved);
+ __annotate_from_to(__start_ + __current_size, __map_.size() * __block_size, __asan_poison, __asan_back_moved);
+ }
+ }
+
+ _LIBCPP_HIDE_FROM_ABI
+ void __annotate_delete() const _NOEXCEPT {
+ if (empty())
+ __annotate_from_to(0, __map_.size() * __block_size, __asan_unposion, __asan_back_moved);
+ else {
+ __annotate_from_to(0, __start_, __asan_unposion, __asan_front_moved);
+ __annotate_from_to(__start_ + size(), __map_.size() * __block_size, __asan_unposion, __asan_back_moved);
+ }
+ }
+
+ _LIBCPP_HIDE_FROM_ABI
+ void __annotate_increase_front(size_type __n) const _NOEXCEPT {
+ __annotate_from_to(__start_ - __n, __start_, __asan_unposion, __asan_front_moved);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI
+ void __annotate_increase_back(size_type __n) const _NOEXCEPT {
+ __annotate_from_to(__start_ + size(), __start_ + size() + __n, __asan_unposion, __asan_back_moved);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI
+ void __annotate_shrink_front(size_type __old_size, size_type __old_start) const _NOEXCEPT {
+ __annotate_from_to(__old_start, __old_start + (__old_size - size()), __asan_poison, __asan_front_moved);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI
+ void __annotate_shrink_back(size_type __old_size, size_type __old_start) const _NOEXCEPT {
+ __annotate_from_to(__old_start + size(), __old_start + __old_size, __asan_poison, __asan_back_moved);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI
+ void __annotate_whole_block(size_t __block_index, __asan_annotation_type __annotation_type, __asan_annotation_place __place) {
+ __annotate_from_to(__block_index * __block_size, (__block_index + 1) * __block_size, __annotation_type, __place);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI
+ void __annotate_poison_block(void *__beginning, void *__end) {
+ __annotate_double_ended_contiguous_container(__beginning, __end, __beginning, __end, __end, __end);
+ }
+
+#if !defined(_LIBCPP_HAS_NO_ASAN)
+
+ public:
+ _LIBCPP_HIDE_FROM_ABI
+ bool __verify_asan_annotations() const _NOEXCEPT {
+ // This function tests deque object annotations.
+ if (empty()) {
+ for (__map_const_iterator __it = __map_.begin(); __it != __map_.end(); ++__it) {
+ if (!__sanitizer_verify_double_ended_contiguous_container(
+ std::__to_address(*__it),
+ std::__to_address(*__it),
+ std::__to_address(*__it),
+ std::__to_address(*__it + __block_size)))
+ return false;
+ }
+
+ return true;
+ }
+
+ size_type __end = __start_ + size();
+ __map_const_iterator __first_mp = __map_.begin() + __start_ / __block_size;
+ __map_const_iterator __last_mp = __map_.begin() + (__end - 1) / __block_size;
+
+ // Pointers to first and after last elements
+ // Those can be in different deque blocks
+ void* __p_beg = std::__to_address(*__first_mp + (__start_ % __block_size));
+ void* __p_end =
+ std::__to_address(*__last_mp + ((__end % __block_size == 0) ? __block_size : __end % __block_size));
+
+ for (__map_const_iterator __it = __map_.begin(); __it != __map_.end(); ++__it) {
+ // Go over all blocks, find the place we are in and verify its annotations
+ // Note that __p_end points *behind* the last item.
+
+ // - blocks before the first block with container elements
+ // - first block with items
+ // - last block with items
+ // - blocks after last block with ciontainer elements
+
+ // Is the block before or after deque blocks that contain elements?
+ if (__it < __first_mp || __it > __last_mp) {
+ if (!__sanitizer_verify_double_ended_contiguous_container(
+ std::__to_address(*__it),
+ std::__to_address(*__it),
+ std::__to_address(*__it),
+ std::__to_address(*__it + __block_size)))
+ return false;
+ } else {
+ void* __containers_buffer_beg = (__it == __first_mp) ? __p_beg : (void*)std::__to_address(*__it);
+ void* __containers_buffer_end =
+ (__it == __last_mp) ? __p_end : (void*)std::__to_address(*__it + __block_size);
+ if (!__sanitizer_verify_double_ended_contiguous_container(
+ std::__to_address(*__it),
+ __containers_buffer_beg,
+ __containers_buffer_end,
+ std::__to_address(*__it + __block_size))) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ private:
+#endif // _LIBCPP_VERIFY_ASAN_DEQUE_ANNOTATIONS
_LIBCPP_HIDE_FROM_ABI
bool __maybe_remove_front_spare(bool __keep_one = true) {
if (__front_spare_blocks() >= 2 || (!__keep_one && __front_spare_blocks())) {
+ __annotate_whole_block(0, __asan_unposion, __asan_back_moved);
__alloc_traits::deallocate(__alloc(), __map_.front(),
__block_size);
__map_.pop_front();
_LIBCPP_HIDE_FROM_ABI
bool __maybe_remove_back_spare(bool __keep_one = true) {
if (__back_spare_blocks() >= 2 || (!__keep_one && __back_spare_blocks())) {
+ __annotate_whole_block(__map_.size() - 1, __asan_unposion, __asan_back_moved);
__alloc_traits::deallocate(__alloc(), __map_.back(),
__block_size);
__map_.pop_back();
deque<_Tp, _Allocator>::deque(size_type __n)
: __start_(0), __size_(0, __default_init_tag())
{
+ __annotate_new(0);
if (__n > 0)
__append(__n);
}
deque<_Tp, _Allocator>::deque(size_type __n, const _Allocator& __a)
: __map_(__pointer_allocator(__a)), __start_(0), __size_(0, __a)
{
+ __annotate_new(0);
if (__n > 0)
__append(__n);
}
deque<_Tp, _Allocator>::deque(size_type __n, const value_type& __v)
: __start_(0), __size_(0, __default_init_tag())
{
+ __annotate_new(0);
if (__n > 0)
__append(__n, __v);
}
typename enable_if<__has_input_iterator_category<_InputIter>::value>::type*)
: __start_(0), __size_(0, __default_init_tag())
{
+ __annotate_new(0);
__append(__f, __l);
}
typename enable_if<__has_input_iterator_category<_InputIter>::value>::type*)
: __map_(__pointer_allocator(__a)), __start_(0), __size_(0, __a)
{
+ __annotate_new(0);
__append(__f, __l);
}
__start_(0),
__size_(0, __map_.__alloc())
{
+ __annotate_new(0);
__append(__c.begin(), __c.end());
}
deque<_Tp, _Allocator>::deque(const deque& __c, const __type_identity_t<allocator_type>& __a)
: __map_(__pointer_allocator(__a)), __start_(0), __size_(0, __a)
{
+ __annotate_new(0);
__append(__c.begin(), __c.end());
}
deque<_Tp, _Allocator>::deque(initializer_list<value_type> __il)
: __start_(0), __size_(0, __default_init_tag())
{
+ __annotate_new(0);
__append(__il.begin(), __il.end());
}
deque<_Tp, _Allocator>::deque(initializer_list<value_type> __il, const allocator_type& __a)
: __map_(__pointer_allocator(__a)), __start_(0), __size_(0, __a)
{
+ __annotate_new(0);
__append(__il.begin(), __il.end());
}
allocator_type& __a = __alloc();
if (empty())
{
+ __annotate_delete();
while (__map_.size() > 0)
{
__alloc_traits::deallocate(__a, __map_.back(), __block_size);
if (__back_spare() == 0)
__add_back_capacity();
// __back_spare() >= 1
+ __annotate_increase_back(1);
__alloc_traits::construct(__a, _VSTD::addressof(*end()), __v);
++__size();
}
if (__front_spare() == 0)
__add_front_capacity();
// __front_spare() >= 1
+ __annotate_increase_front(1);
__alloc_traits::construct(__a, _VSTD::addressof(*--begin()), __v);
--__start_;
++__size();
if (__back_spare() == 0)
__add_back_capacity();
// __back_spare() >= 1
+ __annotate_increase_back(1);
__alloc_traits::construct(__a, _VSTD::addressof(*end()), _VSTD::move(__v));
++__size();
}
if (__back_spare() == 0)
__add_back_capacity();
// __back_spare() >= 1
+ __annotate_increase_back(1);
__alloc_traits::construct(__a, _VSTD::addressof(*end()),
_VSTD::forward<_Args>(__args)...);
++__size();
if (__front_spare() == 0)
__add_front_capacity();
// __front_spare() >= 1
+ __annotate_increase_front(1);
__alloc_traits::construct(__a, _VSTD::addressof(*--begin()), _VSTD::move(__v));
--__start_;
++__size();
if (__front_spare() == 0)
__add_front_capacity();
// __front_spare() >= 1
+ __annotate_increase_front(1);
__alloc_traits::construct(__a, _VSTD::addressof(*--begin()), _VSTD::forward<_Args>(__args)...);
--__start_;
++__size();
if (__front_spare() == 0)
__add_front_capacity();
// __front_spare() >= 1
+ __annotate_increase_front(1);
if (__pos == 0)
{
__alloc_traits::construct(__a, _VSTD::addressof(*--begin()), _VSTD::move(__v));
if (__back_spare() == 0)
__add_back_capacity();
// __back_capacity >= 1
+ __annotate_increase_back(1);
size_type __de = size() - __pos;
if (__de == 0)
{
if (__front_spare() == 0)
__add_front_capacity();
// __front_spare() >= 1
+ __annotate_increase_front(1);
if (__pos == 0)
{
__alloc_traits::construct(__a, _VSTD::addressof(*--begin()), _VSTD::forward<_Args>(__args)...);
if (__back_spare() == 0)
__add_back_capacity();
// __back_capacity >= 1
+ __annotate_increase_back(1);
size_type __de = size() - __pos;
if (__de == 0)
{
if (__front_spare() == 0)
__add_front_capacity();
// __front_spare() >= 1
+ __annotate_increase_front(1);
if (__pos == 0)
{
__alloc_traits::construct(__a, _VSTD::addressof(*--begin()), __v);
if (__back_spare() == 0)
__add_back_capacity();
// __back_capacity >= 1
+ __annotate_increase_back(1);
size_type __de = size() - __pos;
if (__de == 0)
{
if (__n > __front_spare())
__add_front_capacity(__n - __front_spare());
// __n <= __front_spare()
+ __annotate_increase_front(__n);
iterator __old_begin = begin();
iterator __i = __old_begin;
if (__n > __pos)
if (__n > __back_capacity)
__add_back_capacity(__n - __back_capacity);
// __n <= __back_capacity
+ __annotate_increase_back(__n);
iterator __old_end = end();
iterator __i = __old_end;
size_type __de = size() - __pos;
if (__n > __front_spare())
__add_front_capacity(__n - __front_spare());
// __n <= __front_spare()
+ __annotate_increase_front(__n);
iterator __old_begin = begin();
iterator __i = __old_begin;
_BiIter __m = __f;
if (__n > __back_capacity)
__add_back_capacity(__n - __back_capacity);
// __n <= __back_capacity
+ __annotate_increase_back(__n);
iterator __old_end = end();
iterator __i = __old_end;
_BiIter __m = __l;
__add_back_capacity(__n - __back_capacity);
// __n <= __back_capacity
+ __annotate_increase_back(__n);
for (__deque_block_range __br : __deque_range(end(), end() + __n)) {
_ConstructTransaction __tx(this, __br);
for (; __tx.__pos_ != __tx.__end_; ++__tx.__pos_, (void)++__f) {
if (__n > __back_capacity)
__add_back_capacity(__n - __back_capacity);
// __n <= __back_capacity
+ __annotate_increase_back(__n);
for (__deque_block_range __br : __deque_range(end(), end() + __n)) {
_ConstructTransaction __tx(this, __br);
for (; __tx.__pos_ != __tx.__end_; ++__tx.__pos_) {
if (__n > __back_capacity)
__add_back_capacity(__n - __back_capacity);
// __n <= __back_capacity
+ __annotate_increase_back(__n);
for (__deque_block_range __br : __deque_range(end(), end() + __n)) {
_ConstructTransaction __tx(this, __br);
for (; __tx.__pos_ != __tx.__end_; ++__tx.__pos_) {
__block_size / 2 :
__start_ + __block_size;
}
+ __annotate_whole_block(0, __asan_poison, __asan_front_moved);
}
// Create front capacity for __n elements.
if (__map_.__front_spare() == 0)
break;
__map_.push_front(__alloc_traits::allocate(__a, __block_size));
+ __annotate_whole_block(0, __asan_poison, __asan_front_moved);
}
for (; __nb > 0; --__nb, ++__back_capacity)
__map_.push_back(__alloc_traits::allocate(__a, __block_size));
pointer __pt = __map_.back();
__map_.pop_back();
__map_.push_front(__pt);
+ __annotate_whole_block(0, __asan_poison, __asan_front_moved);
}
}
// Else need to allocate __nb buffers, *and* we need to reallocate __map_.
try
{
#endif // _LIBCPP_HAS_NO_EXCEPTIONS
- for (; __nb > 0; --__nb)
+ for (; __nb > 0; --__nb) {
__buf.push_back(__alloc_traits::allocate(__a, __block_size));
+ // ASan: this is empty container, we have to poison whole block
+ __annotate_poison_block(
+ std::__to_address(__buf.back()),
+ std::__to_address(__buf.back() + __block_size));
+ }
#ifndef _LIBCPP_HAS_NO_EXCEPTIONS
}
catch (...)
{
+ __annotate_delete();
for (__map_pointer __i = __buf.begin();
__i != __buf.end(); ++__i)
__alloc_traits::deallocate(__a, *__i, __block_size);
__map_.pop_front();
__map_.push_back(__pt);
}
+ __annotate_whole_block(__map_.size() - 1, __asan_poison, __asan_back_moved);
}
// Else need to allocate 1 buffer, *and* we need to reallocate __map_.
else
_VSTD::swap(__map_.__begin_, __buf.__begin_);
_VSTD::swap(__map_.__end_, __buf.__end_);
_VSTD::swap(__map_.__end_cap(), __buf.__end_cap());
+ __annotate_whole_block(__map_.size() - 1, __asan_poison, __asan_back_moved);
}
}
if (__map_.__back_spare() == 0)
break;
__map_.push_back(__alloc_traits::allocate(__a, __block_size));
+ __annotate_whole_block(__map_.size() - 1, __asan_poison, __asan_back_moved);
}
for (; __nb > 0; --__nb, ++__front_capacity, __start_ +=
- __block_size - (__map_.size() == 1))
+ __block_size - (__map_.size() == 1)) {
__map_.push_front(__alloc_traits::allocate(__a, __block_size));
+ __annotate_whole_block(0, __asan_poison, __asan_back_moved);
+ }
// Done allocating, reorder capacity
__start_ -= __block_size * __front_capacity;
for (; __front_capacity > 0; --__front_capacity)
try
{
#endif // _LIBCPP_HAS_NO_EXCEPTIONS
- for (; __nb > 0; --__nb)
+ for (; __nb > 0; --__nb) {
__buf.push_back(__alloc_traits::allocate(__a, __block_size));
+ // ASan: this is an empty container, we have to poison the whole block
+ __annotate_poison_block(
+ std::__to_address(__buf.back()),
+ std::__to_address(__buf.back() + __block_size));
+ }
#ifndef _LIBCPP_HAS_NO_EXCEPTIONS
}
catch (...)
{
+ __annotate_delete();
for (__map_pointer __i = __buf.begin();
__i != __buf.end(); ++__i)
__alloc_traits::deallocate(__a, *__i, __block_size);
void
deque<_Tp, _Allocator>::pop_front()
{
+ size_type __old_sz = size();
+ size_type __old_start = __start_;
allocator_type& __a = __alloc();
__alloc_traits::destroy(__a, _VSTD::__to_address(*(__map_.begin() +
__start_ / __block_size) +
__start_ % __block_size));
--__size();
++__start_;
+ __annotate_shrink_front(__old_sz, __old_start);
__maybe_remove_front_spare();
}
deque<_Tp, _Allocator>::pop_back()
{
_LIBCPP_ASSERT(!empty(), "deque::pop_back called on an empty deque");
+ size_type __old_sz = size();
+ size_type __old_start = __start_;
allocator_type& __a = __alloc();
size_type __p = size() + __start_ - 1;
__alloc_traits::destroy(__a, _VSTD::__to_address(*(__map_.begin() +
__p / __block_size) +
__p % __block_size));
--__size();
+ __annotate_shrink_back(__old_sz, __old_start);
__maybe_remove_back_spare();
}
typename deque<_Tp, _Allocator>::iterator
deque<_Tp, _Allocator>::erase(const_iterator __f)
{
+ size_type __old_sz = size();
+ size_type __old_start = __start_;
iterator __b = begin();
difference_type __pos = __f - __b;
iterator __p = __b + __pos;
__alloc_traits::destroy(__a, _VSTD::addressof(*__b));
--__size();
++__start_;
+ __annotate_shrink_front(__old_sz, __old_start);
__maybe_remove_front_spare();
}
else
iterator __i = _VSTD::move(_VSTD::next(__p), end(), __p);
__alloc_traits::destroy(__a, _VSTD::addressof(*__i));
--__size();
+ __annotate_shrink_back(__old_sz, __old_start);
__maybe_remove_back_spare();
}
return begin() + __pos;
typename deque<_Tp, _Allocator>::iterator
deque<_Tp, _Allocator>::erase(const_iterator __f, const_iterator __l)
{
+ size_type __old_sz = size();
+ size_type __old_start = __start_;
difference_type __n = __l - __f;
iterator __b = begin();
difference_type __pos = __f - __b;
__alloc_traits::destroy(__a, _VSTD::addressof(*__b));
__size() -= __n;
__start_ += __n;
+ __annotate_shrink_front(__old_sz, __old_start);
while (__maybe_remove_front_spare()) {
}
}
for (iterator __e = end(); __i != __e; ++__i)
__alloc_traits::destroy(__a, _VSTD::addressof(*__i));
__size() -= __n;
+ __annotate_shrink_back(__old_sz, __old_start);
while (__maybe_remove_back_spare()) {
}
}
void
deque<_Tp, _Allocator>::__erase_to_end(const_iterator __f)
{
+ size_type __old_sz = size();
+ size_type __old_start = __start_;
iterator __e = end();
difference_type __n = __e - __f;
if (__n > 0)
for (iterator __p = __b + __pos; __p != __e; ++__p)
__alloc_traits::destroy(__a, _VSTD::addressof(*__p));
__size() -= __n;
+ __annotate_shrink_back(__old_sz, __old_start);
while (__maybe_remove_back_spare()) {
}
}
void
deque<_Tp, _Allocator>::clear() _NOEXCEPT
{
+ __annotate_delete();
allocator_type& __a = __alloc();
for (iterator __i = begin(), __e = end(); __i != __e; ++__i)
__alloc_traits::destroy(__a, _VSTD::addressof(*__i));
__start_ = __block_size;
break;
}
+ __annotate_new(0);
}
template <class _Tp, class _Allocator>
--- /dev/null
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: asan
+
+// <deque>
+
+// reference operator[](size_type n);
+
+#include "asan_testing.h"
+#include <deque>
+#include <cassert>
+#include <cstdlib>
+
+#include "min_allocator.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+
+extern "C" void __sanitizer_set_death_callback(void (*callback)(void));
+
+void do_exit() {
+ exit(0);
+}
+
+int main(int, char**)
+{
+ {
+ typedef cpp17_input_iterator<int*> MyInputIter;
+ // Sould not trigger ASan.
+ std::deque<int> v;
+ int i[] = {42};
+ v.insert(v.begin(), MyInputIter(i), MyInputIter(i + 1));
+ assert(v[0] == 42);
+ assert(is_double_ended_contiguous_container_asan_correct(v));
+ }
+ __sanitizer_set_death_callback(do_exit);
+ {
+ typedef int T;
+ typedef std::deque<T> C;
+ const T t[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+ C c(std::begin(t), std::end(t));
+ assert(is_double_ended_contiguous_container_asan_correct(c));
+ T* ptr = &c[0];
+ for(size_t i = 0; i < (8 + sizeof(T) - 1)/sizeof(T); ++i)
+ c.pop_front();
+ *ptr = 1;
+ volatile T foo = c[c.size()]; // should trigger ASAN. Use volatile to prevent being optimized away.
+ assert(false); // if we got here, ASAN didn't trigger
+ ((void)foo);
+ }
+}
// const_reference back() const;
// libc++ marks these as 'noexcept'
+#include "asan_testing.h"
#include <deque>
#include <cassert>
assert(c.at(i) == i);
assert(c.front() == 0);
assert(c.back() == 9);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
}
{
typedef std::deque<int> C;
assert(c.at(i) == i);
assert(c.front() == 0);
assert(c.back() == 9);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
}
#if TEST_STD_VER >= 11
{
assert(c.at(i) == i);
assert(c.front() == 0);
assert(c.back() == 9);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
}
{
typedef std::deque<int, min_allocator<int>> C;
assert(c.at(i) == i);
assert(c.front() == 0);
assert(c.back() == 9);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
}
#endif
// bool empty() const noexcept;
+#include "asan_testing.h"
#include <deque>
#include <cassert>
assert(c.empty());
c.push_back(C::value_type(1));
assert(!c.empty());
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
c.clear();
assert(c.empty());
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
}
#if TEST_STD_VER >= 11
{
assert(c.empty());
c.push_back(C::value_type(1));
assert(!c.empty());
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
c.clear();
assert(c.empty());
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
}
#endif
// size_type max_size() const;
+#include "asan_testing.h"
#include <cassert>
#include <deque>
#include <limits>
C c;
assert(c.max_size() <= 10);
LIBCPP_ASSERT(c.max_size() == 10);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
}
{
typedef limited_allocator<int, (std::size_t)-1> A;
C c;
assert(c.max_size() <= max_dist);
LIBCPP_ASSERT(c.max_size() == max_dist);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
}
{
typedef std::deque<char> C;
C c;
assert(c.max_size() <= max_dist);
assert(c.max_size() <= alloc_max_size(c.get_allocator()));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
}
return 0;
// void resize(size_type n);
+#include "asan_testing.h"
#include <deque>
#include <algorithm>
#include <iterator>
C c(init, 0);
for (int i = 0; i < init-start; ++i)
c.pop_back();
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
for (int i = 0; i < size; ++i)
c.push_back(i);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
for (int i = 0; i < start; ++i)
c.pop_front();
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
return c;
}
c1.resize(size);
assert(c1.size() == static_cast<std::size_t>(size));
assert(static_cast<std::size_t>(std::distance(c1.begin(), c1.end())) == c1.size());
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
CI i = c1.begin();
for (int j = 0; static_cast<std::size_t>(j) < std::min(c1_osize, c1.size()); ++j, ++i)
assert(*i == j);
for (int k = 0; k < N; ++k)
testN<std::deque<int, min_allocator<int>>>(rng[i], rng[j], rng[k]);
}
+ {
+ int rng[] = {0, 1, 2, 3, 1023, 1024, 1025, 2047, 2048, 2049};
+ const int N = sizeof(rng)/sizeof(rng[0]);
+ for (int i = 0; i < N; ++i)
+ for (int j = 0; j < N; ++j)
+ for (int k = 0; k < N; ++k)
+ testN<std::deque<int, safe_allocator<int>>>(rng[i], rng[j], rng[k]);
+ }
#endif
return 0;
// void resize(size_type n, const value_type& v);
+#include "asan_testing.h"
#include <deque>
#include <algorithm>
#include <iterator>
C c(init, 0);
for (int i = 0; i < init-start; ++i)
c.pop_back();
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
for (int i = 0; i < size; ++i)
c.push_back(i);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
for (int i = 0; i < start; ++i)
c.pop_front();
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
return c;
}
c1.resize(size, x);
assert(c1.size() == static_cast<std::size_t>(size));
assert(static_cast<std::size_t>(std::distance(c1.begin(), c1.end())) == c1.size());
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
CI i = c1.begin();
for (int j = 0; static_cast<std::size_t>(j) < std::min(c1_osize, c1.size()); ++j, ++i)
assert(*i == j);
for (int k = 0; k < N; ++k)
testN<std::deque<int, min_allocator<int>>>(rng[i], rng[j], rng[k]);
}
+ {
+ int rng[] = {0, 1, 2, 3, 1023, 1024, 1025, 2047, 2048, 2049};
+ const int N = sizeof(rng)/sizeof(rng[0]);
+ for (int i = 0; i < N; ++i)
+ for (int j = 0; j < N; ++j)
+ for (int k = 0; k < N; ++k)
+ testN<std::deque<int, safe_allocator<int>>>(rng[i], rng[j], rng[k]);
+ }
#endif
return 0;
// void shrink_to_fit();
+#include "asan_testing.h"
#include <deque>
#include <cassert>
C s = c1;
c1.shrink_to_fit();
assert(c1 == s);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
}
template <class C>
for (int j = 0; j < N; ++j)
testN<std::deque<int, min_allocator<int>> >(rng[i], rng[j]);
}
+ {
+ int rng[] = {0, 1, 2, 3, 1023, 1024, 1025, 2047, 2048, 2049};
+ const int N = sizeof(rng)/sizeof(rng[0]);
+ for (int i = 0; i < N; ++i)
+ for (int j = 0; j < N; ++j)
+ testN<std::deque<int, safe_allocator<int>> >(rng[i], rng[j]);
+ }
#endif
return 0;
// size_type size() const noexcept;
+#include "asan_testing.h"
#include <deque>
#include <cassert>
C c;
ASSERT_NOEXCEPT(c.size());
assert(c.size() == 0);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
c.push_back(C::value_type(2));
assert(c.size() == 1);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
c.push_back(C::value_type(1));
assert(c.size() == 2);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
c.push_back(C::value_type(3));
assert(c.size() == 3);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
c.erase(c.begin());
assert(c.size() == 2);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
c.erase(c.begin());
assert(c.size() == 1);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
c.erase(c.begin());
assert(c.size() == 0);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
}
#if TEST_STD_VER >= 11
{
C c;
ASSERT_NOEXCEPT(c.size());
assert(c.size() == 0);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
c.push_back(C::value_type(2));
assert(c.size() == 1);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
c.push_back(C::value_type(1));
assert(c.size() == 2);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
c.push_back(C::value_type(3));
assert(c.size() == 3);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
c.erase(c.begin());
assert(c.size() == 2);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
c.erase(c.begin());
assert(c.size() == 1);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
c.erase(c.begin());
assert(c.size() == 0);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
}
#endif
// explicit deque(const allocator_type& a);
+#include "asan_testing.h"
#include <deque>
#include <cassert>
std::deque<T, Allocator> d(a);
assert(d.size() == 0);
assert(d.get_allocator() == a);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(d));
}
int main(int, char**)
test<NotConstructible>(test_allocator<NotConstructible>(3));
#if TEST_STD_VER >= 11
test<int>(min_allocator<int>());
+ test<int>(safe_allocator<int>());
test<NotConstructible>(min_allocator<NotConstructible>{});
+ test<NotConstructible>(safe_allocator<NotConstructible>{});
test<int>(explicit_allocator<int>());
test<NotConstructible>(explicit_allocator<NotConstructible>{});
#endif
// void assign(initializer_list<value_type> il);
+#include "asan_testing.h"
#include <deque>
#include <cassert>
assert(d[1] == 4);
assert(d[2] == 5);
assert(d[3] == 6);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(d));
}
{
std::deque<int, min_allocator<int>> d;
assert(d[1] == 4);
assert(d[2] == 5);
assert(d[3] == 6);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(d));
}
return 0;
// template <class InputIterator>
// void assign(InputIterator f, InputIterator l);
+#include "asan_testing.h"
#include <deque>
#include <cassert>
#include <cstddef>
c1.assign(c2.begin(), c2.end());
assert(static_cast<std::size_t>(std::distance(c1.begin(), c1.end())) == c1.size());
assert(c1 == c2);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c2));
}
template <class C>
c1.assign(ICI(c2.begin()), ICI(c2.end()));
assert(static_cast<std::size_t>(std::distance(c1.begin(), c1.end())) == c1.size());
assert(c1 == c2);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c2));
}
template <class C>
testN<std::deque<int, min_allocator<int>> >(rng[i], rng[j], rng[k]);
testNI<std::deque<int, min_allocator<int>> >(1500, 2000, 1000);
}
+ {
+ int rng[] = {0, 1, 2, 3, 1023, 1024, 1025, 2047, 2048, 2049};
+ const int N = sizeof(rng)/sizeof(rng[0]);
+ for (int i = 0; i < N; ++i)
+ for (int j = 0; j < N; ++j)
+ for (int k = 0; k < N; ++k)
+ testN<std::deque<int, safe_allocator<int>> >(rng[i], rng[j], rng[k]);
+ testNI<std::deque<int, safe_allocator<int>> >(1500, 2000, 1000);
+ }
#endif
}
// void assign(size_type n, const value_type& v);
+#include "asan_testing.h"
#include <deque>
#include <cassert>
#include <cstddef>
c1.assign(size, v);
assert(c1.size() == static_cast<std::size_t>(size));
assert(static_cast<std::size_t>(std::distance(c1.begin(), c1.end())) == c1.size());
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
for (CI i = c1.begin(); i != c1.end(); ++i)
assert(*i == v);
}
// deque(const deque&);
+#include "asan_testing.h"
#include <deque>
#include <cassert>
{
C c(x);
assert(c == x);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(x));
}
int main(int, char**)
std::deque<int, test_allocator<int> > v2 = v;
assert(v2 == v);
assert(v2.get_allocator() == v.get_allocator());
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(v));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(v2));
}
#if TEST_STD_VER >= 11
{
std::deque<int, other_allocator<int> > v2 = v;
assert(v2 == v);
assert(v2.get_allocator() == other_allocator<int>(-2));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(v));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(v2));
}
{
int ab[] = {3, 4, 2, 8, 0, 1, 44, 34, 45, 96, 80, 1, 13, 31, 45};
std::deque<int, min_allocator<int> > v2 = v;
assert(v2 == v);
assert(v2.get_allocator() == v.get_allocator());
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(v));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(v2));
}
#endif
// deque(const deque& c, const allocator_type& a);
+#include "asan_testing.h"
#include <deque>
#include <cassert>
C c(x, a);
assert(c == x);
assert(c.get_allocator() == a);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
}
int main(int, char**)
test(std::deque<int, min_allocator<int> >(ab, an, min_allocator<int>()),
min_allocator<int>());
}
+ {
+ int ab[] = {3, 4, 2, 8, 0, 1, 44, 34, 45, 96, 80, 1, 13, 31, 45};
+ int* an = ab + sizeof(ab)/sizeof(ab[0]);
+ test(std::deque<int, safe_allocator<int> >(ab, an, safe_allocator<int>()),
+ safe_allocator<int>());
+ }
#endif
return 0;
// deque(from_range_t, R&&, Allocator = Allocator())
// -> deque<ranges::range_value_t<R>, Allocator>; // C++23
+#include "asan_testing.h"
#include <array>
#include <cassert>
#include <climits> // INT_MAX
static_assert(std::is_same_v<decltype(deq), std::deque<int>>, "");
assert(std::equal(deq.begin(), deq.end(), std::begin(arr), std::end(arr)));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(deq));
}
{
assert(deq[0] == INT_MAX);
assert(deq[1] == 1L);
assert(deq[2] == 2L);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(deq));
}
// Test the implicit deduction guides
static_assert(std::is_same_v<decltype(deq)::value_type, A>, "");
static_assert(std::is_same_v<decltype(deq)::allocator_type, std::allocator<A>>, "");
assert(deq.size() == 1);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(deq));
}
{
static_assert(std::is_same_v<decltype(deq)::value_type, A>, "");
static_assert(std::is_same_v<decltype(deq)::allocator_type, test_allocator<A>>, "");
assert(deq.size() == 1);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(deq));
}
{
static_assert(std::is_same_v<decltype(deq)::value_type, unsigned>, "");
assert(deq.size() == 5);
assert(deq[2] == 3U);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(deq));
}
{
static_assert(std::is_same_v<decltype(deq)::allocator_type, test_allocator<double>>, "");
assert(deq.size() == 4);
assert(deq[3] == 4.0);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(deq));
}
{
static_assert(std::is_same_v<decltype(deq)::value_type, long double>, "");
static_assert(std::is_same_v<decltype(deq)::allocator_type, std::allocator<long double>>, "");
assert(deq.size() == 0);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(deq));
}
{
std::deque<short, Alloc> source;
std::deque deq(source, Alloc(2));
static_assert(std::is_same_v<decltype(deq), decltype(source)>);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(deq));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(source));
}
{
std::deque<short, Alloc> source;
std::deque deq(source, ConvertibleToAlloc(2));
static_assert(std::is_same_v<decltype(deq), decltype(source)>);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(deq));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(source));
}
{
std::deque<short, Alloc> source;
std::deque deq(std::move(source), Alloc(2));
static_assert(std::is_same_v<decltype(deq), decltype(source)>);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(deq));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(source));
}
{
std::deque<short, Alloc> source;
std::deque deq(std::move(source), ConvertibleToAlloc(2));
static_assert(std::is_same_v<decltype(deq), decltype(source)>);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(deq));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(source));
}
}
// deque()
+#include "asan_testing.h"
#include <deque>
#include <cassert>
{
std::deque<T, Allocator> d;
assert(d.size() == 0);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(d));
#if TEST_STD_VER >= 11
std::deque<T, Allocator> d1 = {};
assert(d1.size() == 0);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(d1));
#endif
}
// deque(initializer_list<value_type> il);
+#include "asan_testing.h"
#include <deque>
#include <cassert>
assert(d[1] == 4);
assert(d[2] == 5);
assert(d[3] == 6);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(d));
}
{
std::deque<int, min_allocator<int>> d = {3, 4, 5, 6};
assert(d[1] == 4);
assert(d[2] == 5);
assert(d[3] == 6);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(d));
}
return 0;
// deque(initializer_list<value_type> il, const Allocator& a = allocator_type());
+#include "asan_testing.h"
#include <deque>
#include <cassert>
assert(d[1] == 4);
assert(d[2] == 5);
assert(d[3] == 6);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(d));
}
{
std::deque<int, min_allocator<int>> d({3, 4, 5, 6}, min_allocator<int>());
assert(d[1] == 4);
assert(d[2] == 5);
assert(d[3] == 6);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(d));
}
return 0;
// template <class InputIterator> deque(InputIterator f, InputIterator l);
+#include "asan_testing.h"
#include <deque>
#include <cassert>
#include <cstddef>
C d(f, l);
assert(d.size() == static_cast<std::size_t>(std::distance(f, l)));
assert(static_cast<std::size_t>(std::distance(d.begin(), d.end())) == d.size());
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(d));
for (const_iterator i = d.begin(), e = d.end(); i != e; ++i, ++f)
assert(*i == *f);
}
C d(f, l);
assert(d.size() == static_cast<std::size_t>(std::distance(f, l)));
assert(static_cast<std::size_t>(std::distance(d.begin(), d.end())) == d.size());
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(d));
for (const_iterator i = d.begin(), e = d.end(); i != e; ++i, ++f)
assert(*i == *f);
}
// template <class InputIterator>
// deque(InputIterator f, InputIterator l, const allocator_type& a);
+#include "asan_testing.h"
#include <deque>
#include <cassert>
#include <cstddef>
assert(d.get_allocator() == a);
assert(d.size() == static_cast<std::size_t>(std::distance(f, l)));
assert(static_cast<std::size_t>(std::distance(d.begin(), d.end())) == d.size());
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(d));
for (const_iterator i = d.begin(), e = d.end(); i != e; ++i, ++f)
assert(*i == *f);
}
{
std::deque<T> v(It(arr1), It(std::end(arr1)), a);
assert(v[0].value == 42);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(v));
}
{
std::deque<T> v(It(arr2), It(std::end(arr2)), a);
assert(v[0].value == 1);
assert(v[1].value == 101);
assert(v[2].value == 42);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(v));
}
}
{
std::deque<T> v(It(arr1), It(std::end(arr1)), a);
assert(v[0].copied == 0);
assert(v[0].value == 42);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(v));
}
{
std::deque<T> v(It(arr2), It(std::end(arr2)), a);
assert(v[1].value == 101);
assert(v[2].copied == 0);
assert(v[2].value == 42);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(v));
}
}
#endif
// deque(deque&&);
+#include "asan_testing.h"
#include <deque>
#include <cassert>
assert(c1.size() == 0);
assert(c3.get_allocator() == old_a);
assert(c1.get_allocator() == A(test_alloc_base::moved_value));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c2));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c3));
}
{
int ab[] = {3, 4, 2, 8, 0, 1, 44, 34, 45, 96, 80, 1, 13, 31, 45};
assert(c2 == c3);
assert(c1.size() == 0);
assert(c3.get_allocator() == c1.get_allocator());
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c2));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c3));
}
{
int ab[] = {3, 4, 2, 8, 0, 1, 44, 34, 45, 96, 80, 1, 13, 31, 45};
assert(c2 == c3);
assert(c1.size() == 0);
assert(c3.get_allocator() == c1.get_allocator());
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c2));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c3));
}
return 0;
// deque(deque&& c, const allocator_type& a);
+#include "asan_testing.h"
#include <deque>
#include <cassert>
assert(c2 == c3);
assert(c3.get_allocator() == A(3));
LIBCPP_ASSERT(c1.size() != 0);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c2));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c3));
}
{
int ab[] = {3, 4, 2, 8, 0, 1, 44, 34, 45, 96, 80, 1, 13, 31, 45};
assert(c2 == c3);
assert(c3.get_allocator() == A(1));
LIBCPP_ASSERT(c1.size() == 0);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c2));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c3));
}
{
int ab[] = {3, 4, 2, 8, 0, 1, 44, 34, 45, 96, 80, 1, 13, 31, 45};
assert(c2 == c3);
assert(c3.get_allocator() == A(3));
LIBCPP_ASSERT(c1.size() != 0);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c2));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c3));
}
{
int ab[] = {3, 4, 2, 8, 0, 1, 44, 34, 45, 96, 80, 1, 13, 31, 45};
assert(c2 == c3);
assert(c3.get_allocator() == A());
LIBCPP_ASSERT(c1.size() == 0);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c2));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c3));
}
return 0;
// deque& operator=(deque&& c);
+#include "asan_testing.h"
#include <deque>
#include <cassert>
assert(c2 == c3);
assert(c1.size() == 0);
assert(c3.get_allocator() == A(5));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c2));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c3));
}
{
int ab[] = {3, 4, 2, 8, 0, 1, 44, 34, 45, 96, 80, 1, 13, 31, 45};
assert(c2 == c3);
assert(c1.size() != 0);
assert(c3.get_allocator() == A(6));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c2));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c3));
}
{
int ab[] = {3, 4, 2, 8, 0, 1, 44, 34, 45, 96, 80, 1, 13, 31, 45};
assert(c2 == c3);
assert(c1.size() == 0);
assert(c3.get_allocator() == A(5));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c2));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c3));
}
{
int ab[] = {3, 4, 2, 8, 0, 1, 44, 34, 45, 96, 80, 1, 13, 31, 45};
assert(c2 == c3);
assert(c1.size() == 0);
assert(c3.get_allocator() == A());
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c2));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c3));
}
return 0;
// deque& operator=(const deque& c);
+#include "asan_testing.h"
#include <deque>
#include <cassert>
#include "test_macros.h"
C c;
c = x;
assert(c == x);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(x));
}
int main(int, char**)
l2 = l;
assert(l2 == l);
assert(l2.get_allocator() == test_allocator<int>(3));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(l));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(l2));
}
{
std::deque<int, other_allocator<int> > l(3, 2, other_allocator<int>(5));
l2 = l;
assert(l2 == l);
assert(l2.get_allocator() == other_allocator<int>(5));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(l));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(l2));
}
#if TEST_STD_VER >= 11
{
l2 = l;
assert(l2 == l);
assert(l2.get_allocator() == min_allocator<int>());
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(l));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(l2));
}
#endif
// deque& operator=(initializer_list<value_type> il);
+#include "asan_testing.h"
#include <deque>
#include <cassert>
assert(d[1] == 4);
assert(d[2] == 5);
assert(d[3] == 6);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(d));
}
{
std::deque<int, min_allocator<int>> d;
assert(d[1] == 4);
assert(d[2] == 5);
assert(d[3] == 6);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(d));
}
return 0;
// explicit deque(size_type n);
+#include "asan_testing.h"
#include <deque>
#include <cassert>
#include <cstddef>
assert(static_cast<unsigned>(DefaultOnly::count) == n);
assert(d.size() == n);
assert(static_cast<std::size_t>(std::distance(d.begin(), d.end())) == d.size());
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(d));
for (const_iterator i = d.begin(), e = d.end(); i != e; ++i)
assert(*i == T());
}
assert(static_cast<unsigned>(DefaultOnly::count) == n);
assert(d.size() == n);
assert(static_cast<std::size_t>(std::distance(d.begin(), d.end())) == d.size());
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(d));
#if TEST_STD_VER >= 11
for (const_iterator i = d.begin(), e = d.end(); i != e; ++i)
assert(*i == T());
C d(n, alloc);
assert(d.size() == n);
assert(d.get_allocator() == alloc);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(d));
}
#else
((void)n);
// deque(size_type n, const value_type& v);
+#include "asan_testing.h"
#include <deque>
#include <cassert>
#include <cstddef>
C d(n, x);
assert(d.size() == n);
assert(static_cast<std::size_t>(std::distance(d.begin(), d.end())) == d.size());
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(d));
for (const_iterator i = d.begin(), e = d.end(); i != e; ++i)
assert(*i == x);
}
// deque(size_type n, const value_type& v, const allocator_type& a);
+#include "asan_testing.h"
#include <deque>
#include <cassert>
#include <cstddef>
assert(d.get_allocator() == a);
assert(d.size() == n);
assert(static_cast<std::size_t>(std::distance(d.begin(), d.end())) == d.size());
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(d));
for (const_iterator i = d.begin(), e = d.end(); i != e; ++i)
assert(*i == x);
}
// typename deque<T, Allocator>::size_type
// erase(deque<T, Allocator>& c, const U& value);
+#include "asan_testing.h"
#include <deque>
#include <optional>
ASSERT_SAME_TYPE(typename S::size_type, decltype(std::erase(s, val)));
assert(expected_erased_count == std::erase(s, val));
assert(s == expected);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(s));
}
template <class S>
{
test<std::deque<int>>();
test<std::deque<int, min_allocator<int>>> ();
+ test<std::deque<int, safe_allocator<int>>> ();
test<std::deque<int, test_allocator<int>>> ();
test<std::deque<long>>();
// typename deque<T, Allocator>::size_type
// erase_if(deque<T, Allocator>& c, Predicate pred);
+#include "asan_testing.h"
#include <deque>
#include "test_macros.h"
ASSERT_SAME_TYPE(typename S::size_type, decltype(std::erase_if(s, p)));
assert(expected_erased_count == std::erase_if(s, p));
assert(s == expected);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(s));
}
template <typename S>
{
test<std::deque<int>>();
test<std::deque<int, min_allocator<int>>> ();
+ test<std::deque<int, safe_allocator<int>>> ();
test<std::deque<int, test_allocator<int>>> ();
test<std::deque<long>>();
// void clear() noexcept;
+#include "asan_testing.h"
#include <deque>
#include <cassert>
ASSERT_NOEXCEPT(c.clear());
c.clear();
assert(std::distance(c.begin(), c.end()) == 0);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
}
{
typedef int T;
ASSERT_NOEXCEPT(c.clear());
c.clear();
assert(std::distance(c.begin(), c.end()) == 0);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
c.clear();
assert(std::distance(c.begin(), c.end()) == 0);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
}
#if TEST_STD_VER >= 11
{
ASSERT_NOEXCEPT(c.clear());
c.clear();
assert(std::distance(c.begin(), c.end()) == 0);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
}
{
typedef int T;
ASSERT_NOEXCEPT(c.clear());
c.clear();
assert(std::distance(c.begin(), c.end()) == 0);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
c.clear();
assert(std::distance(c.begin(), c.end()) == 0);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
}
#endif
// UNSUPPORTED: c++03
+#include "asan_testing.h"
#include <deque>
#include <cassert>
#include <cstddef>
assert(c1.size() == c1_osize + 1);
assert(static_cast<std::size_t>(std::distance(c1.begin(), c1.end())) == c1.size());
assert(*i == Emplaceable(1, 2.5));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
}
template <class C>
for (int j = 0; j < N; ++j)
testN<std::deque<Emplaceable, min_allocator<Emplaceable>> >(rng[i], rng[j]);
}
+ {
+ int rng[] = {0, 1, 2, 3, 1023, 1024, 1025, 2047, 2048, 2049};
+ const int N = sizeof(rng)/sizeof(rng[0]);
+ for (int i = 0; i < N; ++i)
+ for (int j = 0; j < N; ++j)
+ testN<std::deque<Emplaceable, safe_allocator<Emplaceable>> >(rng[i], rng[j]);
+ }
return 0;
}
// template <class... Args> reference emplace_back(Args&&... args);
// return type is 'reference' in C++17; 'void' before
+#include "asan_testing.h"
#include <deque>
#include <cstddef>
#include <cassert>
== static_cast<std::ptrdiff_t>(c1.size()));
I i = c1.end();
assert(*--i == Emplaceable(1, 2.5));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
#if TEST_STD_VER > 14
assert(&(*i) == &ref);
#endif
std::deque<Tag_X, TaggingAllocator<Tag_X>> c;
c.emplace_back();
assert(c.size() == 1);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
c.emplace_back(1, 2, 3);
assert(c.size() == 2);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
c.emplace_front();
assert(c.size() == 3);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
c.emplace_front(1, 2, 3);
assert(c.size() == 4);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
}
return 0;
// template <class... Args> reference emplace_front(Args&&... args);
// return type is 'reference' in C++17; 'void' before
+#include "asan_testing.h"
#include <deque>
#include <cstddef>
#include <cassert>
== static_cast<std::ptrdiff_t>(c1.size()));
I i = c1.begin();
assert(*i == Emplaceable(1, 2.5));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
#if TEST_STD_VER > 14
assert(&res_ref == &(*i));
#endif
std::deque<Tag_X, TaggingAllocator<Tag_X>> c;
c.emplace_front();
assert(c.size() == 1);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
c.emplace_front(1, 2, 3);
assert(c.size() == 2);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
c.emplace_front();
assert(c.size() == 3);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
c.emplace_front(1, 2, 3);
assert(c.size() == 4);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
}
return 0;
// Erasing items from the beginning or the end of a deque shall not invalidate iterators
// to items that were not erased.
+#include "asan_testing.h"
#include <deque>
#include <cassert>
assert( it2 == it4);
assert( *it2 == *it4);
assert(&*it2 == &*it4);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
}
int main(int, char**)
// iterator erase(const_iterator p)
+#include "asan_testing.h"
#include <deque>
#include <algorithm>
#include <iterator>
v.erase(--v.end());
v.erase(v.begin());
assert(v.size() == 0);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(v));
}
#endif
// to items that were not erased.
+#include "asan_testing.h"
#include <deque>
#include <cstdint>
#include <cassert>
assert( it2 == it4);
assert( *it2 == *it4);
assert(&*it2 == &*it4);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
}
template <typename C>
assert( it2 == it4);
assert( *it2 == *it4);
assert(&*it2 == &*it4);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
}
// iterator erase(const_iterator f, const_iterator l)
+#include "asan_testing.h"
#include <deque>
#include <algorithm>
#include <iterator>
assert(i == c1.begin() + P);
assert(c1.size() == c1_osize - size);
assert(static_cast<std::size_t>(std::distance(c1.begin(), c1.end())) == c1.size());
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
i = c1.begin();
int j = 0;
for (; j < P; ++j, ++i)
Throws::sThrows = true;
v.erase(v.begin(), --v.end());
assert(v.size() == 1);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(v));
v.erase(v.begin(), v.end());
assert(v.size() == 0);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(v));
}
#endif
// iterator insert(const_iterator p, initializer_list<value_type> il);
+#include "asan_testing.h"
#include <deque>
#include <cassert>
assert(d[11] == 1);
assert(d[12] == 1);
assert(d[13] == 1);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(d));
}
{
std::deque<int, min_allocator<int>> d(10, 1);
assert(d[11] == 1);
assert(d[12] == 1);
assert(d[13] == 1);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(d));
}
return 0;
// template <class InputIterator>
// iterator insert (const_iterator p, InputIterator f, InputIterator l);
+#include "asan_testing.h"
#include <deque>
#include <cassert>
#include <cstddef>
assert(i == c1.begin() + P);
assert(c1.size() == c1_osize + c2.size());
assert(static_cast<std::size_t>(std::distance(c1.begin(), c1.end())) == c1.size());
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c2));
i = c1.begin();
for (int j = 0; j < P; ++j, ++i)
assert(*i == j);
assert(i == c1.begin() + P);
assert(c1.size() == c1_osize + c2.size());
assert(static_cast<std::size_t>(std::distance(c1.begin(), c1.end())) == c1.size());
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c2));
i = c1.begin();
for (int j = 0; j < P; ++j, ++i)
assert(*i == j);
testNI<std::deque<int> >(1500, 2000, 1000);
test_move<std::deque<MoveOnly, min_allocator<MoveOnly> > >();
}
+ {
+ int rng[] = {0, 1, 2, 3, 1023, 1024, 1025, 2047, 2048, 2049};
+ const int N = sizeof(rng)/sizeof(rng[0]);
+ for (int i = 0; i < N; ++i)
+ for (int j = 0; j < N; ++j)
+ for (int k = 0; k < N; ++k)
+ testN<std::deque<int, safe_allocator<int>> >(rng[i], rng[j], rng[k]);
+ testNI<std::deque<int> >(1500, 2000, 1000);
+ test_move<std::deque<MoveOnly, safe_allocator<MoveOnly> > >();
+ }
#endif
return 0;
// UNSUPPORTED: c++03
+#include "asan_testing.h"
#include <deque>
#include <cassert>
#include <cstddef>
assert(i == c1.begin() + P);
assert(c1.size() == c1_osize + 1);
assert(static_cast<std::size_t>(std::distance(c1.begin(), c1.end())) == c1.size());
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
i = c1.begin();
for (int j = 0; j < P; ++j, (void) ++i)
assert(*i == MoveOnly(j));
for (int j = 0; j < N; ++j)
testN<std::deque<MoveOnly, min_allocator<MoveOnly>> >(rng[i], rng[j]);
}
+ {
+ int rng[] = {0, 1, 2, 3, 1023, 1024, 1025, 2047, 2048, 2049};
+ const int N = sizeof(rng)/sizeof(rng[0]);
+ for (int i = 0; i < N; ++i)
+ for (int j = 0; j < N; ++j)
+ testN<std::deque<MoveOnly, safe_allocator<MoveOnly>> >(rng[i], rng[j]);
+ }
return 0;
}
// iterator insert (const_iterator p, size_type n, const value_type& v);
+#include "asan_testing.h"
#include <deque>
#include <cassert>
#include <cstddef>
assert(i == c1.begin() + P);
assert(c1.size() == c1_osize + size);
assert(static_cast<std::size_t>(std::distance(c1.begin(), c1.end())) == c1.size());
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
i = c1.begin();
for (int j = 0; j < P; ++j, ++i)
assert(*i == j);
testN<std::deque<int, min_allocator<int>> >(rng[i], rng[j], rng[k]);
self_reference_test<std::deque<int, min_allocator<int>> >();
}
+ {
+ int rng[] = {0, 1, 2, 3, 1023, 1024, 1025, 2047, 2048, 2049};
+ const int N = sizeof(rng)/sizeof(rng[0]);
+ for (int i = 0; i < N; ++i)
+ for (int j = 0; j < N; ++j)
+ for (int k = 0; k < N; ++k)
+ testN<std::deque<int, safe_allocator<int>> >(rng[i], rng[j], rng[k]);
+ self_reference_test<std::deque<int, safe_allocator<int>> >();
+ }
#endif
return 0;
// iterator insert (const_iterator p, const value_type& v);
+#include "asan_testing.h"
#include <deque>
#include <cassert>
#include <cstddef>
assert(i == c1.begin() + P);
assert(c1.size() == c1_osize + 1);
assert(static_cast<std::size_t>(std::distance(c1.begin(), c1.end())) == c1.size());
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
i = c1.begin();
for (int j = 0; j < P; ++j, ++i)
assert(*i == j);
testN<std::deque<int, min_allocator<int>> >(rng[i], rng[j]);
self_reference_test<std::deque<int, min_allocator<int>> >();
}
+ {
+ int rng[] = {0, 1, 2, 3, 1023, 1024, 1025, 2047, 2048, 2049};
+ const int N = sizeof(rng)/sizeof(rng[0]);
+ for (int i = 0; i < N; ++i)
+ for (int j = 0; j < N; ++j)
+ testN<std::deque<int, safe_allocator<int>> >(rng[i], rng[j]);
+ self_reference_test<std::deque<int, safe_allocator<int>> >();
+ }
#endif
return 0;
// Erasing items from the beginning or the end of a deque shall not invalidate iterators
// to items that were not erased.
+#include "asan_testing.h"
#include <deque>
#include <cassert>
assert( it2 == it4);
assert( *it2 == *it4);
assert(&*it2 == &*it4);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
}
int main(int, char**)
{
std::deque<int> queue;
- for (int i = 0; i < 20; ++i)
+ for (int i = 0; i < 4098; ++i)
queue.push_back(i);
while (queue.size() > 1)
{
test(queue);
queue.pop_back();
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(queue));
}
return 0;
// void pop_back()
+#include "asan_testing.h"
#include <deque>
#include <cassert>
#include <cstddef>
c1.pop_back();
assert(c1.size() == c1_osize - 1);
assert(static_cast<std::size_t>(std::distance(c1.begin(), c1.end())) == c1.size());
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
I i = c1.begin();
for (int j = 0; static_cast<std::size_t>(j) < c1.size(); ++j, ++i)
assert(*i == j);
for (int j = 0; j < N; ++j)
testN<std::deque<int, min_allocator<int>> >(rng[i], rng[j]);
}
+ {
+ int rng[] = {0, 1, 2, 3, 1023, 1024, 1025, 2047, 2048, 2049};
+ const int N = sizeof(rng)/sizeof(rng[0]);
+ for (int i = 0; i < N; ++i)
+ for (int j = 0; j < N; ++j)
+ testN<std::deque<int, safe_allocator<int>> >(rng[i], rng[j]);
+ }
#endif
return 0;
// Erasing items from the beginning or the end of a deque shall not invalidate iterators
// to items that were not erased.
+#include "asan_testing.h"
#include <deque>
#include <cassert>
int main(int, char**)
{
std::deque<int> queue;
- for (int i = 0; i < 20; ++i)
+ for (int i = 0; i < 4098; ++i)
queue.push_back(i);
while (queue.size() > 1)
{
test(queue);
queue.pop_back();
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(queue));
}
return 0;
// void pop_front()
+#include "asan_testing.h"
#include <deque>
#include <cassert>
#include <cstddef>
c1.pop_front();
assert(c1.size() == c1_osize - 1);
assert(static_cast<std::size_t>(std::distance(c1.begin(), c1.end())) == c1.size());
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
I i = c1.begin();
for (int j = 1; static_cast<std::size_t>(j) < c1.size(); ++j, ++i)
assert(*i == j);
// void pop_back();
// void pop_front();
+#include "asan_testing.h"
#include <deque>
#include <cassert>
{
C c = make<C>(size, rng[j]);
typename C::const_iterator it = c.begin();
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
for (int i = 0; i < size; ++i, ++it)
assert(*it == i);
}
// void pop_back();
// void pop_front();
+#include "asan_testing.h"
#include <deque>
#include <cassert>
{
C c = make<C>(size, rng[j]);
typename C::const_iterator it = c.begin();
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c));
for (int i = 0; i < size; ++i, (void) ++it)
assert(*it == MoveOnly(i));
}
for (int j = 0; j < N; ++j)
test<std::deque<MoveOnly, min_allocator<MoveOnly>> >(rng[j]);
}
+ {
+ int rng[] = {0, 1, 2, 3, 1023, 1024, 1025, 2046, 2047, 2048, 2049, 4094, 4095, 4096};
+ const int N = sizeof(rng)/sizeof(rng[0]);
+ for (int j = 0; j < N; ++j)
+ test<std::deque<MoveOnly, safe_allocator<MoveOnly>> >(rng[j]);
+ }
return 0;
}
// void push_front(const value_type& v);
+#include "asan_testing.h"
#include <deque>
#include <cassert>
#include <cstddef>
assert(static_cast<std::size_t>(std::distance(c1.begin(), c1.end())) == c1.size());
I i = c1.begin();
assert(*i == x);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
++i;
for (int j = 0; static_cast<std::size_t>(j) < c1_osize; ++j, ++i)
assert(*i == j);
for (int j = 0; j < N; ++j)
testN<std::deque<int, min_allocator<int>> >(rng[i], rng[j]);
}
+ {
+ int rng[] = {0, 1, 2, 3, 1023, 1024, 1025, 2047, 2048, 2049};
+ const int N = sizeof(rng)/sizeof(rng[0]);
+ for (int i = 0; i < N; ++i)
+ for (int j = 0; j < N; ++j)
+ testN<std::deque<int, safe_allocator<int>> >(rng[i], rng[j]);
+ }
#endif
return 0;
// void push_front(value_type&& v);
+#include "asan_testing.h"
#include <deque>
#include <cassert>
#include <cstddef>
assert(static_cast<std::size_t>(std::distance(c1.begin(), c1.end())) == c1.size());
I i = c1.begin();
assert(*i == MoveOnly(x));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
++i;
for (int j = 0; static_cast<std::size_t>(j) < c1_osize; ++j, (void) ++i)
assert(*i == MoveOnly(j));
const int N = sizeof(rng)/sizeof(rng[0]);
for (int i = 0; i < N; ++i)
for (int j = 0; j < N; ++j)
- testN<std::deque<MoveOnly, min_allocator<MoveOnly>> >(rng[i], rng[j]);
+ testN<std::deque<MoveOnly, safe_allocator<MoveOnly>> >(rng[i], rng[j]);
}
return 0;
// OutputIterator
// copy(InputIterator first, InputIterator last, OutputIterator result);
+#include "asan_testing.h"
#include <deque>
#include <cassert>
C c2 = make<C>(N);
assert(std::copy(c1.cbegin(), c1.cend(), c2.begin()) == c2.end());
assert(c1 == c2);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c2));
assert(std::copy(c2.cbegin(), c2.cend(), c1.begin()) == c1.end());
assert(c1 == c2);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c2));
assert(std::copy(c1.cbegin(), c1.cend(), RAI(c2.begin())) == RAI(c2.end()));
assert(c1 == c2);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c2));
assert(std::copy(c2.cbegin(), c2.cend(), RAI(c1.begin())) == RAI(c1.end()));
assert(c1 == c2);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c2));
assert(std::copy(RACI(c1.cbegin()), RACI(c1.cend()), c2.begin()) == c2.end());
assert(c1 == c2);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c2));
assert(std::copy(ICI(c2.cbegin()), ICI(c2.cend()), c1.begin()) == c1.end());
assert(c1 == c2);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c2));
}
int main(int, char**)
// OutputIterator
// copy_backward(InputIterator first, InputIterator last, OutputIterator result);
+#include "asan_testing.h"
#include <deque>
#include <cassert>
C c2 = make<C>(N);
assert(std::copy_backward(c1.cbegin(), c1.cend(), c2.end()) == c2.begin());
assert(c1 == c2);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c2));
assert(std::copy_backward(c2.cbegin(), c2.cend(), c1.end()) == c1.begin());
assert(c1 == c2);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c2));
assert(std::copy_backward(c1.cbegin(), c1.cend(), RAI(c2.end())) == RAI(c2.begin()));
assert(c1 == c2);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c2));
assert(std::copy_backward(c2.cbegin(), c2.cend(), RAI(c1.end())) == RAI(c1.begin()));
assert(c1 == c2);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c2));
assert(std::copy_backward(RACI(c1.cbegin()), RACI(c1.cend()), c2.end()) == c2.begin());
assert(c1 == c2);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c2));
assert(std::copy_backward(RACI(c2.cbegin()), RACI(c2.cend()), c1.end()) == c1.begin());
assert(c1 == c2);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c2));
}
int main(int, char**)
// OutputIterator
// move(InputIterator first, InputIterator last, OutputIterator result);
+#include "asan_testing.h"
#include <deque>
#include <cassert>
C c2 = make<C>(N);
assert(std::move(c1.cbegin(), c1.cend(), c2.begin()) == c2.end());
assert(c1 == c2);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c2));
assert(std::move(c2.cbegin(), c2.cend(), c1.begin()) == c1.end());
assert(c1 == c2);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c2));
assert(std::move(c1.cbegin(), c1.cend(), RAI(c2.begin())) == RAI(c2.end()));
assert(c1 == c2);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c2));
assert(std::move(c2.cbegin(), c2.cend(), RAI(c1.begin())) == RAI(c1.end()));
assert(c1 == c2);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c2));
assert(std::move(RACI(c1.cbegin()), RACI(c1.cend()), c2.begin()) == c2.end());
assert(c1 == c2);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c2));
assert(std::move(RACI(c2.cbegin()), RACI(c2.cend()), c1.begin()) == c1.end());
assert(c1 == c2);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c2));
}
int main(int, char**)
// OutputIterator
// move_backward(InputIterator first, InputIterator last, OutputIterator result);
+#include "asan_testing.h"
#include <deque>
#include <cassert>
C c2 = make<C>(N);
assert(std::move_backward(c1.cbegin(), c1.cend(), c2.end()) == c2.begin());
assert(c1 == c2);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c2));
assert(std::move_backward(c2.cbegin(), c2.cend(), c1.end()) == c1.begin());
assert(c1 == c2);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c2));
assert(std::move_backward(c1.cbegin(), c1.cend(), RAI(c2.end())) == RAI(c2.begin()));
assert(c1 == c2);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c2));
assert(std::move_backward(c2.cbegin(), c2.cend(), RAI(c1.end())) == RAI(c1.begin()));
assert(c1 == c2);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c2));
assert(std::move_backward(RACI(c1.cbegin()), RACI(c1.cend()), c2.end()) == c2.begin());
assert(c1 == c2);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c2));
assert(std::move_backward(RACI(c2.cbegin()), RACI(c2.cend()), c1.end()) == c1.begin());
assert(c1 == c2);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c2));
}
int main(int, char**)
// template <class T, class A>
// void swap(deque<T, A>& x, deque<T, A>& y);
+#include "asan_testing.h"
#include <deque>
#include <cassert>
#include "test_macros.h"
swap(c1, c2);
assert(c1 == c2_save);
assert(c2 == c1_save);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c2));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1_save));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c2_save));
}
int main(int, char**)
assert(c1.get_allocator().get_id() == 1);
assert((c2 == std::deque<int, A>(a1, a1+sizeof(a1)/sizeof(a1[0]))));
assert(c2.get_allocator().get_id() == 2);
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c2));
}
{
int a1[] = {1, 3, 7, 9, 10};
assert(c1.get_allocator() == A(2));
assert((c2 == std::deque<int, A>(a1, a1+sizeof(a1)/sizeof(a1[0]))));
assert(c2.get_allocator() == A(1));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c2));
}
#if TEST_STD_VER >= 11
{
assert(c1.get_allocator() == A());
assert((c2 == std::deque<int, A>(a1, a1+sizeof(a1)/sizeof(a1[0]))));
assert(c2.get_allocator() == A());
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c1));
+ LIBCPP_ASSERT(is_double_ended_contiguous_container_asan_correct(c2));
}
#endif
#define ASAN_TESTING_H
#include "test_macros.h"
+#include <vector>
#if TEST_HAS_FEATURE(address_sanitizer)
extern "C" int __sanitizer_verify_contiguous_container
c.data(), c.data() + c.size(), c.data() + c.capacity()) != 0;
return true;
}
-
#else
template <typename T, typename Alloc>
TEST_CONSTEXPR bool is_contiguous_container_asan_correct ( const std::vector<T, Alloc> &)
{
return true;
}
-#endif
+#endif // TEST_HAS_FEATURE(address_sanitizer)
+#if TEST_HAS_FEATURE(address_sanitizer) && _LIBCPP_CLANG_VER >= 1600
+extern "C" int __sanitizer_verify_double_ended_contiguous_container(
+ const void* beg, const void* con_beg, const void* con_end, const void* end);
+extern "C" bool __sanitizer_is_annotable(const void* address, const unsigned long size);
+#include <deque>
+
+template <class T, class Alloc>
+TEST_CONSTEXPR bool is_double_ended_contiguous_container_asan_correct(const std::deque<T, Alloc>& c) {
+ if (TEST_IS_CONSTANT_EVALUATED)
+ return true;
+ if (std::is_same<Alloc, std::allocator<T> >::value)
+ return c.__verify_asan_annotations();
+ return true;
+}
+#else
+# include <deque>
+template <class T, class Alloc>
+TEST_CONSTEXPR bool is_double_ended_contiguous_container_asan_correct(const std::deque<T, Alloc>&) {
+ return true;
+}
+#endif
#endif // ASAN_TESTING_H