Index: include/vector =================================================================== --- include/vector +++ include/vector @@ -788,7 +788,6 @@ 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 @@ -840,7 +839,7 @@ // 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) @@ -848,26 +847,42 @@ #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 @@ -963,12 +978,13 @@ 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); } @@ -984,12 +1000,13 @@ 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); } @@ -1005,22 +1022,9 @@ 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_; } } @@ -1582,9 +1586,10 @@ { 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 @@ -1600,10 +1605,11 @@ { 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 @@ -1633,10 +1639,11 @@ { 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 @@ -1716,7 +1723,7 @@ 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(), @@ -1731,6 +1738,7 @@ ++__xr; *__p = *__xr; } + __annotator.__done(); } else { @@ -1756,7 +1764,7 @@ 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(), @@ -1769,6 +1777,7 @@ __move_range(__p, this->__end_, __p + 1); *__p = _VSTD::move(__x); } + __annotator.__done(); } else { @@ -1795,7 +1804,7 @@ 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(), @@ -1809,6 +1818,7 @@ __move_range(__p, this->__end_, __p + 1); *__p = _VSTD::move(__tmp); } + __annotator.__done(); } else { @@ -1847,8 +1857,9 @@ } 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; @@ -1958,8 +1969,9 @@ } 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); } } Index: test/containers/sequences/vector/asan_throw.pass.cc =================================================================== --- /dev/null +++ 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(); +} Index: test/support/asan_testing.h =================================================================== --- test/support/asan_testing.h +++ test/support/asan_testing.h @@ -19,7 +19,7 @@ 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 @@ #endif -#endif // ASAN_TESTING_H \ No newline at end of file +#endif // ASAN_TESTING_H