From 3f0e834842b1423769e0b32fc4ad595a8cb3cc30 Mon Sep 17 00:00:00 2001
From: Kostya Serebryany <kcc@google.com>
Date: Tue, 2 Sep 2014 23:43:38 +0000
Subject: [PATCH] [asan] Make vector asan annotations exception-friendly

Fix vector asan annotations with RAII.
Add a test.
Also, remove one dead function.
Review: http://reviews.llvm.org/D4170

llvm-svn: 216995
---
 libcxx/include/vector                         |  74 ++++---
 .../sequences/vector/asan_throw.pass.cc       | 198 ++++++++++++++++++
 libcxx/test/support/asan_testing.h            |   4 +-
 3 files changed, 243 insertions(+), 33 deletions(-)
 create mode 100644 libcxx/test/containers/sequences/vector/asan_throw.pass.cc

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<allocator_type, __default_allocator_type>::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 <class _Tp, class _Allocator>
@@ -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 <class _Tp, class _Allocator>
-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<const_pointer>::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 <vector>
+#include <cassert>
+
+#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<X> 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<X> 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<X> 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<X> 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<X> 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<X> 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<X> 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<X> 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<X> 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 <typename T, typename Alloc>
 bool is_contiguous_container_asan_correct ( const std::vector<T, Alloc> &c )
 {
-    if ( std::is_same<Alloc, std::allocator<T>>::value && c.data() != NULL)
+    if ( std::is_same<Alloc, std::allocator<T> >::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<T, Alloc> &c )
 #endif
  
 
-#endif  // ASAN_TESTING_H
\ No newline at end of file
+#endif  // ASAN_TESTING_H