diff --git a/libcxx/include/vector b/libcxx/include/vector index 1be584d061c6..12195df56124 100644 --- a/libcxx/include/vector +++ b/libcxx/include/vector @@ -784,7 +784,6 @@ private: void >::type __construct_at_end(_ForwardIterator __first, _ForwardIterator __last); - void __move_construct_at_end(pointer __first, pointer __last); void __append(size_type __n); void __append(size_type __n, const_reference __x); _LIBCPP_INLINE_VISIBILITY @@ -836,7 +835,7 @@ private: // may not meet the AddressSanitizer alignment constraints. // See the documentation for __sanitizer_annotate_contiguous_container for more details. void __annotate_contiguous_container - (const void *__beg, const void *__end, const void *__old_mid, const void *__new_mid) + (const void *__beg, const void *__end, const void *__old_mid, const void *__new_mid) const { #ifndef _LIBCPP_HAS_NO_ASAN if (__beg && is_same::value) @@ -844,26 +843,42 @@ private: #endif } - void __annotate_new(size_type __current_size) + void __annotate_new(size_type __current_size) const { __annotate_contiguous_container(data(), data() + capacity(), data() + capacity(), data() + __current_size); } - void __annotate_delete() + void __annotate_delete() const { __annotate_contiguous_container(data(), data() + capacity(), data() + size(), data() + capacity()); } - void __annotate_increase(size_type __n) + void __annotate_increase(size_type __n) const { __annotate_contiguous_container(data(), data() + capacity(), data() + size(), data() + size() + __n); } - void __annotate_shrink(size_type __old_size) + void __annotate_shrink(size_type __old_size) const { __annotate_contiguous_container(data(), data() + capacity(), data() + __old_size, data() + size()); } + // The annotation for size increase should happen before the actual increase, + // but if an exception is thrown after that the annotation has to be undone. + struct __RAII_IncreaseAnnotator { + __RAII_IncreaseAnnotator(const vector &__v, size_type __n = 1) + : __commit(false), __v(__v), __n(__n) { + __v.__annotate_increase(__n); + } + void __done() { __commit = true; } + ~__RAII_IncreaseAnnotator() { + if (__commit) return; + __v.__annotate_shrink(__v.size() + __n); + } + bool __commit; + size_type __n; + const vector &__v; + }; }; template @@ -959,12 +974,13 @@ void vector<_Tp, _Allocator>::__construct_at_end(size_type __n) { allocator_type& __a = this->__alloc(); - __annotate_increase(__n); do { + __RAII_IncreaseAnnotator __annotator(*this); __alloc_traits::construct(__a, _VSTD::__to_raw_pointer(this->__end_)); ++this->__end_; --__n; + __annotator.__done(); } while (__n > 0); } @@ -980,12 +996,13 @@ void vector<_Tp, _Allocator>::__construct_at_end(size_type __n, const_reference __x) { allocator_type& __a = this->__alloc(); - __annotate_increase(__n); do { + __RAII_IncreaseAnnotator __annotator(*this); __alloc_traits::construct(__a, _VSTD::__to_raw_pointer(this->__end_), __x); ++this->__end_; --__n; + __annotator.__done(); } while (__n > 0); } @@ -1001,22 +1018,9 @@ vector<_Tp, _Allocator>::__construct_at_end(_ForwardIterator __first, _ForwardIt allocator_type& __a = this->__alloc(); for (; __first != __last; ++__first) { - __annotate_increase(1); + __RAII_IncreaseAnnotator __annotator(*this); __alloc_traits::construct(__a, _VSTD::__to_raw_pointer(this->__end_), *__first); - ++this->__end_; - } -} - -template -void -vector<_Tp, _Allocator>::__move_construct_at_end(pointer __first, pointer __last) -{ - allocator_type& __a = this->__alloc(); - for (; __first != __last; ++__first) - { - __annotate_increase(1); - __alloc_traits::construct(__a, _VSTD::__to_raw_pointer(this->__end_), - _VSTD::move(*__first)); + __annotator.__done(); ++this->__end_; } } @@ -1578,9 +1582,10 @@ vector<_Tp, _Allocator>::push_back(const_reference __x) { if (this->__end_ != this->__end_cap()) { - __annotate_increase(1); + __RAII_IncreaseAnnotator __annotator(*this); __alloc_traits::construct(this->__alloc(), _VSTD::__to_raw_pointer(this->__end_), __x); + __annotator.__done(); ++this->__end_; } else @@ -1596,10 +1601,11 @@ vector<_Tp, _Allocator>::push_back(value_type&& __x) { if (this->__end_ < this->__end_cap()) { - __annotate_increase(1); + __RAII_IncreaseAnnotator __annotator(*this); __alloc_traits::construct(this->__alloc(), _VSTD::__to_raw_pointer(this->__end_), _VSTD::move(__x)); + __annotator.__done(); ++this->__end_; } else @@ -1629,10 +1635,11 @@ vector<_Tp, _Allocator>::emplace_back(_Args&&... __args) { if (this->__end_ < this->__end_cap()) { - __annotate_increase(1); + __RAII_IncreaseAnnotator __annotator(*this); __alloc_traits::construct(this->__alloc(), _VSTD::__to_raw_pointer(this->__end_), _VSTD::forward<_Args>(__args)...); + __annotator.__done(); ++this->__end_; } else @@ -1712,7 +1719,7 @@ vector<_Tp, _Allocator>::insert(const_iterator __position, const_reference __x) pointer __p = this->__begin_ + (__position - begin()); if (this->__end_ < this->__end_cap()) { - __annotate_increase(1); + __RAII_IncreaseAnnotator __annotator(*this); if (__p == this->__end_) { __alloc_traits::construct(this->__alloc(), @@ -1727,6 +1734,7 @@ vector<_Tp, _Allocator>::insert(const_iterator __position, const_reference __x) ++__xr; *__p = *__xr; } + __annotator.__done(); } else { @@ -1752,7 +1760,7 @@ vector<_Tp, _Allocator>::insert(const_iterator __position, value_type&& __x) pointer __p = this->__begin_ + (__position - begin()); if (this->__end_ < this->__end_cap()) { - __annotate_increase(1); + __RAII_IncreaseAnnotator __annotator(*this); if (__p == this->__end_) { __alloc_traits::construct(this->__alloc(), @@ -1765,6 +1773,7 @@ vector<_Tp, _Allocator>::insert(const_iterator __position, value_type&& __x) __move_range(__p, this->__end_, __p + 1); *__p = _VSTD::move(__x); } + __annotator.__done(); } else { @@ -1791,7 +1800,7 @@ vector<_Tp, _Allocator>::emplace(const_iterator __position, _Args&&... __args) pointer __p = this->__begin_ + (__position - begin()); if (this->__end_ < this->__end_cap()) { - __annotate_increase(1); + __RAII_IncreaseAnnotator __annotator(*this); if (__p == this->__end_) { __alloc_traits::construct(this->__alloc(), @@ -1805,6 +1814,7 @@ vector<_Tp, _Allocator>::emplace(const_iterator __position, _Args&&... __args) __move_range(__p, this->__end_, __p + 1); *__p = _VSTD::move(__tmp); } + __annotator.__done(); } else { @@ -1843,8 +1853,9 @@ vector<_Tp, _Allocator>::insert(const_iterator __position, size_type __n, const_ } if (__n > 0) { - __annotate_increase(__n); + __RAII_IncreaseAnnotator __annotator(*this); __move_range(__p, __old_last, __p + __old_n); + __annotator.__done(); const_pointer __xr = pointer_traits::pointer_to(__x); if (__p <= __xr && __xr < this->__end_) __xr += __old_n; @@ -1954,8 +1965,9 @@ vector<_Tp, _Allocator>::insert(const_iterator __position, _ForwardIterator __fi } if (__n > 0) { - __annotate_increase(__n); + __RAII_IncreaseAnnotator __annotator(*this, __n); __move_range(__p, __old_last, __p + __old_n); + __annotator.__done(); _VSTD::copy(__first, __m, __p); } } diff --git a/libcxx/test/containers/sequences/vector/asan_throw.pass.cc b/libcxx/test/containers/sequences/vector/asan_throw.pass.cc new file mode 100644 index 000000000000..a1dce4a3b447 --- /dev/null +++ b/libcxx/test/containers/sequences/vector/asan_throw.pass.cc @@ -0,0 +1,198 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// Test asan vector annotations with a class that throws in a CTOR. + +#include +#include + +#include "asan_testing.h" + +class X { +public: + X(const X &x) { Init(x.a); } + X(char arg) { Init(arg); } + X() { Init(42); } + X &operator=(const X &x) { + Init(x.a); + return *this; + } + void Init(char arg) { + if (arg == 42) + throw 0; + if (arg == 66) + arg = 42; + a = arg; + } + char get() const { return a; } + void set(char arg) { a = arg; } + +private: + char a; +}; + +void test_push_back() { + std::vector v; + v.reserve(2); + v.push_back(X(2)); + assert(v.size() == 1); + try { + v.push_back(X(66)); + assert(0); + } catch (int e) { + assert(v.size() == 1); + } + assert(v.size() == 1); + assert(is_contiguous_container_asan_correct(v)); +} + +void test_emplace_back() { +#ifndef _LIBCPP_HAS_NO_VARIADICS + std::vector v; + v.reserve(2); + v.push_back(X(2)); + assert(v.size() == 1); + try { + v.emplace_back(42); + assert(0); + } catch (int e) { + assert(v.size() == 1); + } + assert(v.size() == 1); + assert(is_contiguous_container_asan_correct(v)); +#endif // _LIBCPP_HAS_NO_VARIADICS +} + +void test_insert_range() { + std::vector v; + v.reserve(4); + v.push_back(X(1)); + v.push_back(X(2)); + assert(v.size() == 2); + assert(v.capacity() >= 4); + try { + char a[2] = {21, 42}; + v.insert(v.end(), a, a + 2); + assert(0); + } catch (int e) { + assert(v.size() == 3); + } + assert(v.size() == 3); + assert(is_contiguous_container_asan_correct(v)); +} + +void test_insert() { + std::vector v; + v.reserve(3); + v.insert(v.end(), X(1)); + v.insert(v.begin(), X(2)); + assert(v.size() == 2); + try { + v.insert(v.end(), X(66)); + assert(0); + } catch (int e) { + assert(v.size() == 2); + } + assert(v.size() == 2); + assert(is_contiguous_container_asan_correct(v)); +} + +void test_emplace() { +#ifndef _LIBCPP_HAS_NO_VARIADICS + std::vector v; + v.reserve(3); + v.insert(v.end(), X(1)); + v.insert(v.begin(), X(2)); + assert(v.size() == 2); + try { + v.emplace(v.end(), 42); + assert(0); + } catch (int e) { + assert(v.size() == 2); + } + assert(v.size() == 2); + assert(is_contiguous_container_asan_correct(v)); +#endif // _LIBCPP_HAS_NO_VARIADICS +} + +void test_insert_range2() { + std::vector v; + v.reserve(4); + v.insert(v.end(), X(1)); + v.insert(v.begin(), X(2)); + assert(v.size() == 2); + assert(v.capacity() >= 4); + try { + char a[2] = {10, 42}; + v.insert(v.begin(), a, a + 2); + assert(0); + } catch (int e) { + assert(v.size() <= 4); + assert(is_contiguous_container_asan_correct(v)); + return; + } + assert(0); +} + +void test_insert_n() { + std::vector v; + v.reserve(10); + v.insert(v.end(), X(1)); + v.insert(v.begin(), X(2)); + assert(v.size() == 2); + try { + v.insert(v.begin(), 1, X(66)); + assert(0); + } catch (int e) { + assert(v.size() <= 3); + assert(is_contiguous_container_asan_correct(v)); + return; + } + assert(0); +} + +void test_resize() { + std::vector v; + v.reserve(3); + v.push_back(X(0)); + try { + v.resize(3); + assert(0); + } catch (int e) { + assert(v.size() == 1); + } + assert(v.size() == 1); + assert(is_contiguous_container_asan_correct(v)); +} + +void test_resize_param() { + std::vector v; + v.reserve(3); + v.push_back(X(0)); + try { + v.resize(3, X(66)); + assert(0); + } catch (int e) { + assert(v.size() == 1); + } + assert(v.size() == 1); + assert(is_contiguous_container_asan_correct(v)); +} + +int main() { + test_push_back(); + test_emplace_back(); + test_insert_range(); + test_insert(); + test_emplace(); + test_insert_range2(); + test_insert_n(); + test_resize(); + test_resize_param(); +} diff --git a/libcxx/test/support/asan_testing.h b/libcxx/test/support/asan_testing.h index c8797bde6f24..45ad04b1bb2c 100644 --- a/libcxx/test/support/asan_testing.h +++ b/libcxx/test/support/asan_testing.h @@ -19,7 +19,7 @@ extern "C" int __sanitizer_verify_contiguous_container template bool is_contiguous_container_asan_correct ( const std::vector &c ) { - if ( std::is_same>::value && c.data() != NULL) + if ( std::is_same >::value && c.data() != NULL) return __sanitizer_verify_contiguous_container ( c.data(), c.data() + c.size(), c.data() + c.capacity()) != 0; return true; @@ -34,4 +34,4 @@ bool is_contiguous_container_asan_correct ( const std::vector &c ) #endif -#endif // ASAN_TESTING_H \ No newline at end of file +#endif // ASAN_TESTING_H