[libcxx-commits] [libcxx] a9356a5 - [ASan][libcxx] Annotating std::vector with all allocators

Nikolas Klauser via libcxx-commits libcxx-commits at lists.llvm.org
Thu Feb 23 11:46:52 PST 2023


Author: Advenam Tacet
Date: 2023-02-23T20:46:05+01:00
New Revision: a9356a515b5a1a3637eaf5820fc0d2c0dad21a64

URL: https://github.com/llvm/llvm-project/commit/a9356a515b5a1a3637eaf5820fc0d2c0dad21a64
DIFF: https://github.com/llvm/llvm-project/commit/a9356a515b5a1a3637eaf5820fc0d2c0dad21a64.diff

LOG: [ASan][libcxx] Annotating std::vector with all allocators

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.

In revision D132522, support for non-aligned memory buffers (sharing
first/last granule with other objects) was added, therefore the
check for standard allocator is not necessary anymore.
This patch removes the check in std::vector annotation member
function (__annotate_contiguous_container) to support
different allocators.

Additionally, this revision fixes unpoisoning in std::vector.
It guarantees that __alloc_traits::deallocate may access returned memory.
Originally suggested in D144155 revision.

If you have any questions, please email:
 - advenam.tacet at trailofbits.com
 - disconnect3d at trailofbits.com

Reviewed By: #libc, #sanitizers, philnik, vitalybuka

Spies: hans, EricWF, philnik, #sanitizers, libcxx-commits

Differential Revision: https://reviews.llvm.org/D136765

Added: 
    

Modified: 
    libcxx/include/vector
    libcxx/test/libcxx/containers/sequences/vector/asan.pass.cpp
    libcxx/test/std/containers/sequences/vector/access.pass.cpp
    libcxx/test/std/containers/sequences/vector/contiguous.pass.cpp
    libcxx/test/std/containers/sequences/vector/vector.capacity/empty.pass.cpp
    libcxx/test/std/containers/sequences/vector/vector.capacity/reserve.pass.cpp
    libcxx/test/std/containers/sequences/vector/vector.capacity/resize_size.pass.cpp
    libcxx/test/std/containers/sequences/vector/vector.capacity/resize_size_value.pass.cpp
    libcxx/test/std/containers/sequences/vector/vector.capacity/shrink_to_fit.pass.cpp
    libcxx/test/std/containers/sequences/vector/vector.capacity/size.pass.cpp
    libcxx/test/std/containers/sequences/vector/vector.capacity/swap.pass.cpp
    libcxx/test/std/containers/sequences/vector/vector.cons/assign_copy.pass.cpp
    libcxx/test/std/containers/sequences/vector/vector.cons/assign_move.pass.cpp
    libcxx/test/std/containers/sequences/vector/vector.cons/construct_iter_iter.pass.cpp
    libcxx/test/std/containers/sequences/vector/vector.cons/construct_iter_iter_alloc.pass.cpp
    libcxx/test/std/containers/sequences/vector/vector.cons/construct_size.pass.cpp
    libcxx/test/std/containers/sequences/vector/vector.cons/construct_size_value.pass.cpp
    libcxx/test/std/containers/sequences/vector/vector.cons/construct_size_value_alloc.pass.cpp
    libcxx/test/std/containers/sequences/vector/vector.cons/copy.pass.cpp
    libcxx/test/std/containers/sequences/vector/vector.cons/copy_alloc.pass.cpp
    libcxx/test/std/containers/sequences/vector/vector.cons/initializer_list.pass.cpp
    libcxx/test/std/containers/sequences/vector/vector.cons/initializer_list_alloc.pass.cpp
    libcxx/test/std/containers/sequences/vector/vector.cons/move.pass.cpp
    libcxx/test/std/containers/sequences/vector/vector.cons/move_alloc.pass.cpp
    libcxx/test/std/containers/sequences/vector/vector.data/data.pass.cpp
    libcxx/test/std/containers/sequences/vector/vector.data/data_const.pass.cpp
    libcxx/test/std/containers/sequences/vector/vector.erasure/erase.pass.cpp
    libcxx/test/std/containers/sequences/vector/vector.erasure/erase_if.pass.cpp
    libcxx/test/std/containers/sequences/vector/vector.modifiers/clear.pass.cpp
    libcxx/test/std/containers/sequences/vector/vector.modifiers/emplace.pass.cpp
    libcxx/test/std/containers/sequences/vector/vector.modifiers/emplace_back.pass.cpp
    libcxx/test/std/containers/sequences/vector/vector.modifiers/emplace_extra.pass.cpp
    libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_iter_lvalue.pass.cpp
    libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_iter_rvalue.pass.cpp
    libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_iter_size_value.pass.cpp
    libcxx/test/std/containers/sequences/vector/vector.special/swap.pass.cpp
    libcxx/test/support/min_allocator.h

Removed: 
    


################################################################################
diff  --git a/libcxx/include/vector b/libcxx/include/vector
index 619e25822a462..d1e4b136ae2d9 100644
--- a/libcxx/include/vector
+++ b/libcxx/include/vector
@@ -439,11 +439,11 @@ private:
       _LIBCPP_CONSTEXPR __destroy_vector(vector& __vec) : __vec_(__vec) {}
 
       _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void operator()() {
-          __vec_.__annotate_delete();
           std::__debug_db_erase_c(std::addressof(__vec_));
 
           if (__vec_.__begin_ != nullptr) {
             __vec_.__clear();
+            __vec_.__annotate_delete();
             __alloc_traits::deallocate(__vec_.__alloc(), __vec_.__begin_, __vec_.capacity());
           }
       }
@@ -743,8 +743,25 @@ private:
                                          const void *__old_mid,
                                          const void *__new_mid) const
     {
-
+#  if _LIBCPP_CLANG_VER >= 1600
+      if (!__libcpp_is_constant_evaluated() && __beg)
+      // Implementation of __sanitizer_annotate_contiguous_container function changed with commit
+      // rGdd1b7b797a116eed588fd752fbe61d34deeb24e4 and supports:
+      //  - unaligned beginnings of buffers,
+      //  - shared first/last granule (if memory granule is shared with a 
diff erent object
+      //    just after the end of unaligned end/before the unaligned beginning, memory of that object won't be poisoned).
+      //
+      // Therefore, check for standard allocator is not necessary.
+#  else
+      // TODO LLVM18: Remove the special-casing
+      //
+      // Vectors annotations rely on __sanitizer_annotate_contiguous_container function,
+      // its implementation from LLVM15 (and earlier) requires that
+      // beginning of a containers data buffer is aligned to shadow granularity and
+      // memory just after is not shared with another object.
+      // Default allocator satisfies that.
       if (!__libcpp_is_constant_evaluated() && __beg && is_same<allocator_type, __default_allocator_type>::value)
+#  endif
         __sanitizer_annotate_contiguous_container(__beg, __end, __old_mid, __new_mid);
     }
 #else
@@ -866,6 +883,7 @@ private:
     if (__alloc() != __c.__alloc())
     {
       __clear();
+      __annotate_delete();
       __alloc_traits::deallocate(__alloc(), this->__begin_, capacity());
       this->__begin_ = this->__end_ = __end_cap() = nullptr;
     }
@@ -954,6 +972,7 @@ vector<_Tp, _Allocator>::__vdeallocate() _NOEXCEPT
     if (this->__begin_ != nullptr)
     {
         clear();
+        __annotate_delete();
         __alloc_traits::deallocate(this->__alloc(), this->__begin_, capacity());
         this->__begin_ = this->__end_ = this->__end_cap() = nullptr;
     }

diff  --git a/libcxx/test/libcxx/containers/sequences/vector/asan.pass.cpp b/libcxx/test/libcxx/containers/sequences/vector/asan.pass.cpp
index 40275f58cb32c..4032e86b70d4b 100644
--- a/libcxx/test/libcxx/containers/sequences/vector/asan.pass.cpp
+++ b/libcxx/test/libcxx/containers/sequences/vector/asan.pass.cpp
@@ -29,40 +29,65 @@ void do_exit() {
 
 int main(int, char**)
 {
-#if TEST_STD_VER >= 11
-    {
-        typedef int T;
-        typedef std::vector<T, min_allocator<T>> C;
-        const T t[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
-        C c(std::begin(t), std::end(t));
-        c.reserve(2*c.size());
-        volatile T foo = c[c.size()];    // bad, but not caught by ASAN
-        ((void)foo);
-    }
+#if TEST_STD_VER >= 11 && TEST_CLANG_VER < 1600
+  // TODO LLVM18: Remove the special-test
+  {
+    typedef int T;
+    typedef std::vector<T, min_allocator<T>> C;
+    const T t[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+    C c(std::begin(t), std::end(t));
+    c.reserve(2 * c.size());
+    volatile T foo = c[c.size()]; // bad, but not caught by ASAN
+    ((void)foo);
+  }
 #endif
 
-    {
-        typedef cpp17_input_iterator<int*> MyInputIter;
-        // Sould not trigger ASan.
-        std::vector<int> v;
-        v.reserve(1);
-        int i[] = {42};
-        v.insert(v.begin(), MyInputIter(i), MyInputIter(i + 1));
-        assert(v[0] == 42);
-        assert(is_contiguous_container_asan_correct(v));
-    }
+#if TEST_STD_VER >= 11 && TEST_CLANG_VER >= 1600
+  // TODO LLVM18: Remove the special-casing
+  {
+    typedef int T;
+    typedef cpp17_input_iterator<T*> MyInputIter;
+    std::vector<T, min_allocator<T>> v;
+    v.reserve(1);
+    int i[] = {42};
+    v.insert(v.begin(), MyInputIter(i), MyInputIter(i + 1));
+    assert(v[0] == 42);
+    assert(is_contiguous_container_asan_correct(v));
+  }
+  {
+    typedef char T;
+    typedef cpp17_input_iterator<T*> MyInputIter;
+    std::vector<T, unaligned_allocator<T>> v;
+    v.reserve(1);
+    char i[] = {'a', 'b'};
+    v.insert(v.begin(), MyInputIter(i), MyInputIter(i + 2));
+    assert(v[0] == 'a');
+    assert(v[1] == 'b');
+    assert(is_contiguous_container_asan_correct(v));
+  }
+#endif
+  {
+    typedef cpp17_input_iterator<int*> MyInputIter;
+    // Sould not trigger ASan.
+    std::vector<int> v;
+    v.reserve(1);
+    int i[] = {42};
+    v.insert(v.begin(), MyInputIter(i), MyInputIter(i + 1));
+    assert(v[0] == 42);
+    assert(is_contiguous_container_asan_correct(v));
+  }
 
-    __sanitizer_set_death_callback(do_exit);
-    {
-        typedef int T;
-        typedef std::vector<T> C;
-        const T t[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
-        C c(std::begin(t), std::end(t));
-        c.reserve(2*c.size());
-        assert(is_contiguous_container_asan_correct(c));
-        assert(!__sanitizer_verify_contiguous_container( c.data(), c.data() + 1, c.data() + c.capacity()));
-        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);
-    }
+  __sanitizer_set_death_callback(do_exit);
+  {
+    typedef int T;
+    typedef std::vector<T> C;
+    const T t[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+    C c(std::begin(t), std::end(t));
+    c.reserve(2 * c.size());
+    assert(is_contiguous_container_asan_correct(c));
+    assert(!__sanitizer_verify_contiguous_container(c.data(), c.data() + 1, c.data() + c.capacity()));
+    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);
+  }
 }

diff  --git a/libcxx/test/std/containers/sequences/vector/access.pass.cpp b/libcxx/test/std/containers/sequences/vector/access.pass.cpp
index 5989f1e037fd3..026cf92c2d723 100644
--- a/libcxx/test/std/containers/sequences/vector/access.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/access.pass.cpp
@@ -116,6 +116,7 @@ TEST_CONSTEXPR_CXX20 bool tests() {
     test<std::vector<int> >();
 #if TEST_STD_VER >= 11
     test<std::vector<int, min_allocator<int> > >();
+    test<std::vector<int, safe_allocator<int> > >();
 #endif
     return true;
 }

diff  --git a/libcxx/test/std/containers/sequences/vector/contiguous.pass.cpp b/libcxx/test/std/containers/sequences/vector/contiguous.pass.cpp
index d9f557e6c9770..ad18e65bb8eac 100644
--- a/libcxx/test/std/containers/sequences/vector/contiguous.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/contiguous.pass.cpp
@@ -48,6 +48,13 @@ TEST_CONSTEXPR_CXX20 bool tests()
     test_contiguous(C(A{}));
     test_contiguous(C(9, 11.0, A{}));
     }
+    {
+      typedef double T;
+      typedef safe_allocator<T> A;
+      typedef std::vector<T, A> C;
+      test_contiguous(C(A{}));
+      test_contiguous(C(9, 11.0, A{}));
+    }
 #endif
 
     return true;

diff  --git a/libcxx/test/std/containers/sequences/vector/vector.capacity/empty.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.capacity/empty.pass.cpp
index cc5f3bc90a670..ad4d7b310af76 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.capacity/empty.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.capacity/empty.pass.cpp
@@ -40,6 +40,16 @@ TEST_CONSTEXPR_CXX20 bool tests() {
     c.clear();
     assert(c.empty());
     }
+    {
+      typedef std::vector<int, safe_allocator<int>> C;
+      C c;
+      ASSERT_NOEXCEPT(c.empty());
+      assert(c.empty());
+      c.push_back(C::value_type(1));
+      assert(!c.empty());
+      c.clear();
+      assert(c.empty());
+    }
 #endif
 
     return true;

diff  --git a/libcxx/test/std/containers/sequences/vector/vector.capacity/reserve.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.capacity/reserve.pass.cpp
index 99c489601b0f5..387657cc3d2c1 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.capacity/reserve.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.capacity/reserve.pass.cpp
@@ -99,6 +99,23 @@ TEST_CONSTEXPR_CXX20 bool tests() {
         assert(v.capacity() == 150);
         assert(is_contiguous_container_asan_correct(v));
     }
+    {
+      std::vector<int, safe_allocator<int>> v;
+      v.reserve(10);
+      assert(v.capacity() >= 10);
+      assert(is_contiguous_container_asan_correct(v));
+    }
+    {
+      std::vector<int, safe_allocator<int>> v(100);
+      assert(v.capacity() == 100);
+      v.reserve(50);
+      assert(v.size() == 100);
+      assert(v.capacity() == 100);
+      v.reserve(150);
+      assert(v.size() == 100);
+      assert(v.capacity() == 150);
+      assert(is_contiguous_container_asan_correct(v));
+    }
 #endif
 #ifndef TEST_HAS_NO_EXCEPTIONS
     if (!TEST_IS_CONSTANT_EVALUATED) {

diff  --git a/libcxx/test/std/containers/sequences/vector/vector.capacity/resize_size.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.capacity/resize_size.pass.cpp
index 0b9db8e691c08..967daf99f27c7 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.capacity/resize_size.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.capacity/resize_size.pass.cpp
@@ -78,6 +78,17 @@ TEST_CONSTEXPR_CXX20 bool tests() {
         assert(v.capacity() >= 200);
         assert(is_contiguous_container_asan_correct(v));
     }
+    {
+      std::vector<int, safe_allocator<int>> v(100);
+      v.resize(50);
+      assert(v.size() == 50);
+      assert(v.capacity() == 100);
+      assert(is_contiguous_container_asan_correct(v));
+      v.resize(200);
+      assert(v.size() == 200);
+      assert(v.capacity() >= 200);
+      assert(is_contiguous_container_asan_correct(v));
+    }
 #endif
 
     return true;

diff  --git a/libcxx/test/std/containers/sequences/vector/vector.capacity/resize_size_value.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.capacity/resize_size_value.pass.cpp
index c0197982b4a45..a880bcf1f9499 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.capacity/resize_size_value.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.capacity/resize_size_value.pass.cpp
@@ -72,6 +72,33 @@ TEST_CONSTEXPR_CXX20 bool tests() {
         assert(v.capacity() >= 200);
         assert(is_contiguous_container_asan_correct(v));
     }
+    {
+      std::vector<int, safe_allocator<int>> v(100);
+      v.resize(50, 1);
+      assert(v.size() == 50);
+      assert(v.capacity() == 100);
+      assert(is_contiguous_container_asan_correct(v));
+      assert((v == std::vector<int, safe_allocator<int>>(50)));
+      v.resize(200, 1);
+      assert(v.size() == 200);
+      assert(v.capacity() >= 200);
+      assert(is_contiguous_container_asan_correct(v));
+      for (unsigned i = 0; i < 50; ++i)
+        assert(v[i] == 0);
+      for (unsigned i = 50; i < 200; ++i)
+        assert(v[i] == 1);
+    }
+    {
+      std::vector<int, safe_allocator<int>> v(100);
+      v.resize(50, 1);
+      assert(v.size() == 50);
+      assert(v.capacity() == 100);
+      assert(is_contiguous_container_asan_correct(v));
+      v.resize(200, 1);
+      assert(v.size() == 200);
+      assert(v.capacity() >= 200);
+      assert(is_contiguous_container_asan_correct(v));
+    }
 #endif
 
     return true;

diff  --git a/libcxx/test/std/containers/sequences/vector/vector.capacity/shrink_to_fit.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.capacity/shrink_to_fit.pass.cpp
index 3f2487948430d..8851e2a9ed0c7 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.capacity/shrink_to_fit.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.capacity/shrink_to_fit.pass.cpp
@@ -57,6 +57,15 @@ TEST_CONSTEXPR_CXX20 bool tests() {
         assert(v.size() == 101);
         assert(is_contiguous_container_asan_correct(v));
     }
+    {
+      std::vector<int, safe_allocator<int>> v(100);
+      v.push_back(1);
+      assert(is_contiguous_container_asan_correct(v));
+      v.shrink_to_fit();
+      assert(v.capacity() == 101);
+      assert(v.size() == 101);
+      assert(is_contiguous_container_asan_correct(v));
+    }
 #endif
 
     return true;

diff  --git a/libcxx/test/std/containers/sequences/vector/vector.capacity/size.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.capacity/size.pass.cpp
index aa41da69b31ab..f38aab1e13845 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.capacity/size.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.capacity/size.pass.cpp
@@ -57,6 +57,24 @@ TEST_CONSTEXPR_CXX20 bool tests()
     c.erase(c.begin());
     assert(c.size() == 0);
     }
+    {
+      typedef std::vector<int, safe_allocator<int>> C;
+      C c;
+      ASSERT_NOEXCEPT(c.size());
+      assert(c.size() == 0);
+      c.push_back(C::value_type(2));
+      assert(c.size() == 1);
+      c.push_back(C::value_type(1));
+      assert(c.size() == 2);
+      c.push_back(C::value_type(3));
+      assert(c.size() == 3);
+      c.erase(c.begin());
+      assert(c.size() == 2);
+      c.erase(c.begin());
+      assert(c.size() == 1);
+      c.erase(c.begin());
+      assert(c.size() == 0);
+    }
 #endif
 
     return true;

diff  --git a/libcxx/test/std/containers/sequences/vector/vector.capacity/swap.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.capacity/swap.pass.cpp
index 47a0435674136..1b2ac76eb2b5f 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.capacity/swap.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.capacity/swap.pass.cpp
@@ -45,6 +45,19 @@ TEST_CONSTEXPR_CXX20 bool tests() {
         assert(v2.capacity() == 100);
         assert(is_contiguous_container_asan_correct(v2));
     }
+    {
+      std::vector<int, safe_allocator<int>> v1(100);
+      std::vector<int, safe_allocator<int>> v2(200);
+      assert(is_contiguous_container_asan_correct(v1));
+      assert(is_contiguous_container_asan_correct(v2));
+      v1.swap(v2);
+      assert(v1.size() == 200);
+      assert(v1.capacity() == 200);
+      assert(is_contiguous_container_asan_correct(v1));
+      assert(v2.size() == 100);
+      assert(v2.capacity() == 100);
+      assert(is_contiguous_container_asan_correct(v2));
+    }
 #endif
 
     return true;

diff  --git a/libcxx/test/std/containers/sequences/vector/vector.cons/assign_copy.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.cons/assign_copy.pass.cpp
index f5b461b05a349..e0e227e8dc0c6 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.cons/assign_copy.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.cons/assign_copy.pass.cpp
@@ -78,6 +78,13 @@ TEST_CONSTEXPR_CXX20 bool tests() {
         assert(l2 == l);
         assert(l2.get_allocator() == min_allocator<int>());
     }
+    {
+      std::vector<int, safe_allocator<int> > l(3, 2, safe_allocator<int>());
+      std::vector<int, safe_allocator<int> > l2(l, safe_allocator<int>());
+      l2 = l;
+      assert(l2 == l);
+      assert(l2.get_allocator() == safe_allocator<int>());
+    }
 #endif
 
     return true;

diff  --git a/libcxx/test/std/containers/sequences/vector/vector.cons/assign_move.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.cons/assign_move.pass.cpp
index e6eba51a43bbb..58081fa0ecef5 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.cons/assign_move.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.cons/assign_move.pass.cpp
@@ -95,6 +95,24 @@ TEST_CONSTEXPR_CXX20 bool tests() {
         assert(l2.get_allocator() == lo.get_allocator());
         assert(is_contiguous_container_asan_correct(l2));
     }
+    {
+      std::vector<MoveOnly, safe_allocator<MoveOnly> > l((safe_allocator<MoveOnly>()));
+      std::vector<MoveOnly, safe_allocator<MoveOnly> > lo((safe_allocator<MoveOnly>()));
+      assert(is_contiguous_container_asan_correct(l));
+      assert(is_contiguous_container_asan_correct(lo));
+      for (int i = 1; i <= 3; ++i) {
+        l.push_back(i);
+        lo.push_back(i);
+      }
+      assert(is_contiguous_container_asan_correct(l));
+      assert(is_contiguous_container_asan_correct(lo));
+      std::vector<MoveOnly, safe_allocator<MoveOnly> > l2((safe_allocator<MoveOnly>()));
+      l2 = std::move(l);
+      assert(l2 == lo);
+      assert(l.empty());
+      assert(l2.get_allocator() == lo.get_allocator());
+      assert(is_contiguous_container_asan_correct(l2));
+    }
 
     return true;
 }

diff  --git a/libcxx/test/std/containers/sequences/vector/vector.cons/construct_iter_iter.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.cons/construct_iter_iter.pass.cpp
index 0f2fb36aaa67a..44364023c4e8e 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.cons/construct_iter_iter.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.cons/construct_iter_iter.pass.cpp
@@ -81,6 +81,13 @@ TEST_CONSTEXPR_CXX20 void basic_test_cases() {
       random_access_iterator<const int*>(a),
       random_access_iterator<const int*>(an));
   test<std::vector<int> >(a, an);
+  test<std::vector<int, safe_allocator<int> > >(
+      cpp17_input_iterator<const int*>(a), cpp17_input_iterator<const int*>(an));
+  test<std::vector<int, safe_allocator<int> > >(forward_iterator<const int*>(a), forward_iterator<const int*>(an));
+  test<std::vector<int, safe_allocator<int> > >(
+      bidirectional_iterator<const int*>(a), bidirectional_iterator<const int*>(an));
+  test<std::vector<int, safe_allocator<int> > >(
+      random_access_iterator<const int*>(a), random_access_iterator<const int*>(an));
 #endif
 }
 

diff  --git a/libcxx/test/std/containers/sequences/vector/vector.cons/construct_iter_iter_alloc.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.cons/construct_iter_iter_alloc.pass.cpp
index 5d38543963e5c..b1e34a56fbb8a 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.cons/construct_iter_iter_alloc.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.cons/construct_iter_iter_alloc.pass.cpp
@@ -83,6 +83,20 @@ TEST_CONSTEXPR_CXX20 void basic_tests() {
     test<std::vector<int, min_allocator<int> > >(a, an, alloc);
     test<std::vector<int, implicit_conv_allocator<int> > >(a, an, nullptr);
   }
+  {
+    int a[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 8, 7, 6, 5, 4, 3, 1, 0};
+    int* an = a + sizeof(a) / sizeof(a[0]);
+    safe_allocator<int> alloc;
+    test<std::vector<int, safe_allocator<int> > >(
+        cpp17_input_iterator<const int*>(a), cpp17_input_iterator<const int*>(an), alloc);
+    test<std::vector<int, safe_allocator<int> > >(
+        forward_iterator<const int*>(a), forward_iterator<const int*>(an), alloc);
+    test<std::vector<int, safe_allocator<int> > >(
+        bidirectional_iterator<const int*>(a), bidirectional_iterator<const int*>(an), alloc);
+    test<std::vector<int, safe_allocator<int> > >(
+        random_access_iterator<const int*>(a), random_access_iterator<const int*>(an), alloc);
+    test<std::vector<int, safe_allocator<int> > >(a, an, alloc);
+  }
 #endif
 }
 

diff  --git a/libcxx/test/std/containers/sequences/vector/vector.cons/construct_size.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.cons/construct_size.pass.cpp
index e4b1f431ae491..1dc7cd9bc7c1b 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.cons/construct_size.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.cons/construct_size.pass.cpp
@@ -59,6 +59,8 @@ TEST_CONSTEXPR_CXX20 bool tests() {
 #if TEST_STD_VER >= 11
     test<std::vector<int, min_allocator<int>>>(0);
     test<std::vector<int, min_allocator<int>>>(50);
+    test<std::vector<int, safe_allocator<int>>>(0);
+    test<std::vector<int, safe_allocator<int>>>(50);
 #endif
 
     return true;
@@ -76,6 +78,8 @@ int main(int, char**) {
 #if TEST_STD_VER >= 11
     test<std::vector<DefaultOnly, min_allocator<DefaultOnly>>>(0);
     test<std::vector<DefaultOnly, min_allocator<DefaultOnly>>>(500);
+    test<std::vector<DefaultOnly, safe_allocator<DefaultOnly>>>(0);
+    test<std::vector<DefaultOnly, safe_allocator<DefaultOnly>>>(500);
     test<std::vector<DefaultOnly, test_allocator<DefaultOnly>>>(0, test_allocator<DefaultOnly>(23));
     test<std::vector<DefaultOnly, test_allocator<DefaultOnly>>>(100, test_allocator<DefaultOnly>(23));
     assert(DefaultOnly::count == 0);

diff  --git a/libcxx/test/std/containers/sequences/vector/vector.cons/construct_size_value.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.cons/construct_size_value.pass.cpp
index 7f7777c2c97dc..0f24e77cd47e0 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.cons/construct_size_value.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.cons/construct_size_value.pass.cpp
@@ -40,6 +40,8 @@ TEST_CONSTEXPR_CXX20 bool tests() {
 #if TEST_STD_VER >= 11
     test<std::vector<int, min_allocator<int>> >(0, 3);
     test<std::vector<int, min_allocator<int>> >(50, 3);
+    test<std::vector<int, safe_allocator<int>> >(0, 3);
+    test<std::vector<int, safe_allocator<int>> >(50, 3);
 #endif
 
     return true;

diff  --git a/libcxx/test/std/containers/sequences/vector/vector.cons/construct_size_value_alloc.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.cons/construct_size_value_alloc.pass.cpp
index 17d3a5876bc5b..eb34de8bc9b31 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.cons/construct_size_value_alloc.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.cons/construct_size_value_alloc.pass.cpp
@@ -37,6 +37,8 @@ TEST_CONSTEXPR_CXX20 bool tests() {
 #if TEST_STD_VER >= 11
     test<std::vector<int, min_allocator<int>> >(0, 3, min_allocator<int>());
     test<std::vector<int, min_allocator<int>> >(50, 3, min_allocator<int>());
+    test<std::vector<int, safe_allocator<int>> >(0, 3, safe_allocator<int>());
+    test<std::vector<int, safe_allocator<int>> >(50, 3, safe_allocator<int>());
 #endif
 
     return true;

diff  --git a/libcxx/test/std/containers/sequences/vector/vector.cons/copy.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.cons/copy.pass.cpp
index 88822e9c18357..810ea4f9fa44d 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.cons/copy.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.cons/copy.pass.cpp
@@ -73,6 +73,7 @@ TEST_CONSTEXPR_CXX20 bool tests() {
         int a[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 8, 7, 6, 5, 4, 3, 1, 0};
         int* an = a + sizeof(a)/sizeof(a[0]);
         test(std::vector<int, min_allocator<int>>(a, an));
+        test(std::vector<int, safe_allocator<int>>(a, an));
     }
     {
         std::vector<int, min_allocator<int> > v(3, 2, min_allocator<int>());
@@ -84,6 +85,16 @@ TEST_CONSTEXPR_CXX20 bool tests() {
         assert(is_contiguous_container_asan_correct(v));
         assert(is_contiguous_container_asan_correct(v2));
     }
+    {
+      std::vector<int, safe_allocator<int> > v(3, 2, safe_allocator<int>());
+      std::vector<int, safe_allocator<int> > v2 = v;
+      assert(is_contiguous_container_asan_correct(v));
+      assert(is_contiguous_container_asan_correct(v2));
+      assert(v2 == v);
+      assert(v2.get_allocator() == v.get_allocator());
+      assert(is_contiguous_container_asan_correct(v));
+      assert(is_contiguous_container_asan_correct(v2));
+    }
 #endif
 
     return true;

diff  --git a/libcxx/test/std/containers/sequences/vector/vector.cons/copy_alloc.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.cons/copy_alloc.pass.cpp
index 70434bcfc578c..0c03c50aed2e8 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.cons/copy_alloc.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.cons/copy_alloc.pass.cpp
@@ -61,6 +61,7 @@ TEST_CONSTEXPR_CXX20 bool tests() {
         int a[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 8, 7, 6, 5, 4, 3, 1, 0};
         int* an = a + sizeof(a)/sizeof(a[0]);
         test(std::vector<int, min_allocator<int>>(a, an), min_allocator<int>());
+        test(std::vector<int, safe_allocator<int>>(a, an), safe_allocator<int>());
     }
     {
         std::vector<int, min_allocator<int> > l(3, 2, min_allocator<int>());
@@ -68,6 +69,12 @@ TEST_CONSTEXPR_CXX20 bool tests() {
         assert(l2 == l);
         assert(l2.get_allocator() == min_allocator<int>());
     }
+    {
+      std::vector<int, safe_allocator<int> > l(3, 2, safe_allocator<int>());
+      std::vector<int, safe_allocator<int> > l2(l, safe_allocator<int>());
+      assert(l2 == l);
+      assert(l2.get_allocator() == safe_allocator<int>());
+    }
 #endif
 
     return true;

diff  --git a/libcxx/test/std/containers/sequences/vector/vector.cons/initializer_list.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.cons/initializer_list.pass.cpp
index 6454f87f7e8ab..af2f3e40a91b5 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.cons/initializer_list.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.cons/initializer_list.pass.cpp
@@ -38,6 +38,15 @@ TEST_CONSTEXPR_CXX20 bool tests()
     assert(d[2] == 5);
     assert(d[3] == 6);
     }
+    {
+      std::vector<int, safe_allocator<int>> d = {3, 4, 5, 6};
+      assert(d.size() == 4);
+      assert(is_contiguous_container_asan_correct(d));
+      assert(d[0] == 3);
+      assert(d[1] == 4);
+      assert(d[2] == 5);
+      assert(d[3] == 6);
+    }
 
     return true;
 }

diff  --git a/libcxx/test/std/containers/sequences/vector/vector.cons/initializer_list_alloc.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.cons/initializer_list_alloc.pass.cpp
index 157aefc5af823..8396e84681620 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.cons/initializer_list_alloc.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.cons/initializer_list_alloc.pass.cpp
@@ -48,6 +48,22 @@ TEST_CONSTEXPR_CXX20 bool tests()
     assert(d.empty());
     assert(is_contiguous_container_asan_correct(d));
     }
+    {
+      std::vector<int, safe_allocator<int>> d({3, 4, 5, 6}, safe_allocator<int>());
+      assert(d.get_allocator() == safe_allocator<int>());
+      assert(d.size() == 4);
+      assert(is_contiguous_container_asan_correct(d));
+      assert(d[0] == 3);
+      assert(d[1] == 4);
+      assert(d[2] == 5);
+      assert(d[3] == 6);
+    }
+    {
+      std::vector<int, safe_allocator<int>> d({}, safe_allocator<int>());
+      assert(d.size() == 0);
+      assert(d.empty());
+      assert(is_contiguous_container_asan_correct(d));
+    }
 
     return true;
 }

diff  --git a/libcxx/test/std/containers/sequences/vector/vector.cons/move.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.cons/move.pass.cpp
index 0f3e737c1709b..95b896b41f530 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.cons/move.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.cons/move.pass.cpp
@@ -100,6 +100,34 @@ TEST_CONSTEXPR_CXX20 bool tests()
         assert(*j == 3);
         assert(is_contiguous_container_asan_correct(c2));
     }
+    {
+      std::vector<MoveOnly, safe_allocator<MoveOnly> > l((safe_allocator<MoveOnly>()));
+      std::vector<MoveOnly, safe_allocator<MoveOnly> > lo((safe_allocator<MoveOnly>()));
+      assert(is_contiguous_container_asan_correct(l));
+      assert(is_contiguous_container_asan_correct(lo));
+      for (int i = 1; i <= 3; ++i) {
+        l.push_back(i);
+        lo.push_back(i);
+      }
+      assert(is_contiguous_container_asan_correct(l));
+      assert(is_contiguous_container_asan_correct(lo));
+      std::vector<MoveOnly, safe_allocator<MoveOnly> > l2 = std::move(l);
+      assert(l2 == lo);
+      assert(l.empty());
+      assert(l2.get_allocator() == lo.get_allocator());
+      assert(is_contiguous_container_asan_correct(l2));
+    }
+    {
+      int a1[] = {1, 3, 7, 9, 10};
+      std::vector<int, safe_allocator<int> > c1(a1, a1 + sizeof(a1) / sizeof(a1[0]));
+      assert(is_contiguous_container_asan_correct(c1));
+      std::vector<int, safe_allocator<int> >::const_iterator i = c1.begin();
+      std::vector<int, safe_allocator<int> > c2                = std::move(c1);
+      assert(is_contiguous_container_asan_correct(c2));
+      std::vector<int, safe_allocator<int> >::iterator j = c2.erase(i);
+      assert(*j == 3);
+      assert(is_contiguous_container_asan_correct(c2));
+    }
     {
       alloc_stats.clear();
       using Vect = std::vector<int, test_allocator<int> >;

diff  --git a/libcxx/test/std/containers/sequences/vector/vector.cons/move_alloc.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.cons/move_alloc.pass.cpp
index 2b6705a38e860..fd0b995b54d54 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.cons/move_alloc.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.cons/move_alloc.pass.cpp
@@ -94,6 +94,23 @@ TEST_CONSTEXPR_CXX20 bool tests()
         assert(l2.get_allocator() == min_allocator<MoveOnly>());
         assert(is_contiguous_container_asan_correct(l2));
     }
+    {
+      std::vector<MoveOnly, safe_allocator<MoveOnly> > l((safe_allocator<MoveOnly>()));
+      std::vector<MoveOnly, safe_allocator<MoveOnly> > lo((safe_allocator<MoveOnly>()));
+      assert(is_contiguous_container_asan_correct(l));
+      assert(is_contiguous_container_asan_correct(lo));
+      for (int i = 1; i <= 3; ++i) {
+        l.push_back(i);
+        lo.push_back(i);
+      }
+      assert(is_contiguous_container_asan_correct(l));
+      assert(is_contiguous_container_asan_correct(lo));
+      std::vector<MoveOnly, safe_allocator<MoveOnly> > l2(std::move(l), safe_allocator<MoveOnly>());
+      assert(l2 == lo);
+      assert(l.empty());
+      assert(l2.get_allocator() == safe_allocator<MoveOnly>());
+      assert(is_contiguous_container_asan_correct(l2));
+    }
 
     return true;
 }

diff  --git a/libcxx/test/std/containers/sequences/vector/vector.data/data.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.data/data.pass.cpp
index 4d2cadc00ceb1..64b85424e54a5 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.data/data.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.data/data.pass.cpp
@@ -59,6 +59,21 @@ TEST_CONSTEXPR_CXX20 bool tests()
         assert(v.data() == std::addressof(v.front()));
         assert(is_contiguous_container_asan_correct(v));
     }
+    {
+      std::vector<int, safe_allocator<int>> v;
+      assert(v.data() == 0);
+      assert(is_contiguous_container_asan_correct(v));
+    }
+    {
+      std::vector<int, safe_allocator<int>> v(100);
+      assert(v.data() == std::addressof(v.front()));
+      assert(is_contiguous_container_asan_correct(v));
+    }
+    {
+      std::vector<Nasty, safe_allocator<Nasty>> v(100);
+      assert(v.data() == std::addressof(v.front()));
+      assert(is_contiguous_container_asan_correct(v));
+    }
 #endif
 
     return true;

diff  --git a/libcxx/test/std/containers/sequences/vector/vector.data/data_const.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.data/data_const.pass.cpp
index d314f90daf4b6..885caf272afbf 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.data/data_const.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.data/data_const.pass.cpp
@@ -59,6 +59,21 @@ TEST_CONSTEXPR_CXX20 bool tests()
         assert(v.data() == std::addressof(v.front()));
         assert(is_contiguous_container_asan_correct(v));
     }
+    {
+      const std::vector<int, safe_allocator<int>> v;
+      assert(v.data() == 0);
+      assert(is_contiguous_container_asan_correct(v));
+    }
+    {
+      const std::vector<int, safe_allocator<int>> v(100);
+      assert(v.data() == &v.front());
+      assert(is_contiguous_container_asan_correct(v));
+    }
+    {
+      std::vector<Nasty, safe_allocator<Nasty>> v(100);
+      assert(v.data() == std::addressof(v.front()));
+      assert(is_contiguous_container_asan_correct(v));
+    }
 #endif
 
     return true;

diff  --git a/libcxx/test/std/containers/sequences/vector/vector.erasure/erase.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.erasure/erase.pass.cpp
index 72441c12dac8e..01a43a9570830 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.erasure/erase.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.erasure/erase.pass.cpp
@@ -68,6 +68,7 @@ TEST_CONSTEXPR_CXX20 bool tests()
     test<std::vector<int>>();
     test<std::vector<int, min_allocator<int>>> ();
     test<std::vector<int, test_allocator<int>>> ();
+    test<std::vector<int, safe_allocator<int>>>();
 
     test<std::vector<long>>();
     test<std::vector<double>>();

diff  --git a/libcxx/test/std/containers/sequences/vector/vector.erasure/erase_if.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.erasure/erase_if.pass.cpp
index 1fcc1c9424868..3da8eca862a28 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.erasure/erase_if.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.erasure/erase_if.pass.cpp
@@ -70,6 +70,7 @@ TEST_CONSTEXPR_CXX20 bool tests()
     test<std::vector<int>>();
     test<std::vector<int, min_allocator<int>>> ();
     test<std::vector<int, test_allocator<int>>> ();
+    test<std::vector<int, safe_allocator<int>>>();
 
     test<std::vector<long>>();
     test<std::vector<double>>();

diff  --git a/libcxx/test/std/containers/sequences/vector/vector.modifiers/clear.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.modifiers/clear.pass.cpp
index 5a1e134f66fdf..9390b8309736f 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.modifiers/clear.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.modifiers/clear.pass.cpp
@@ -38,6 +38,15 @@ TEST_CONSTEXPR_CXX20 bool tests()
     LIBCPP_ASSERT(c.__invariants());
     LIBCPP_ASSERT(is_contiguous_container_asan_correct(c));
     }
+    {
+      int a[] = {1, 2, 3};
+      std::vector<int, safe_allocator<int>> c(a, a + 3);
+      ASSERT_NOEXCEPT(c.clear());
+      c.clear();
+      assert(c.empty());
+      LIBCPP_ASSERT(c.__invariants());
+      LIBCPP_ASSERT(is_contiguous_container_asan_correct(c));
+    }
 #endif
 
     return true;

diff  --git a/libcxx/test/std/containers/sequences/vector/vector.modifiers/emplace.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.modifiers/emplace.pass.cpp
index f2c656cd27b2f..4f5e36abb6bb8 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.modifiers/emplace.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.modifiers/emplace.pass.cpp
@@ -132,6 +132,30 @@ TEST_CONSTEXPR_CXX20 bool tests()
         assert(c.back().geti() == 3);
         assert(c.back().getd() == 4.5);
     }
+    {
+      std::vector<A, safe_allocator<A> > c;
+      std::vector<A, safe_allocator<A> >::iterator i = c.emplace(c.cbegin(), 2, 3.5);
+      assert(i == c.begin());
+      assert(c.size() == 1);
+      assert(c.front().geti() == 2);
+      assert(c.front().getd() == 3.5);
+      i = c.emplace(c.cend(), 3, 4.5);
+      assert(i == c.end() - 1);
+      assert(c.size() == 2);
+      assert(c.front().geti() == 2);
+      assert(c.front().getd() == 3.5);
+      assert(c.back().geti() == 3);
+      assert(c.back().getd() == 4.5);
+      i = c.emplace(c.cbegin() + 1, 4, 6.5);
+      assert(i == c.begin() + 1);
+      assert(c.size() == 3);
+      assert(c.front().geti() == 2);
+      assert(c.front().getd() == 3.5);
+      assert(c[1].geti() == 4);
+      assert(c[1].getd() == 6.5);
+      assert(c.back().geti() == 3);
+      assert(c.back().getd() == 4.5);
+    }
 
     return true;
 }

diff  --git a/libcxx/test/std/containers/sequences/vector/vector.modifiers/emplace_back.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.modifiers/emplace_back.pass.cpp
index bb7fa745ac355..730ed32bd7334 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.modifiers/emplace_back.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.modifiers/emplace_back.pass.cpp
@@ -130,6 +130,33 @@ TEST_CONSTEXPR_CXX20 bool tests()
         assert(is_contiguous_container_asan_correct(c));
         c.emplace_back(3, 4.5);
         assert(c.size() == 2);
+#endif
+        assert(c.front().geti() == 2);
+        assert(c.front().getd() == 3.5);
+        assert(c.back().geti() == 3);
+        assert(c.back().getd() == 4.5);
+        assert(is_contiguous_container_asan_correct(c));
+    }
+    {
+      std::vector<A, safe_allocator<A> > c;
+#if TEST_STD_VER > 14
+      A& r1 = c.emplace_back(2, 3.5);
+      assert(c.size() == 1);
+      assert(&r1 == &c.back());
+      assert(c.front().geti() == 2);
+      assert(c.front().getd() == 3.5);
+      assert(is_contiguous_container_asan_correct(c));
+      A& r2 = c.emplace_back(3, 4.5);
+      assert(c.size() == 2);
+      assert(&r2 == &c.back());
+#else
+        c.emplace_back(2, 3.5);
+        assert(c.size() == 1);
+        assert(c.front().geti() == 2);
+        assert(c.front().getd() == 3.5);
+        assert(is_contiguous_container_asan_correct(c));
+        c.emplace_back(3, 4.5);
+        assert(c.size() == 2);
 #endif
         assert(c.front().geti() == 2);
         assert(c.front().getd() == 3.5);

diff  --git a/libcxx/test/std/containers/sequences/vector/vector.modifiers/emplace_extra.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.modifiers/emplace_extra.pass.cpp
index 3e1a1d82c2ebb..8c0caa74a9322 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.modifiers/emplace_extra.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.modifiers/emplace_extra.pass.cpp
@@ -56,6 +56,24 @@ TEST_CONSTEXPR_CXX20 bool tests() {
         assert(v[0] == 3);
         assert(is_contiguous_container_asan_correct(v));
     }
+    {
+      std::vector<int, safe_allocator<int>> v;
+      v.reserve(3);
+      assert(is_contiguous_container_asan_correct(v));
+      v = {1, 2, 3};
+      v.emplace(v.begin(), v.back());
+      assert(v[0] == 3);
+      assert(is_contiguous_container_asan_correct(v));
+    }
+    {
+      std::vector<int, safe_allocator<int>> v;
+      v.reserve(4);
+      assert(is_contiguous_container_asan_correct(v));
+      v = {1, 2, 3};
+      v.emplace(v.begin(), v.back());
+      assert(v[0] == 3);
+      assert(is_contiguous_container_asan_correct(v));
+    }
     {
         std::vector<int> v;
         v.reserve(8);

diff  --git a/libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_iter_lvalue.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_iter_lvalue.pass.cpp
index c8bb994258894..8985435e11c9b 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_iter_lvalue.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_iter_lvalue.pass.cpp
@@ -114,6 +114,20 @@ TEST_CONSTEXPR_CXX20 bool test() {
         for (++j; j < 101; ++j)
             assert(v[j] == 0);
     }
+    {
+      std::vector<int, safe_allocator<int>> v(100);
+      const int lvalue                                  = 1;
+      std::vector<int, safe_allocator<int>>::iterator i = v.insert(v.cbegin() + 10, lvalue);
+      assert(v.size() == 101);
+      assert(is_contiguous_container_asan_correct(v));
+      assert(i == v.begin() + 10);
+      int j;
+      for (j = 0; j < 10; ++j)
+        assert(v[j] == 0);
+      assert(v[j] == 1);
+      for (++j; j < 101; ++j)
+        assert(v[j] == 0);
+    }
 #endif
 
     return true;

diff  --git a/libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_iter_rvalue.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_iter_rvalue.pass.cpp
index 11a7175c61967..4619d7a7d1b6f 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_iter_rvalue.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_iter_rvalue.pass.cpp
@@ -62,6 +62,19 @@ TEST_CONSTEXPR_CXX20 bool tests()
         for (++j; j < 101; ++j)
             assert(v[j] == MoveOnly());
     }
+    {
+      std::vector<MoveOnly, safe_allocator<MoveOnly> > v(100);
+      std::vector<MoveOnly, safe_allocator<MoveOnly> >::iterator i = v.insert(v.cbegin() + 10, MoveOnly(3));
+      assert(v.size() == 101);
+      assert(is_contiguous_container_asan_correct(v));
+      assert(i == v.begin() + 10);
+      int j;
+      for (j = 0; j < 10; ++j)
+        assert(v[j] == MoveOnly());
+      assert(v[j] == MoveOnly(3));
+      for (++j; j < 101; ++j)
+        assert(v[j] == MoveOnly());
+    }
 
     return true;
 }

diff  --git a/libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_iter_size_value.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_iter_size_value.pass.cpp
index c629428828081..f6e447b2ff294 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_iter_size_value.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_iter_size_value.pass.cpp
@@ -97,18 +97,18 @@ TEST_CONSTEXPR_CXX20 bool tests()
             assert(v[j] == 0);
     }
     {
-        std::vector<int, min_allocator<int>> v(100);
-        std::vector<int, min_allocator<int>>::iterator i = v.insert(v.cbegin() + 10, 5, 1);
-        assert(v.size() == 105);
-        assert(is_contiguous_container_asan_correct(v));
-        assert(i == v.begin() + 10);
-        int j;
-        for (j = 0; j < 10; ++j)
-            assert(v[j] == 0);
-        for (; j < 15; ++j)
-            assert(v[j] == 1);
-        for (++j; j < 105; ++j)
-            assert(v[j] == 0);
+      std::vector<int, safe_allocator<int>> v(100);
+      std::vector<int, safe_allocator<int>>::iterator i = v.insert(v.cbegin() + 10, 5, 1);
+      assert(v.size() == 105);
+      assert(is_contiguous_container_asan_correct(v));
+      assert(i == v.begin() + 10);
+      int j;
+      for (j = 0; j < 10; ++j)
+        assert(v[j] == 0);
+      for (; j < 15; ++j)
+        assert(v[j] == 1);
+      for (++j; j < 105; ++j)
+        assert(v[j] == 0);
     }
 #endif
 

diff  --git a/libcxx/test/std/containers/sequences/vector/vector.special/swap.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.special/swap.pass.cpp
index e1f45208539ac..98aece64c80e7 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.special/swap.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.special/swap.pass.cpp
@@ -19,6 +19,81 @@
 #include "min_allocator.h"
 #include "asan_testing.h"
 
+template <typename A>
+TEST_CONSTEXPR_CXX20 void test_with_allocator() {
+  {
+    int a1[] = {1, 3, 7, 9, 10};
+    int a2[] = {0, 2, 4, 5, 6, 8, 11};
+    std::vector<int, A> c1(a1, a1 + sizeof(a1) / sizeof(a1[0]));
+    std::vector<int, A> c2(a2, a2 + sizeof(a2) / sizeof(a2[0]));
+    assert(is_contiguous_container_asan_correct(c1));
+    assert(is_contiguous_container_asan_correct(c2));
+    swap(c1, c2);
+    assert((c1 == std::vector<int, A>(a2, a2 + sizeof(a2) / sizeof(a2[0]))));
+    assert((c2 == std::vector<int, A>(a1, a1 + sizeof(a1) / sizeof(a1[0]))));
+    assert(is_contiguous_container_asan_correct(c1));
+    assert(is_contiguous_container_asan_correct(c2));
+  }
+  {
+    int a1[] = {1, 3, 7, 9, 10};
+    int a2[] = {0, 2, 4, 5, 6, 8, 11};
+    std::vector<int, A> c1(a1, a1);
+    std::vector<int, A> c2(a2, a2 + sizeof(a2) / sizeof(a2[0]));
+    assert(is_contiguous_container_asan_correct(c1));
+    assert(is_contiguous_container_asan_correct(c2));
+    swap(c1, c2);
+    assert((c1 == std::vector<int, A>(a2, a2 + sizeof(a2) / sizeof(a2[0]))));
+    assert(c2.empty());
+    assert(std::distance(c2.begin(), c2.end()) == 0);
+    assert(is_contiguous_container_asan_correct(c1));
+    assert(is_contiguous_container_asan_correct(c2));
+  }
+  {
+    int a1[] = {1, 3, 7, 9, 10};
+    int a2[] = {0, 2, 4, 5, 6, 8, 11};
+    std::vector<int, A> c1(a1, a1 + sizeof(a1) / sizeof(a1[0]));
+    std::vector<int, A> c2(a2, a2);
+    assert(is_contiguous_container_asan_correct(c1));
+    assert(is_contiguous_container_asan_correct(c2));
+    swap(c1, c2);
+    assert(c1.empty());
+    assert(std::distance(c1.begin(), c1.end()) == 0);
+    assert((c2 == std::vector<int, A>(a1, a1 + sizeof(a1) / sizeof(a1[0]))));
+    assert(is_contiguous_container_asan_correct(c1));
+    assert(is_contiguous_container_asan_correct(c2));
+  }
+  {
+    int a1[] = {1, 3, 7, 9, 10};
+    int a2[] = {0, 2, 4, 5, 6, 8, 11};
+    std::vector<int, A> c1(a1, a1);
+    std::vector<int, A> c2(a2, a2);
+    assert(is_contiguous_container_asan_correct(c1));
+    assert(is_contiguous_container_asan_correct(c2));
+    swap(c1, c2);
+    assert(c1.empty());
+    assert(std::distance(c1.begin(), c1.end()) == 0);
+    assert(c2.empty());
+    assert(std::distance(c2.begin(), c2.end()) == 0);
+    assert(is_contiguous_container_asan_correct(c1));
+    assert(is_contiguous_container_asan_correct(c2));
+  }
+  {
+    int a1[] = {1, 3, 7, 9, 10};
+    int a2[] = {0, 2, 4, 5, 6, 8, 11};
+    std::vector<int, A> c1(a1, a1 + sizeof(a1) / sizeof(a1[0]), A());
+    std::vector<int, A> c2(a2, a2 + sizeof(a2) / sizeof(a2[0]), A());
+    assert(is_contiguous_container_asan_correct(c1));
+    assert(is_contiguous_container_asan_correct(c2));
+    swap(c1, c2);
+    assert((c1 == std::vector<int, A>(a2, a2 + sizeof(a2) / sizeof(a2[0]))));
+    assert(c1.get_allocator() == A());
+    assert((c2 == std::vector<int, A>(a1, a1 + sizeof(a1) / sizeof(a1[0]))));
+    assert(c2.get_allocator() == A());
+    assert(is_contiguous_container_asan_correct(c1));
+    assert(is_contiguous_container_asan_correct(c2));
+  }
+}
+
 TEST_CONSTEXPR_CXX20 bool tests()
 {
     {
@@ -106,78 +181,8 @@ TEST_CONSTEXPR_CXX20 bool tests()
         assert(is_contiguous_container_asan_correct(c2));
     }
 #if TEST_STD_VER >= 11
-    {
-        int a1[] = {1, 3, 7, 9, 10};
-        int a2[] = {0, 2, 4, 5, 6, 8, 11};
-        std::vector<int, min_allocator<int>> c1(a1, a1+sizeof(a1)/sizeof(a1[0]));
-        std::vector<int, min_allocator<int>> c2(a2, a2+sizeof(a2)/sizeof(a2[0]));
-        assert(is_contiguous_container_asan_correct(c1));
-        assert(is_contiguous_container_asan_correct(c2));
-        swap(c1, c2);
-        assert((c1 == std::vector<int, min_allocator<int>>(a2, a2+sizeof(a2)/sizeof(a2[0]))));
-        assert((c2 == std::vector<int, min_allocator<int>>(a1, a1+sizeof(a1)/sizeof(a1[0]))));
-        assert(is_contiguous_container_asan_correct(c1));
-        assert(is_contiguous_container_asan_correct(c2));
-    }
-    {
-        int a1[] = {1, 3, 7, 9, 10};
-        int a2[] = {0, 2, 4, 5, 6, 8, 11};
-        std::vector<int, min_allocator<int>> c1(a1, a1);
-        std::vector<int, min_allocator<int>> c2(a2, a2+sizeof(a2)/sizeof(a2[0]));
-        assert(is_contiguous_container_asan_correct(c1));
-        assert(is_contiguous_container_asan_correct(c2));
-        swap(c1, c2);
-        assert((c1 == std::vector<int, min_allocator<int>>(a2, a2+sizeof(a2)/sizeof(a2[0]))));
-        assert(c2.empty());
-        assert(std::distance(c2.begin(), c2.end()) == 0);
-        assert(is_contiguous_container_asan_correct(c1));
-        assert(is_contiguous_container_asan_correct(c2));
-    }
-    {
-        int a1[] = {1, 3, 7, 9, 10};
-        int a2[] = {0, 2, 4, 5, 6, 8, 11};
-        std::vector<int, min_allocator<int>> c1(a1, a1+sizeof(a1)/sizeof(a1[0]));
-        std::vector<int, min_allocator<int>> c2(a2, a2);
-        assert(is_contiguous_container_asan_correct(c1));
-        assert(is_contiguous_container_asan_correct(c2));
-        swap(c1, c2);
-        assert(c1.empty());
-        assert(std::distance(c1.begin(), c1.end()) == 0);
-        assert((c2 == std::vector<int, min_allocator<int>>(a1, a1+sizeof(a1)/sizeof(a1[0]))));
-        assert(is_contiguous_container_asan_correct(c1));
-        assert(is_contiguous_container_asan_correct(c2));
-    }
-    {
-        int a1[] = {1, 3, 7, 9, 10};
-        int a2[] = {0, 2, 4, 5, 6, 8, 11};
-        std::vector<int, min_allocator<int>> c1(a1, a1);
-        std::vector<int, min_allocator<int>> c2(a2, a2);
-        assert(is_contiguous_container_asan_correct(c1));
-        assert(is_contiguous_container_asan_correct(c2));
-        swap(c1, c2);
-        assert(c1.empty());
-        assert(std::distance(c1.begin(), c1.end()) == 0);
-        assert(c2.empty());
-        assert(std::distance(c2.begin(), c2.end()) == 0);
-        assert(is_contiguous_container_asan_correct(c1));
-        assert(is_contiguous_container_asan_correct(c2));
-    }
-    {
-        int a1[] = {1, 3, 7, 9, 10};
-        int a2[] = {0, 2, 4, 5, 6, 8, 11};
-        typedef min_allocator<int> A;
-        std::vector<int, A> c1(a1, a1+sizeof(a1)/sizeof(a1[0]), A());
-        std::vector<int, A> c2(a2, a2+sizeof(a2)/sizeof(a2[0]), A());
-        assert(is_contiguous_container_asan_correct(c1));
-        assert(is_contiguous_container_asan_correct(c2));
-        swap(c1, c2);
-        assert((c1 == std::vector<int, A>(a2, a2+sizeof(a2)/sizeof(a2[0]))));
-        assert(c1.get_allocator() == A());
-        assert((c2 == std::vector<int, A>(a1, a1+sizeof(a1)/sizeof(a1[0]))));
-        assert(c2.get_allocator() == A());
-        assert(is_contiguous_container_asan_correct(c1));
-        assert(is_contiguous_container_asan_correct(c2));
-    }
+    test_with_allocator<min_allocator<int>>();
+    test_with_allocator<safe_allocator<int>>();
 #endif
 
     return true;

diff  --git a/libcxx/test/support/min_allocator.h b/libcxx/test/support/min_allocator.h
index f27ac80c04649..f9d37e72ef631 100644
--- a/libcxx/test/support/min_allocator.h
+++ b/libcxx/test/support/min_allocator.h
@@ -17,6 +17,7 @@
 #include <memory>
 #include <new>
 #include <type_traits>
+#include <cstring>
 
 #include "test_macros.h"
 
@@ -432,4 +433,51 @@ class explicit_allocator
     TEST_CONSTEXPR_CXX20 friend bool operator!=(explicit_allocator x, explicit_allocator y) {return !(x == y);}
 };
 
+template <class T>
+class unaligned_allocator {
+public:
+  static_assert(TEST_ALIGNOF(T) == 1, "Type T cannot be created on unaligned address (UB)");
+  typedef T value_type;
+
+  TEST_CONSTEXPR_CXX20 unaligned_allocator() TEST_NOEXCEPT {}
+
+  template <class U>
+  TEST_CONSTEXPR_CXX20 explicit unaligned_allocator(unaligned_allocator<U>) TEST_NOEXCEPT {}
+
+  TEST_CONSTEXPR_CXX20 T* allocate(std::size_t n) { return std::allocator<T>().allocate(n + 1) + 1; }
+
+  TEST_CONSTEXPR_CXX20 void deallocate(T* p, std::size_t n) { std::allocator<T>().deallocate(p - 1, n + 1); }
+
+  TEST_CONSTEXPR_CXX20 friend bool operator==(unaligned_allocator, unaligned_allocator) { return true; }
+  TEST_CONSTEXPR_CXX20 friend bool operator!=(unaligned_allocator x, unaligned_allocator y) { return !(x == y); }
+};
+
+template <class T>
+class safe_allocator {
+public:
+  typedef T value_type;
+
+  TEST_CONSTEXPR_CXX20 safe_allocator() TEST_NOEXCEPT {}
+
+  template <class U>
+  TEST_CONSTEXPR_CXX20 safe_allocator(safe_allocator<U>) TEST_NOEXCEPT {}
+
+  TEST_CONSTEXPR_CXX20 T* allocate(std::size_t n) {
+    T* memory = std::allocator<T>().allocate(n);
+    if (!std::__libcpp_is_constant_evaluated())
+      std::memset(memory, 0, sizeof(T) * n);
+
+    return memory;
+  }
+
+  TEST_CONSTEXPR_CXX20 void deallocate(T* p, std::size_t n) {
+    if (!std::__libcpp_is_constant_evaluated())
+      DoNotOptimize(std::memset(p, 0, sizeof(T) * n));
+    std::allocator<T>().deallocate(p, n);
+  }
+
+  TEST_CONSTEXPR_CXX20 friend bool operator==(safe_allocator, safe_allocator) { return true; }
+  TEST_CONSTEXPR_CXX20 friend bool operator!=(safe_allocator x, safe_allocator y) { return !(x == y); }
+};
+
 #endif // MIN_ALLOCATOR_H


        


More information about the libcxx-commits mailing list