[libcxx-commits] [libcxx] [ASan][libc++] std::basic_string annotations (PR #72677)

via libcxx-commits libcxx-commits at lists.llvm.org
Thu Nov 23 20:10:38 PST 2023


https://github.com/AdvenamTacet updated https://github.com/llvm/llvm-project/pull/72677

>From 14dfcbf0eaa8cf17f2ae67186834454892de2dfb Mon Sep 17 00:00:00 2001
From: Advenam Tacet <advenam.tacet at trailofbits.com>
Date: Fri, 17 Nov 2023 01:51:21 +0100
Subject: [PATCH 1/3] [ASan][libc++] std::basic_string annotations

This commit turns on basic annotations for std::basic_string, similar to those in std::vector and std::deque.

Originally proposed here: https://reviews.llvm.org/D132769

Related patches:
Turning on annotations for short strings: https://reviews.llvm.org/D147680

Turning on annotations for all allocators: https://reviews.llvm.org/D146214

This revision is a part of a series of patches extending AddressSanitizer C++ container overflow detection capabilities by adding annotations, similar to those existing in std::vector, to std::string and std::deque collections. These changes allow ASan to detect cases when the instrumented program accesses memory which is internally allocated by the collection but is still not in-use (accesses before or after the stored elements for std::deque, or between the size and capacity bounds for std::string).

The motivation for the research and those changes was a bug, found by Trail of Bits, in a real code where an out-of-bounds read could happen as two strings were compared via a std::equals function that took iter1_begin, iter1_end, iter2_begin iterators (with a custom comparison function). When object iter1 was longer than iter2, read out-of-bounds on iter2 could happen. Container sanitization would detect it.

This Pull Request adds annotations for `std::basic_string`. Long string is very similar to std::vector, and therefore works well with `__sanitizer_annotate_contiguous_container` from LLVM 15 and therefore annotations works if that implementation is compiled with previous LLVM.
However, only the standard allocator is supported.

As D132522 extended possibilities of `__sanitizer_annotate_contiguous_container`, now annotations may be supported with all allocators, but that support will be added in a next PR. Only strings with default allocator are annotated with that commit.

If you have any questions, please email:
- advenam.tacet at trailofbits.com
- disconnect3d at trailofbits.com
---
 libcxx/include/string                         | 275 ++++++++++++++----
 .../string.capacity/capacity.pass.cpp         |   9 +
 .../string.capacity/clear.pass.cpp            |   7 +
 .../string.capacity/reserve.pass.cpp          |   3 +
 .../reserve_size.asan.pass.cpp                |  66 +++++
 .../string.capacity/reserve_size.pass.cpp     |   3 +
 .../resize_and_overwrite.pass.cpp             |   4 +
 .../string.capacity/resize_size.pass.cpp      |   3 +
 .../string.capacity/resize_size_char.pass.cpp |  13 +
 .../string.capacity/shrink_to_fit.pass.cpp    |   8 +
 .../string.cons/T_size_size.pass.cpp          |   3 +
 .../basic.string/string.cons/alloc.pass.cpp   |   6 +
 .../string.cons/brace_assignment.pass.cpp     |   3 +
 .../string.cons/char_assignment.pass.cpp      |   3 +
 .../basic.string/string.cons/copy.pass.cpp    |   4 +
 .../string.cons/copy_alloc.pass.cpp           |   4 +
 .../string.cons/copy_assignment.pass.cpp      |   4 +
 .../basic.string/string.cons/default.pass.cpp |   2 +
 .../string.cons/initializer_list.pass.cpp     |  10 +
 .../initializer_list_assignment.pass.cpp      |  14 +
 .../string.cons/iter_alloc.pass.cpp           |   3 +
 .../string.cons/iter_alloc_deduction.pass.cpp |   6 +
 .../basic.string/string.cons/move.pass.cpp    |   4 +
 .../string.cons/move_alloc.pass.cpp           |   4 +
 .../string.cons/move_assignment.pass.cpp      |   5 +
 .../string.cons/pointer_alloc.pass.cpp        |   4 +
 .../string.cons/pointer_assignment.pass.cpp   |   3 +
 .../string.cons/pointer_size_alloc.pass.cpp   |   4 +
 .../string.cons/size_char_alloc.pass.cpp      |   4 +
 .../string.cons/string_view.pass.cpp          |   6 +
 .../string_view_assignment.pass.cpp           |   3 +
 .../basic.string/string.cons/substr.pass.cpp  |   5 +
 .../string_append/initializer_list.pass.cpp   |   3 +
 .../string_append/iterator.pass.cpp           |   3 +
 .../string_append/pointer.pass.cpp            |   3 +
 .../string_append/pointer_size.pass.cpp       |   3 +
 .../string_append/push_back.pass.cpp          |   3 +
 .../string_append/size_char.pass.cpp          |   3 +
 .../string_append/string.pass.cpp             |   3 +
 .../string_append/string_size_size.pass.cpp   |   3 +
 .../string_assign/T_size_size.pass.cpp        |   3 +
 .../string_assign/initializer_list.pass.cpp   |   3 +
 .../string_assign/iterator.pass.cpp           |   3 +
 .../string_assign/pointer.pass.cpp            |   3 +
 .../string_assign/pointer_size.pass.cpp       |   3 +
 .../string_assign/size_char.pass.cpp          |   3 +
 .../string_assign/string.pass.cpp             |   3 +
 .../string_assign/string_size_size.pass.cpp   |   3 +
 .../string_copy/copy.pass.cpp                 |   5 +
 .../string_erase/iter.pass.cpp                |   3 +
 .../string_erase/iter_iter.pass.cpp           |   3 +
 .../string_erase/pop_back.asan.pass.cpp       |  50 ++++
 .../string_erase/pop_back.pass.cpp            |   3 +
 .../string_erase/size_size.pass.cpp           |   3 +
 .../string_insert/iter_char.pass.cpp          |   4 +
 .../iter_initializer_list.pass.cpp            |   3 +
 .../string_insert/iter_iter_iter.pass.cpp     |   3 +
 .../string_insert/iter_size_char.pass.cpp     |   3 +
 .../string_insert/size_pointer.pass.cpp       |   3 +
 .../string_insert/size_pointer_size.pass.cpp  |   3 +
 .../string_insert/size_size_char.pass.cpp     |   3 +
 .../string_insert/size_string.pass.cpp        |   3 +
 .../size_string_size_size.pass.cpp            |   3 +
 .../string_op_plus_equal/char.pass.cpp        |   3 +
 .../initializer_list.pass.cpp                 |   3 +
 .../string_op_plus_equal/pointer.pass.cpp     |   3 +
 .../string_op_plus_equal/string.pass.cpp      |   4 +
 .../string_replace/iter_iter_string.pass.cpp  |   3 +
 .../size_size_T_size_size.pass.cpp            |   3 +
 .../string_replace/size_size_pointer.pass.cpp |   3 +
 .../size_size_pointer_size.pass.cpp           |   3 +
 .../size_size_size_char.pass.cpp              |   3 +
 .../string_replace/size_size_string.pass.cpp  |   3 +
 .../size_size_string_size_size.pass.cpp       |   3 +
 .../size_size_string_view.pass.cpp            |   3 +
 .../string_swap/swap.asan.pass.cpp            |  87 ++++++
 .../string_swap/swap.pass.cpp                 |   4 +
 .../string.special/swap.pass.cpp              |   4 +
 .../string_op+/char_string.pass.cpp           |   3 +
 .../string_op+/string_char.pass.cpp           |   3 +
 .../string_op+/string_pointer.pass.cpp        |   3 +
 .../string_op+/string_string.pass.cpp         |   3 +
 libcxx/test/support/asan_testing.h            |  63 +++-
 83 files changed, 781 insertions(+), 64 deletions(-)
 create mode 100644 libcxx/test/std/strings/basic.string/string.capacity/reserve_size.asan.pass.cpp
 create mode 100644 libcxx/test/std/strings/basic.string/string.modifiers/string_erase/pop_back.asan.pass.cpp
 create mode 100644 libcxx/test/std/strings/basic.string/string.modifiers/string_swap/swap.asan.pass.cpp

diff --git a/libcxx/include/string b/libcxx/include/string
index 9c2efac8006bd72..6f6b2752960a506 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -645,6 +645,17 @@ basic_string<char32_t> operator""s( const char32_t *str, size_t len );
 _LIBCPP_PUSH_MACROS
 #include <__undef_macros>
 
+#ifndef _LIBCPP_HAS_NO_ASAN
+#  define _LIBCPP_STRING_INTERNAL_MEMORY_ACCESS __attribute__((__no_sanitize__("address")))
+// This macro disables AddressSanitizer (ASan) instrumentation for a specific function,
+// allowing memory accesses that would normally trigger ASan errors to proceed without crashing.
+// This is useful for accessing parts of objects memory, which should not be accessed,
+// such as unused bytes in short strings, that should never be accessed
+// by other parts of the program.
+#else
+#  define _LIBCPP_STRING_INTERNAL_MEMORY_ACCESS
+#endif
+#define _LIBCPP_SHORT_STRING_ANNOTATIONS_ALLOWED false
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
@@ -702,6 +713,9 @@ struct __init_with_sentinel_tag {};
 template<class _CharT, class _Traits, class _Allocator>
 class basic_string
 {
+private:
+  using __default_allocator_type = allocator<_CharT>;
+
 public:
     typedef basic_string                                 __self;
     typedef basic_string_view<_CharT, _Traits>           __self_view;
@@ -888,34 +902,46 @@ public:
 #endif
       : __r_(__value_init_tag(), __a) {}
 
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string(const basic_string& __str)
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_STRING_INTERNAL_MEMORY_ACCESS basic_string(const basic_string& __str)
       : __r_(__default_init_tag(), __alloc_traits::select_on_container_copy_construction(__str.__alloc())) {
     if (!__str.__is_long())
+    {
       __r_.first() = __str.__r_.first();
+      __annotate_new(__get_short_size());
+    }
     else
       __init_copy_ctor_external(std::__to_address(__str.__get_long_pointer()), __str.__get_long_size());
   }
 
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string(const basic_string& __str, const allocator_type& __a)
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_STRING_INTERNAL_MEMORY_ACCESS basic_string(const basic_string& __str, const allocator_type& __a)
       : __r_(__default_init_tag(), __a) {
     if (!__str.__is_long())
+    {
       __r_.first() = __str.__r_.first();
+      __annotate_new(__get_short_size());
+    }
     else
       __init_copy_ctor_external(std::__to_address(__str.__get_long_pointer()), __str.__get_long_size());
   }
 
 #ifndef _LIBCPP_CXX03_LANG
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string(basic_string&& __str)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
+  basic_string(basic_string&& __str)
 #  if _LIBCPP_STD_VER <= 14
       _NOEXCEPT_(is_nothrow_move_constructible<allocator_type>::value)
 #  else
       _NOEXCEPT
 #  endif
-      : __r_(std::move(__str.__r_)) {
+      // Turning off ASan instrumentation for variable initialization with _LIBCPP_STRING_INTERNAL_MEMORY_ACCESS
+      // is inconsistent and that initialization may be annotated.
+      // Therefore, to copy __str memory, we have to unpoison it first (if object is poisoned and not external buffer,
+      // so only the short string case).
+      : __r_( ( (__str.__is_long() ? 0 : (__str.__annotate_delete(), 0)), std::move(__str.__r_)) ) {
     __str.__r_.first() = __rep();
   }
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string(basic_string&& __str, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_STRING_INTERNAL_MEMORY_ACCESS
+  basic_string(basic_string&& __str, const allocator_type& __a)
       : __r_(__default_init_tag(), __a) {
     if (__str.__is_long() && __a != __str.__alloc()) // copy, not move
       __init(std::__to_address(__str.__get_long_pointer()), __str.__get_long_size());
@@ -923,6 +949,9 @@ public:
       if (__libcpp_is_constant_evaluated())
         __r_.first() = __rep();
       __r_.first() = __str.__r_.first();
+      if (!__str.__is_long()) {
+        __str.__annotate_delete();
+      }
       __str.__r_.first() = __rep();
     }
   }
@@ -965,11 +994,11 @@ public:
   }
 
 #if _LIBCPP_STD_VER >= 23
-  _LIBCPP_HIDE_FROM_ABI constexpr
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_STRING_INTERNAL_MEMORY_ACCESS constexpr
   basic_string(basic_string&& __str, size_type __pos, const _Allocator& __alloc = _Allocator())
       : basic_string(std::move(__str), __pos, npos, __alloc) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_STRING_INTERNAL_MEMORY_ACCESS constexpr
   basic_string(basic_string&& __str, size_type __pos, size_type __n, const _Allocator& __alloc = _Allocator())
       : __r_(__default_init_tag(), __alloc) {
     if (__pos > __str.size())
@@ -1081,6 +1110,7 @@ public:
 #endif // _LIBCPP_CXX03_LANG
 
   inline _LIBCPP_CONSTEXPR_SINCE_CXX20 ~basic_string() {
+    __annotate_delete();
     if (__is_long())
       __alloc_traits::deallocate(__alloc(), __get_long_pointer(), __get_long_cap());
   }
@@ -1098,8 +1128,8 @@ public:
     }
 
 #ifndef _LIBCPP_CXX03_LANG
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& operator=(basic_string&& __str)
-      _NOEXCEPT_((__noexcept_move_assign_container<_Allocator, __alloc_traits>::value)) {
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_STRING_INTERNAL_MEMORY_ACCESS basic_string&
+    operator=(basic_string&& __str) _NOEXCEPT_((__noexcept_move_assign_container<_Allocator, __alloc_traits>::value)) {
     __move_assign(__str, integral_constant<bool, __alloc_traits::propagate_on_container_move_assignment::value>());
     return *this;
   }
@@ -1112,7 +1142,7 @@ public:
 #if _LIBCPP_STD_VER >= 23
     basic_string& operator=(nullptr_t) = delete;
 #endif
-    _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& operator=(value_type __c);
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_STRING_INTERNAL_MEMORY_ACCESS basic_string& operator=(value_type __c);
 
     _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
     iterator begin() _NOEXCEPT
@@ -1329,7 +1359,7 @@ public:
   }
 
 #if _LIBCPP_STD_VER >= 20
-  _LIBCPP_HIDE_FROM_ABI constexpr
+  _LIBCPP_HIDE_FROM_ABI constexpr _LIBCPP_STRING_INTERNAL_MEMORY_ACCESS
   void __move_assign(basic_string&& __str, size_type __pos, size_type __len) {
     // Pilfer the allocation from __str.
     _LIBCPP_ASSERT_INTERNAL(__alloc() == __str.__alloc(), "__move_assign called with wrong allocator");
@@ -1345,7 +1375,7 @@ public:
     _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
     basic_string& assign(const basic_string& __str) { return *this = __str; }
 #ifndef _LIBCPP_CXX03_LANG
-    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_STRING_INTERNAL_MEMORY_ACCESS
     basic_string& assign(basic_string&& __str)
         _NOEXCEPT_((__noexcept_move_assign_container<_Allocator, __alloc_traits>::value))
         {*this = std::move(__str); return *this;}
@@ -1736,7 +1766,7 @@ private:
 
     _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __shrink_or_extend(size_type __target_capacity);
 
-    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_STRING_INTERNAL_MEMORY_ACCESS
     bool __is_long() const _NOEXCEPT {
         if (__libcpp_is_constant_evaluated() && __builtin_constant_p(__r_.first().__l.__is_long_)) {
             return __r_.first().__l.__is_long_;
@@ -1776,6 +1806,7 @@ private:
         value_type* __p;
         if (__cap - __sz >= __n)
         {
+          __annotate_increase(__n);
             __p = std::__to_address(__get_pointer());
             size_type __n_move = __sz - __ip;
             if (__n_move != 0)
@@ -1802,7 +1833,7 @@ private:
     _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 allocator_type& __alloc() _NOEXCEPT { return __r_.second(); }
     _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR const allocator_type& __alloc() const _NOEXCEPT { return __r_.second(); }
 
-    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_STRING_INTERNAL_MEMORY_ACCESS
     void __set_short_size(size_type __s) _NOEXCEPT {
         _LIBCPP_ASSERT_INTERNAL(
             __s < __min_cap, "__s should never be greater than or equal to the short string capacity");
@@ -1810,7 +1841,7 @@ private:
         __r_.first().__s.__is_long_ = false;
     }
 
-    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_STRING_INTERNAL_MEMORY_ACCESS
     size_type __get_short_size() const _NOEXCEPT {
         _LIBCPP_ASSERT_INTERNAL(
             !__r_.first().__s.__is_long_, "String has to be short when trying to get the short size");
@@ -1860,6 +1891,45 @@ private:
     const_pointer __get_pointer() const _NOEXCEPT
         {return __is_long() ? __get_long_pointer() : __get_short_pointer();}
 
+    // The following functions are no-ops outside of AddressSanitizer mode.
+#ifndef _LIBCPP_HAS_NO_ASAN
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __annotate_contiguous_container(
+        const void* __old_mid, const void* __new_mid) const {
+        const void* __begin = data();
+        const void* __end = data() + capacity() + 1;
+        if (!__libcpp_is_constant_evaluated() && __begin != nullptr && is_same<allocator_type, __default_allocator_type>::value)
+          __sanitizer_annotate_contiguous_container(__begin, __end, __old_mid, __new_mid);
+    }
+#else
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
+    __annotate_contiguous_container(const void*, const void*) const {}
+#endif
+
+    // ASan: short string is poisoned if and only if this function returns true.
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool __asan_short_string_is_annotated() const _NOEXCEPT {
+      return _LIBCPP_SHORT_STRING_ANNOTATIONS_ALLOWED && !__libcpp_is_constant_evaluated();
+    }
+
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __annotate_new(size_type __current_size) const _NOEXCEPT {
+      if (!__libcpp_is_constant_evaluated() && (__asan_short_string_is_annotated() || __is_long()))
+        __annotate_contiguous_container(data() + capacity() + 1, data() + __current_size + 1);
+    }
+
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __annotate_delete() const _NOEXCEPT {
+      if (!__libcpp_is_constant_evaluated() && (__asan_short_string_is_annotated() || __is_long()))
+        __annotate_contiguous_container(data() + size() + 1, data() + capacity() + 1);
+    }
+
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __annotate_increase(size_type __n) const _NOEXCEPT {
+      if (!__libcpp_is_constant_evaluated() && (__asan_short_string_is_annotated() || __is_long()))
+        __annotate_contiguous_container(data() + size() + 1, data() + size() + 1 + __n);
+    }
+
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __annotate_shrink(size_type __old_size) const _NOEXCEPT {
+      if (!__libcpp_is_constant_evaluated() && (__asan_short_string_is_annotated() || __is_long()))
+        __annotate_contiguous_container(data() + __old_size + 1, data() + size() + 1);
+    }
+
     template <size_type __a> static
         _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
         size_type __align_it(size_type __s) _NOEXCEPT
@@ -1962,6 +2032,7 @@ private:
                 }
                 else
                 {
+                    __annotate_delete();
                     allocator_type __a = __str.__alloc();
                     auto __allocation = std::__allocate_at_least(__a, __str.__get_long_cap());
                     __begin_lifetime(__allocation.ptr, __allocation.count);
@@ -1971,6 +2042,7 @@ private:
                     __set_long_pointer(__allocation.ptr);
                     __set_long_cap(__allocation.count);
                     __set_long_size(__str.size());
+                    __annotate_new(__get_long_size());
                 }
             }
         }
@@ -2018,18 +2090,28 @@ private:
 
     // Assigns the value in __s, guaranteed to be __n < __min_cap in length.
     inline _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& __assign_short(const value_type* __s, size_type __n) {
+      size_type __old_size = size();
+      if (__n > __old_size)
+        __annotate_increase(__n - __old_size);
       pointer __p = __is_long()
                         ? (__set_long_size(__n), __get_long_pointer())
                         : (__set_short_size(__n), __get_short_pointer());
       traits_type::move(std::__to_address(__p), __s, __n);
       traits_type::assign(__p[__n], value_type());
+      if (__old_size > __n)
+        __annotate_shrink(__old_size);
       return *this;
     }
 
     _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
     basic_string& __null_terminate_at(value_type* __p, size_type __newsz) {
+      size_type __old_size = size();
+      if (__newsz > __old_size)
+        __annotate_increase(__newsz - __old_size);
       __set_size(__newsz);
       traits_type::assign(__p[__newsz], value_type());
+      if (__old_size > __newsz)
+        __annotate_shrink(__old_size);
       return *this;
     }
 
@@ -2136,6 +2218,7 @@ void basic_string<_CharT, _Traits, _Allocator>::__init(const value_type* __s,
     }
     traits_type::copy(std::__to_address(__p), __s, __sz);
     traits_type::assign(__p[__sz], value_type());
+    __annotate_new(__sz);
 }
 
 template <class _CharT, class _Traits, class _Allocator>
@@ -2164,6 +2247,7 @@ basic_string<_CharT, _Traits, _Allocator>::__init(const value_type* __s, size_ty
     }
     traits_type::copy(std::__to_address(__p), __s, __sz);
     traits_type::assign(__p[__sz], value_type());
+    __annotate_new(__sz);
 }
 
 template <class _CharT, class _Traits, class _Allocator>
@@ -2188,6 +2272,7 @@ void basic_string<_CharT, _Traits, _Allocator>::__init_copy_ctor_external(
     __set_long_size(__sz);
   }
   traits_type::copy(std::__to_address(__p), __s, __sz + 1);
+  __annotate_new(__sz);
 }
 
 template <class _CharT, class _Traits, class _Allocator>
@@ -2217,6 +2302,7 @@ basic_string<_CharT, _Traits, _Allocator>::__init(size_type __n, value_type __c)
     }
     traits_type::assign(std::__to_address(__p), __n, __c);
     traits_type::assign(__p[__n], value_type());
+    __annotate_new(__n);
 }
 
 template <class _CharT, class _Traits, class _Allocator>
@@ -2243,6 +2329,7 @@ void basic_string<_CharT, _Traits, _Allocator>::__init_with_sentinel(_InputItera
     }
     catch (...)
     {
+        __annotate_delete();
         if (__is_long())
             __alloc_traits::deallocate(__alloc(), __get_long_pointer(), __get_long_cap());
         throw;
@@ -2298,11 +2385,13 @@ void basic_string<_CharT, _Traits, _Allocator>::__init_with_size(
     }
     catch (...)
     {
+        __annotate_delete();
         if (__is_long())
             __alloc_traits::deallocate(__alloc(), __get_long_pointer(), __get_long_cap());
         throw;
     }
 #endif  // _LIBCPP_HAS_NO_EXCEPTIONS
+    __annotate_new(__sz);
 }
 
 template <class _CharT, class _Traits, class _Allocator>
@@ -2319,6 +2408,7 @@ basic_string<_CharT, _Traits, _Allocator>::__grow_by_and_replace
     size_type __cap = __old_cap < __ms / 2 - __alignment ?
                           __recommend(std::max(__old_cap + __delta_cap, 2 * __old_cap)) :
                           __ms - 1;
+    __annotate_delete();
     auto __allocation = std::__allocate_at_least(__alloc(), __cap + 1);
     pointer __p = __allocation.ptr;
     __begin_lifetime(__p, __allocation.count);
@@ -2338,6 +2428,7 @@ basic_string<_CharT, _Traits, _Allocator>::__grow_by_and_replace
     __old_sz = __n_copy + __n_add + __sec_cp_sz;
     __set_long_size(__old_sz);
     traits_type::assign(__p[__old_sz], value_type());
+    __annotate_new(__old_cap + __delta_cap);
 }
 
 // __grow_by is deprecated because it does not set the size. It may not update the size when the size is changed, and it
@@ -2360,6 +2451,7 @@ basic_string<_CharT, _Traits, _Allocator>::__grow_by(size_type __old_cap, size_t
     size_type __cap = __old_cap < __ms / 2 - __alignment ?
                           __recommend(std::max(__old_cap + __delta_cap, 2 * __old_cap)) :
                           __ms - 1;
+    __annotate_delete();
     auto __allocation = std::__allocate_at_least(__alloc(), __cap + 1);
     pointer __p = __allocation.ptr;
     __begin_lifetime(__p, __allocation.count);
@@ -2390,6 +2482,7 @@ basic_string<_CharT, _Traits, _Allocator>::__grow_by_without_replace(
     __grow_by(__old_cap, __delta_cap, __old_sz, __n_copy, __n_del, __n_add);
     _LIBCPP_SUPPRESS_DEPRECATED_POP
     __set_long_size(__old_sz - __n_del + __n_add);
+    __annotate_new(__old_sz - __n_del + __n_add);
 }
 
 // assign
@@ -2402,10 +2495,15 @@ basic_string<_CharT, _Traits, _Allocator>::__assign_no_alias(
     const value_type* __s, size_type __n) {
   size_type __cap = __is_short ? static_cast<size_type>(__min_cap) : __get_long_cap();
   if (__n < __cap) {
+    size_type __old_size = __is_short ? __get_short_size() : __get_long_size();
+    if (__n > __old_size)
+      __annotate_increase(__n - __old_size);
     pointer __p = __is_short ? __get_short_pointer() : __get_long_pointer();
     __is_short ? __set_short_size(__n) : __set_long_size(__n);
     traits_type::copy(std::__to_address(__p), __s, __n);
     traits_type::assign(__p[__n], value_type());
+    if (__old_size > __n)
+      __annotate_shrink(__old_size);
   } else {
     size_type __sz = __is_short ? __get_short_size() : __get_long_size();
     __grow_by_and_replace(__cap - 1, __n - __cap + 1, __sz, 0, __sz, __n, __s);
@@ -2420,6 +2518,9 @@ basic_string<_CharT, _Traits, _Allocator>::__assign_external(
     const value_type* __s, size_type __n) {
   size_type __cap = capacity();
   if (__cap >= __n) {
+    size_type __old_size = size();
+    if (__n > __old_size)
+      __annotate_increase(__n - __old_size);
     value_type* __p = std::__to_address(__get_pointer());
     traits_type::move(__p, __s, __n);
     return __null_terminate_at(__p, __n);
@@ -2447,11 +2548,15 @@ basic_string<_CharT, _Traits, _Allocator>&
 basic_string<_CharT, _Traits, _Allocator>::assign(size_type __n, value_type __c)
 {
     size_type __cap = capacity();
+    size_type __old_size = size();
     if (__cap < __n)
     {
         size_type __sz = size();
         __grow_by_without_replace(__cap, __n - __cap, __sz, 0, __sz);
+        __annotate_increase(__n);
     }
+    else if(__n > __old_size)
+        __annotate_increase(__n - __old_size);
     value_type* __p = std::__to_address(__get_pointer());
     traits_type::assign(__p, __n, __c);
     return __null_terminate_at(__p, __n);
@@ -2462,24 +2567,26 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20
 basic_string<_CharT, _Traits, _Allocator>&
 basic_string<_CharT, _Traits, _Allocator>::operator=(value_type __c)
 {
-    pointer __p;
-    if (__is_long())
-    {
-        __p = __get_long_pointer();
-        __set_long_size(1);
-    }
-    else
-    {
-        __p = __get_short_pointer();
-        __set_short_size(1);
-    }
-    traits_type::assign(*__p, __c);
-    traits_type::assign(*++__p, value_type());
-    return *this;
+  pointer __p;
+  size_type __old_size = size();
+  if (__old_size == 0)
+    __annotate_increase(1);
+  if (__is_long()) {
+    __p = __get_long_pointer();
+    __set_long_size(1);
+  } else {
+    __p = __get_short_pointer();
+    __set_short_size(1);
+  }
+  traits_type::assign(*__p, __c);
+  traits_type::assign(*++__p, value_type());
+  if (__old_size > 1)
+    __annotate_shrink(__old_size);
+  return *this;
 }
 
 template <class _CharT, class _Traits, class _Allocator>
-_LIBCPP_CONSTEXPR_SINCE_CXX20
+_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_STRING_INTERNAL_MEMORY_ACCESS
 basic_string<_CharT, _Traits, _Allocator>&
 basic_string<_CharT, _Traits, _Allocator>::operator=(const basic_string& __str)
 {
@@ -2487,7 +2594,12 @@ basic_string<_CharT, _Traits, _Allocator>::operator=(const basic_string& __str)
     __copy_assign_alloc(__str);
     if (!__is_long()) {
       if (!__str.__is_long()) {
+        size_type __old_size = __get_short_size();
+        if (__get_short_size() < __str.__get_short_size())
+          __annotate_increase(__str.__get_short_size() - __get_short_size());
         __r_.first() = __str.__r_.first();
+        if (__old_size > __get_short_size())
+          __annotate_shrink(__old_size);
       } else {
         return __assign_no_alias<true>(__str.data(), __str.size());
       }
@@ -2513,7 +2625,7 @@ basic_string<_CharT, _Traits, _Allocator>::__move_assign(basic_string& __str, fa
 }
 
 template <class _CharT, class _Traits, class _Allocator>
-inline _LIBCPP_CONSTEXPR_SINCE_CXX20
+inline _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_STRING_INTERNAL_MEMORY_ACCESS
 void
 basic_string<_CharT, _Traits, _Allocator>::__move_assign(basic_string& __str, true_type)
 #if _LIBCPP_STD_VER >= 17
@@ -2522,6 +2634,7 @@ basic_string<_CharT, _Traits, _Allocator>::__move_assign(basic_string& __str, tr
     _NOEXCEPT_(is_nothrow_move_assignable<allocator_type>::value)
 #endif
 {
+  __annotate_delete();
   if (__is_long()) {
     __alloc_traits::deallocate(__alloc(), __get_long_pointer(),
                                __get_long_cap());
@@ -2532,10 +2645,31 @@ basic_string<_CharT, _Traits, _Allocator>::__move_assign(basic_string& __str, tr
     }
 #endif
   }
+  size_type __str_old_size = __str.size();
+  bool __str_was_short   = !__str.__is_long();
+
   __move_assign_alloc(__str);
   __r_.first() = __str.__r_.first();
   __str.__set_short_size(0);
   traits_type::assign(__str.__get_short_pointer()[0], value_type());
+
+  if (__str_was_short && this != &__str)
+    __str.__annotate_shrink(__str_old_size);
+  else
+    // ASan annotations: was long, so object memory is unpoisoned as new.
+    // Or is same as *this, and __annotate_delete() was called.
+    __str.__annotate_new(0);
+
+  // ASan annotations: Guard against `std::string s; s = std::move(s);`
+  // You can find more here: https://en.cppreference.com/w/cpp/utility/move
+  // Quote: "Unless otherwise specified, all standard library objects that have been moved
+  // from are placed in a "valid but unspecified state", meaning the object's class
+  // invariants hold (so functions without preconditions, such as the assignment operator,
+  // can be safely used on the object after it was moved from):"
+  // Quote: "v = std::move(v); // the value of v is unspecified"
+  if (!__is_long() && &__str != this)
+    // If it is long string, delete was never called.
+    __annotate_new(__get_short_size());
 }
 
 #endif
@@ -2581,6 +2715,7 @@ basic_string<_CharT, _Traits, _Allocator>::__assign_trivial(_Iterator __first, _
   _LIBCPP_ASSERT_INTERNAL(
       __string_is_trivial_iterator<_Iterator>::value, "The iterator type given to `__assign_trivial` must be trivial");
 
+  size_type __old_size = size();
   size_type __cap = capacity();
   if (__cap < __n) {
     // Unlike `append` functions, if the input range points into the string itself, there is no case that the input
@@ -2591,12 +2726,17 @@ basic_string<_CharT, _Traits, _Allocator>::__assign_trivial(_Iterator __first, _
     //    object itself stays valid even if reallocation happens.
     size_type __sz = size();
     __grow_by_without_replace(__cap, __n - __cap, __sz, 0, __sz);
+    __annotate_increase(__n);
     }
+    else if (__n > __old_size)
+        __annotate_increase(__n - __old_size);
     pointer __p = __get_pointer();
     for (; __first != __last; ++__p, (void) ++__first)
         traits_type::assign(*__p, *__first);
     traits_type::assign(*__p, value_type());
     __set_size(__n);
+    if (__n < __old_size)
+        __annotate_shrink(__old_size);
 }
 
 template <class _CharT, class _Traits, class _Allocator>
@@ -2657,6 +2797,7 @@ basic_string<_CharT, _Traits, _Allocator>::append(const value_type* __s, size_ty
     {
         if (__n)
         {
+            __annotate_increase(__n);
             value_type* __p = std::__to_address(__get_pointer());
             traits_type::copy(__p + __sz, __s, __n);
             __sz += __n;
@@ -2680,6 +2821,7 @@ basic_string<_CharT, _Traits, _Allocator>::append(size_type __n, value_type __c)
         size_type __sz = size();
         if (__cap - __sz < __n)
             __grow_by_without_replace(__cap, __sz + __n - __cap, __sz, __sz, 0);
+        __annotate_increase(__n);
         pointer __p = __get_pointer();
         traits_type::assign(std::__to_address(__p) + __sz, __n, __c);
         __sz += __n;
@@ -2699,6 +2841,7 @@ basic_string<_CharT, _Traits, _Allocator>::__append_default_init(size_type __n)
         size_type __sz = size();
         if (__cap - __sz < __n)
             __grow_by_without_replace(__cap, __sz + __n - __cap, __sz, __sz, 0);
+        __annotate_increase(__n);
         pointer __p = __get_pointer();
         __sz += __n;
         __set_size(__sz);
@@ -2727,8 +2870,10 @@ basic_string<_CharT, _Traits, _Allocator>::push_back(value_type __c)
     if (__sz == __cap)
     {
         __grow_by_without_replace(__cap, 1, __sz, __sz, 0);
+        __annotate_increase(1);
         __is_short = false; // the string is always long after __grow_by
-    }
+    } else
+        __annotate_increase(1);
     pointer __p = __get_pointer();
     if (__is_short)
     {
@@ -2760,6 +2905,7 @@ basic_string<_CharT, _Traits, _Allocator>::append(
         {
             if (__cap - __sz < __n)
               __grow_by_without_replace(__cap, __sz + __n - __cap, __sz, __sz, 0);
+            __annotate_increase(__n);
             pointer __p = __get_pointer() + __sz;
             for (; __first != __last; ++__p, (void) ++__first)
                 traits_type::assign(*__p, *__first);
@@ -2825,6 +2971,7 @@ basic_string<_CharT, _Traits, _Allocator>::insert(size_type __pos, const value_t
     {
         if (__n)
         {
+            __annotate_increase(__n);
             value_type* __p = std::__to_address(__get_pointer());
             size_type __n_move = __sz - __pos;
             if (__n_move != 0)
@@ -2858,6 +3005,7 @@ basic_string<_CharT, _Traits, _Allocator>::insert(size_type __pos, size_type __n
         value_type* __p;
         if (__cap - __sz >= __n)
         {
+            __annotate_increase(__n);
             __p = std::__to_address(__get_pointer());
             size_type __n_move = __sz - __pos;
             if (__n_move != 0)
@@ -2966,6 +3114,7 @@ basic_string<_CharT, _Traits, _Allocator>::insert(const_iterator __pos, value_ty
     }
     else
     {
+        __annotate_increase(1);
         __p = std::__to_address(__get_pointer());
         size_type __n_move = __sz - __ip;
         if (__n_move != 0)
@@ -2996,6 +3145,8 @@ basic_string<_CharT, _Traits, _Allocator>::replace(size_type __pos, size_type __
         value_type* __p = std::__to_address(__get_pointer());
         if (__n1 != __n2)
         {
+            if (__n2 > __n1)
+                __annotate_increase(__n2 - __n1);
             size_type __n_move = __sz - __pos - __n1;
             if (__n_move != 0)
             {
@@ -3040,20 +3191,19 @@ basic_string<_CharT, _Traits, _Allocator>::replace(size_type __pos, size_type __
     __n1 = std::min(__n1, __sz - __pos);
     size_type __cap = capacity();
     value_type* __p;
-    if (__cap - __sz + __n1 >= __n2)
-    {
-        __p = std::__to_address(__get_pointer());
-        if (__n1 != __n2)
-        {
-            size_type __n_move = __sz - __pos - __n1;
-            if (__n_move != 0)
-                traits_type::move(__p + __pos + __n2, __p + __pos + __n1, __n_move);
-        }
-    }
-    else
-    {
-        __grow_by_without_replace(__cap, __sz - __n1 + __n2 - __cap, __sz, __pos, __n1, __n2);
-        __p = std::__to_address(__get_long_pointer());
+    if (__cap - __sz + __n1 >= __n2) {
+      __p = std::__to_address(__get_pointer());
+      if (__n1 != __n2) {
+        if (__n2 > __n1)
+          __annotate_increase(__n2 - __n1);
+        size_type __n_move = __sz - __pos - __n1;
+        if (__n_move != 0)
+          traits_type::move(__p + __pos + __n2, __p + __pos + __n1, __n_move);
+      }
+    } else {
+      __grow_by_without_replace(__cap, __sz - __n1 + __n2 - __cap, __sz, __pos, __n1, __n2);
+      __set_long_size(__sz - __n1 + __n2);
+      __p = std::__to_address(__get_long_pointer());
     }
     traits_type::assign(__p + __pos, __n2, __c);
     return __null_terminate_at(__p, __sz - (__n1 - __n2));
@@ -3181,6 +3331,7 @@ inline _LIBCPP_CONSTEXPR_SINCE_CXX20
 void
 basic_string<_CharT, _Traits, _Allocator>::clear() _NOEXCEPT
 {
+    size_type __old_size = size();
     if (__is_long())
     {
         traits_type::assign(*__get_long_pointer(), value_type());
@@ -3191,6 +3342,7 @@ basic_string<_CharT, _Traits, _Allocator>::clear() _NOEXCEPT
         traits_type::assign(*__get_short_pointer(), value_type());
         __set_short_size(0);
     }
+    __annotate_shrink(__old_size);
 }
 
 template <class _CharT, class _Traits, class _Allocator>
@@ -3253,6 +3405,7 @@ inline _LIBCPP_CONSTEXPR_SINCE_CXX20
 void
 basic_string<_CharT, _Traits, _Allocator>::__shrink_or_extend(size_type __target_capacity)
 {
+    __annotate_delete();
     size_type __cap = capacity();
     size_type __sz = size();
 
@@ -3309,6 +3462,7 @@ basic_string<_CharT, _Traits, _Allocator>::__shrink_or_extend(size_type __target
     }
     else
         __set_short_size(__sz);
+    __annotate_new(__sz);
 }
 
 template <class _CharT, class _Traits, class _Allocator>
@@ -3359,8 +3513,16 @@ basic_string<_CharT, _Traits, _Allocator>::swap(basic_string& __str)
         __alloc_traits::propagate_on_container_swap::value ||
         __alloc_traits::is_always_equal::value ||
         __alloc() == __str.__alloc(), "swapping non-equal allocators");
+    if (!__is_long())
+      __annotate_delete();
+    if (this != &__str && !__str.__is_long())
+      __str.__annotate_delete();
     std::swap(__r_.first(), __str.__r_.first());
     std::__swap_allocator(__alloc(), __str.__alloc());
+    if (!__is_long())
+      __annotate_new(__get_short_size());
+    if (this != &__str && !__str.__is_long())
+      __str.__annotate_new(__str.__get_short_size());
 }
 
 // find
@@ -3848,12 +4010,12 @@ inline _LIBCPP_CONSTEXPR_SINCE_CXX20
 void
 basic_string<_CharT, _Traits, _Allocator>::__clear_and_shrink() _NOEXCEPT
 {
-    clear();
-    if(__is_long())
-    {
-        __alloc_traits::deallocate(__alloc(), __get_long_pointer(), capacity() + 1);
-        __r_.first() = __rep();
-    }
+  clear();
+  if (__is_long()) {
+    __annotate_delete();
+    __alloc_traits::deallocate(__alloc(), __get_long_pointer(), capacity() + 1);
+    __r_.first() = __rep();
+  }
 }
 
 // operator==
@@ -4104,6 +4266,7 @@ operator+(const basic_string<_CharT, _Traits, _Allocator>& __lhs,
     _Traits::copy(__ptr, __lhs.data(), __lhs_sz);
     _Traits::copy(__ptr + __lhs_sz, __rhs.data(), __rhs_sz);
     _Traits::assign(__ptr + __lhs_sz + __rhs_sz, 1, _CharT());
+    __r.__annotate_new(__lhs_sz + __rhs_sz);
     return __r;
 }
 
@@ -4118,10 +4281,12 @@ operator+(const _CharT* __lhs , const basic_string<_CharT,_Traits,_Allocator>& _
     _String __r(__uninitialized_size_tag(),
                 __lhs_sz + __rhs_sz,
                 _String::__alloc_traits::select_on_container_copy_construction(__rhs.get_allocator()));
+    __r.__annotate_delete();
     auto __ptr = std::__to_address(__r.__get_pointer());
     _Traits::copy(__ptr, __lhs, __lhs_sz);
     _Traits::copy(__ptr + __lhs_sz, __rhs.data(), __rhs_sz);
     _Traits::assign(__ptr + __lhs_sz + __rhs_sz, 1, _CharT());
+    __r.__annotate_new(__lhs_sz + __rhs_sz);
     return __r;
 }
 
@@ -4135,10 +4300,12 @@ operator+(_CharT __lhs, const basic_string<_CharT,_Traits,_Allocator>& __rhs)
     _String __r(__uninitialized_size_tag(),
                 __rhs_sz + 1,
                 _String::__alloc_traits::select_on_container_copy_construction(__rhs.get_allocator()));
+    __r.__annotate_delete();
     auto __ptr = std::__to_address(__r.__get_pointer());
     _Traits::assign(__ptr, 1, __lhs);
     _Traits::copy(__ptr + 1, __rhs.data(), __rhs_sz);
     _Traits::assign(__ptr + 1 + __rhs_sz, 1, _CharT());
+    __r.__annotate_new(1 + __rhs_sz);
     return __r;
 }
 
@@ -4153,10 +4320,12 @@ operator+(const basic_string<_CharT, _Traits, _Allocator>& __lhs, const _CharT*
     _String __r(__uninitialized_size_tag(),
                 __lhs_sz + __rhs_sz,
                 _String::__alloc_traits::select_on_container_copy_construction(__lhs.get_allocator()));
+    __r.__annotate_delete();
     auto __ptr = std::__to_address(__r.__get_pointer());
     _Traits::copy(__ptr, __lhs.data(), __lhs_sz);
     _Traits::copy(__ptr + __lhs_sz, __rhs, __rhs_sz);
     _Traits::assign(__ptr + __lhs_sz + __rhs_sz, 1, _CharT());
+    __r.__annotate_new(__lhs_sz + __rhs_sz);
     return __r;
 }
 
@@ -4170,10 +4339,12 @@ operator+(const basic_string<_CharT, _Traits, _Allocator>& __lhs, _CharT __rhs)
     _String __r(__uninitialized_size_tag(),
                 __lhs_sz + 1,
                 _String::__alloc_traits::select_on_container_copy_construction(__lhs.get_allocator()));
+    __r.__annotate_delete();
     auto __ptr = std::__to_address(__r.__get_pointer());
     _Traits::copy(__ptr, __lhs.data(), __lhs_sz);
     _Traits::assign(__ptr + __lhs_sz, 1, __rhs);
     _Traits::assign(__ptr + 1 + __lhs_sz, 1, _CharT());
+    __r.__annotate_new(__lhs_sz + 1);
     return __r;
 }
 
diff --git a/libcxx/test/std/strings/basic.string/string.capacity/capacity.pass.cpp b/libcxx/test/std/strings/basic.string/string.capacity/capacity.pass.cpp
index e1d20662e41de89..61867cfb087b939 100644
--- a/libcxx/test/std/strings/basic.string/string.capacity/capacity.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.capacity/capacity.pass.cpp
@@ -15,6 +15,7 @@
 
 #include "test_allocator.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 #include "test_macros.h"
 
@@ -28,6 +29,7 @@ TEST_CONSTEXPR_CXX20 void test_invariant(S s, test_allocator_statistics& alloc_s
     while (s.size() < s.capacity())
       s.push_back(typename S::value_type());
     assert(s.size() == s.capacity());
+    LIBCPP_ASSERT(is_string_asan_correct(s));
   }
 #ifndef TEST_HAS_NO_EXCEPTIONS
   catch (...) {
@@ -43,10 +45,12 @@ TEST_CONSTEXPR_CXX20 void test_string(const Alloc& a) {
   {
     S const s((Alloc(a)));
     assert(s.capacity() >= 0);
+    LIBCPP_ASSERT(is_string_asan_correct(s));
   }
   {
     S const s(3, 'x', Alloc(a));
     assert(s.capacity() >= 3);
+    LIBCPP_ASSERT(is_string_asan_correct(s));
   }
 #if TEST_STD_VER >= 11
   // Check that we perform SSO
@@ -54,6 +58,7 @@ TEST_CONSTEXPR_CXX20 void test_string(const Alloc& a) {
     S const s;
     assert(s.capacity() > 0);
     ASSERT_NOEXCEPT(s.capacity());
+    LIBCPP_ASSERT(is_string_asan_correct(s));
   }
 #endif
 }
@@ -63,18 +68,22 @@ TEST_CONSTEXPR_CXX20 bool test() {
   test_string(test_allocator<char>());
   test_string(test_allocator<char>(3));
   test_string(min_allocator<char>());
+  test_string(safe_allocator<char>());
 
   {
     test_allocator_statistics alloc_stats;
     typedef std::basic_string<char, std::char_traits<char>, test_allocator<char> > S;
     S s((test_allocator<char>(&alloc_stats)));
     test_invariant(s, alloc_stats);
+    LIBCPP_ASSERT(is_string_asan_correct(s));
     s.assign(10, 'a');
     s.erase(5);
     test_invariant(s, alloc_stats);
+    LIBCPP_ASSERT(is_string_asan_correct(s));
     s.assign(100, 'a');
     s.erase(50);
     test_invariant(s, alloc_stats);
+    LIBCPP_ASSERT(is_string_asan_correct(s));
   }
 
   return true;
diff --git a/libcxx/test/std/strings/basic.string/string.capacity/clear.pass.cpp b/libcxx/test/std/strings/basic.string/string.capacity/clear.pass.cpp
index 3a308de9b7569a3..7fa5cd43b80af40 100644
--- a/libcxx/test/std/strings/basic.string/string.capacity/clear.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.capacity/clear.pass.cpp
@@ -15,31 +15,38 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void test(S s) {
   s.clear();
   assert(s.size() == 0);
+  LIBCPP_ASSERT(is_string_asan_correct(s));
 }
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void test_string() {
   S s;
   test(s);
+  LIBCPP_ASSERT(is_string_asan_correct(s));
 
   s.assign(10, 'a');
   s.erase(5);
+  LIBCPP_ASSERT(is_string_asan_correct(s));
   test(s);
 
   s.assign(100, 'a');
   s.erase(50);
+  LIBCPP_ASSERT(is_string_asan_correct(s));
   test(s);
+  LIBCPP_ASSERT(is_string_asan_correct(s));
 }
 
 TEST_CONSTEXPR_CXX20 bool test() {
   test_string<std::string>();
 #if TEST_STD_VER >= 11
   test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
+  test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char>>>();
 #endif
 
   return true;
diff --git a/libcxx/test/std/strings/basic.string/string.capacity/reserve.pass.cpp b/libcxx/test/std/strings/basic.string/string.capacity/reserve.pass.cpp
index 03e3cf2f9d7d38e..47488906267521f 100644
--- a/libcxx/test/std/strings/basic.string/string.capacity/reserve.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.capacity/reserve.pass.cpp
@@ -18,6 +18,7 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 void test(typename S::size_type min_cap, typename S::size_type erased_index) {
@@ -33,6 +34,7 @@ void test(typename S::size_type min_cap, typename S::size_type erased_index) {
   assert(s == s0);
   assert(s.capacity() <= old_cap);
   assert(s.capacity() >= s.size());
+  LIBCPP_ASSERT(is_string_asan_correct(s));
 }
 
 template <class S>
@@ -46,6 +48,7 @@ bool test() {
   test_string<std::string>();
 #if TEST_STD_VER >= 11
   test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
+  test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char>>>();
 #endif
 
   return true;
diff --git a/libcxx/test/std/strings/basic.string/string.capacity/reserve_size.asan.pass.cpp b/libcxx/test/std/strings/basic.string/string.capacity/reserve_size.asan.pass.cpp
new file mode 100644
index 000000000000000..7b9f7442ff4e233
--- /dev/null
+++ b/libcxx/test/std/strings/basic.string/string.capacity/reserve_size.asan.pass.cpp
@@ -0,0 +1,66 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// <string>
+
+#include <string>
+#include <cassert>
+
+#include "test_macros.h"
+#include "asan_testing.h"
+
+template <class S>
+void test() {
+  S short_s1(3, 'a'), long_s1(100, 'c');
+  short_s1.reserve(0x1337);
+  long_s1.reserve(0x1337);
+
+  LIBCPP_ASSERT(is_string_asan_correct(short_s1));
+  LIBCPP_ASSERT(is_string_asan_correct(long_s1));
+#if TEST_STD_VER >= 11
+  short_s1.shrink_to_fit();
+  long_s1.shrink_to_fit();
+
+  LIBCPP_ASSERT(is_string_asan_correct(short_s1));
+  LIBCPP_ASSERT(is_string_asan_correct(long_s1));
+#endif
+  short_s1.clear();
+  long_s1.clear();
+
+  LIBCPP_ASSERT(is_string_asan_correct(short_s1));
+  LIBCPP_ASSERT(is_string_asan_correct(long_s1));
+#if TEST_STD_VER >= 11
+  short_s1.shrink_to_fit();
+  long_s1.shrink_to_fit();
+
+  LIBCPP_ASSERT(is_string_asan_correct(short_s1));
+  LIBCPP_ASSERT(is_string_asan_correct(long_s1));
+#endif
+  S short_s2(3, 'a'), long_s2(100, 'c');
+  short_s2.reserve(0x1);
+  long_s2.reserve(0x1);
+
+  LIBCPP_ASSERT(is_string_asan_correct(short_s2));
+  LIBCPP_ASSERT(is_string_asan_correct(long_s2));
+}
+
+int main(int, char**) {
+  test<std::string>();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  test<std::wstring>();
+#endif
+#if TEST_STD_VER >= 11
+  test<std::u16string>();
+  test<std::u32string>();
+#endif
+#if TEST_STD_VER >= 20
+  test<std::u8string>();
+#endif
+
+  return 0;
+}
diff --git a/libcxx/test/std/strings/basic.string/string.capacity/reserve_size.pass.cpp b/libcxx/test/std/strings/basic.string/string.capacity/reserve_size.pass.cpp
index bb804bf328b3b8d..5790a3a1754cd0d 100644
--- a/libcxx/test/std/strings/basic.string/string.capacity/reserve_size.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.capacity/reserve_size.pass.cpp
@@ -20,6 +20,7 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void
@@ -28,6 +29,7 @@ test(typename S::size_type min_cap, typename S::size_type erased_index, typename
   s.erase(erased_index);
   assert(s.size() == erased_index);
   assert(s.capacity() >= min_cap); // Check that we really have at least this capacity.
+  LIBCPP_ASSERT(is_string_asan_correct(s));
 
 #if TEST_STD_VER > 17
   typename S::size_type old_cap = s.capacity();
@@ -39,6 +41,7 @@ test(typename S::size_type min_cap, typename S::size_type erased_index, typename
     assert(s == s0);
     assert(s.capacity() >= res_arg);
     assert(s.capacity() >= s.size());
+    LIBCPP_ASSERT(is_string_asan_correct(s));
 #if TEST_STD_VER > 17
     assert(s.capacity() >= old_cap); // reserve never shrinks as of P0966 (C++20)
 #endif
diff --git a/libcxx/test/std/strings/basic.string/string.capacity/resize_and_overwrite.pass.cpp b/libcxx/test/std/strings/basic.string/string.capacity/resize_and_overwrite.pass.cpp
index bbe6551a0ff1181..00f5cccd30d6b73 100644
--- a/libcxx/test/std/strings/basic.string/string.capacity/resize_and_overwrite.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.capacity/resize_and_overwrite.pass.cpp
@@ -19,6 +19,7 @@
 
 #include "make_string.h"
 #include "test_macros.h"
+#include "asan_testing.h"
 
 template <class S>
 constexpr void test_appending(std::size_t k, size_t N, size_t new_capacity) {
@@ -76,11 +77,14 @@ constexpr bool test() {
 void test_value_categories() {
   std::string s;
   s.resize_and_overwrite(10, [](char*&&, std::size_t&&) { return 0; });
+  LIBCPP_ASSERT(is_string_asan_correct(s));
   s.resize_and_overwrite(10, [](char* const&, const std::size_t&) { return 0; });
+  LIBCPP_ASSERT(is_string_asan_correct(s));
   struct RefQualified {
     int operator()(char*, std::size_t) && { return 0; }
   };
   s.resize_and_overwrite(10, RefQualified{});
+  LIBCPP_ASSERT(is_string_asan_correct(s));
 }
 
 int main(int, char**) {
diff --git a/libcxx/test/std/strings/basic.string/string.capacity/resize_size.pass.cpp b/libcxx/test/std/strings/basic.string/string.capacity/resize_size.pass.cpp
index 487b12d9df87f33..7cf4b7ca3b6efd6 100644
--- a/libcxx/test/std/strings/basic.string/string.capacity/resize_size.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.capacity/resize_size.pass.cpp
@@ -16,6 +16,7 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void test(S s, typename S::size_type n, S expected) {
@@ -23,6 +24,7 @@ TEST_CONSTEXPR_CXX20 void test(S s, typename S::size_type n, S expected) {
     s.resize(n);
     LIBCPP_ASSERT(s.__invariants());
     assert(s == expected);
+    LIBCPP_ASSERT(is_string_asan_correct(s));
   }
 #ifndef TEST_HAS_NO_EXCEPTIONS
   else if (!TEST_IS_CONSTANT_EVALUATED) {
@@ -61,6 +63,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
   test_string<std::string>();
 #if TEST_STD_VER >= 11
   test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
+  test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char>>>();
 #endif
 
   return true;
diff --git a/libcxx/test/std/strings/basic.string/string.capacity/resize_size_char.pass.cpp b/libcxx/test/std/strings/basic.string/string.capacity/resize_size_char.pass.cpp
index 3b6adc0b0afeb19..e3b925ca8bcdbde 100644
--- a/libcxx/test/std/strings/basic.string/string.capacity/resize_size_char.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.capacity/resize_size_char.pass.cpp
@@ -16,6 +16,7 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void test(S s, typename S::size_type n, typename S::value_type c, S expected) {
@@ -23,6 +24,7 @@ TEST_CONSTEXPR_CXX20 void test(S s, typename S::size_type n, typename S::value_t
     s.resize(n, c);
     LIBCPP_ASSERT(s.__invariants());
     assert(s == expected);
+    LIBCPP_ASSERT(is_string_asan_correct(s));
   }
 #ifndef TEST_HAS_NO_EXCEPTIONS
   else if (!TEST_IS_CONSTANT_EVALUATED) {
@@ -57,12 +59,23 @@ TEST_CONSTEXPR_CXX20 void test_string() {
        'a',
        S("12345678901234567890123456789012345678901234567890aaaaaaaaaa"));
   test(S(), S::npos, 'a', S("not going to happen"));
+  //ASan:
+  test(S(), 21, 'a', S("aaaaaaaaaaaaaaaaaaaaa"));
+  test(S(), 22, 'a', S("aaaaaaaaaaaaaaaaaaaaaa"));
+  test(S(), 23, 'a', S("aaaaaaaaaaaaaaaaaaaaaaa"));
+  test(S(), 24, 'a', S("aaaaaaaaaaaaaaaaaaaaaaaa"));
+  test(S(), 29, 'a', S("aaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
+  test(S(), 30, 'a', S("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
+  test(S(), 31, 'a', S("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
+  test(S(), 32, 'a', S("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
+  test(S(), 33, 'a', S("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
 }
 
 TEST_CONSTEXPR_CXX20 bool test() {
   test_string<std::string>();
 #if TEST_STD_VER >= 11
   test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
+  test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char>>>();
 #endif
 
   return true;
diff --git a/libcxx/test/std/strings/basic.string/string.capacity/shrink_to_fit.pass.cpp b/libcxx/test/std/strings/basic.string/string.capacity/shrink_to_fit.pass.cpp
index 1efebfc9cea561b..57f6b49eaf99451 100644
--- a/libcxx/test/std/strings/basic.string/string.capacity/shrink_to_fit.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.capacity/shrink_to_fit.pass.cpp
@@ -15,6 +15,7 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void test(S s) {
@@ -25,6 +26,7 @@ TEST_CONSTEXPR_CXX20 void test(S s) {
   assert(s == s0);
   assert(s.capacity() <= old_cap);
   assert(s.capacity() >= s.size());
+  LIBCPP_ASSERT(is_string_asan_correct(s));
 }
 
 template <class S>
@@ -39,12 +41,18 @@ TEST_CONSTEXPR_CXX20 void test_string() {
   s.assign(100, 'a');
   s.erase(50);
   test(s);
+
+  s.assign(100, 'a');
+  for (int i = 0; i < 90; ++i)
+    s.erase(1);
+  test(s);
 }
 
 TEST_CONSTEXPR_CXX20 bool test() {
   test_string<std::string>();
 #if TEST_STD_VER >= 11
   test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
+  test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char>>>();
 #endif
 
   return true;
diff --git a/libcxx/test/std/strings/basic.string/string.cons/T_size_size.pass.cpp b/libcxx/test/std/strings/basic.string/string.cons/T_size_size.pass.cpp
index a6b625b7b0e8113..dcf697bed752fa4 100644
--- a/libcxx/test/std/strings/basic.string/string.cons/T_size_size.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.cons/T_size_size.pass.cpp
@@ -23,6 +23,7 @@
 #include "test_macros.h"
 #include "test_allocator.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S, class SV>
 TEST_CONSTEXPR_CXX20 void test(SV sv, std::size_t pos, std::size_t n) {
@@ -38,6 +39,7 @@ TEST_CONSTEXPR_CXX20 void test(SV sv, std::size_t pos, std::size_t n) {
     assert(T::compare(s2.data(), sv.data() + pos, rlen) == 0);
     assert(s2.get_allocator() == A());
     assert(s2.capacity() >= s2.size());
+    LIBCPP_ASSERT(is_string_asan_correct(s2));
   }
 #ifndef TEST_HAS_NO_EXCEPTIONS
   else if (!TEST_IS_CONSTANT_EVALUATED) {
@@ -113,6 +115,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
   test_string(test_allocator<char>(8));
 #if TEST_STD_VER >= 11
   test_string(min_allocator<char>());
+  test_string(safe_allocator<char>());
 #endif
 
   {
diff --git a/libcxx/test/std/strings/basic.string/string.cons/alloc.pass.cpp b/libcxx/test/std/strings/basic.string/string.cons/alloc.pass.cpp
index 97a0566ba031b00..91beac37764db48 100644
--- a/libcxx/test/std/strings/basic.string/string.cons/alloc.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.cons/alloc.pass.cpp
@@ -16,6 +16,7 @@
 #include "test_macros.h"
 #include "test_allocator.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void test() {
@@ -31,6 +32,7 @@ TEST_CONSTEXPR_CXX20 void test() {
     assert(s.size() == 0);
     assert(s.capacity() >= s.size());
     assert(s.get_allocator() == typename S::allocator_type());
+    LIBCPP_ASSERT(is_string_asan_correct(s));
   }
   {
 #if TEST_STD_VER > 14
@@ -46,6 +48,7 @@ TEST_CONSTEXPR_CXX20 void test() {
     assert(s.size() == 0);
     assert(s.capacity() >= s.size());
     assert(s.get_allocator() == typename S::allocator_type(5));
+    LIBCPP_ASSERT(is_string_asan_correct(s));
   }
 }
 
@@ -65,6 +68,7 @@ TEST_CONSTEXPR_CXX20 void test2() {
     assert(s.size() == 0);
     assert(s.capacity() >= s.size());
     assert(s.get_allocator() == typename S::allocator_type());
+    LIBCPP_ASSERT(is_string_asan_correct(s));
   }
   {
 #  if TEST_STD_VER > 14
@@ -80,6 +84,7 @@ TEST_CONSTEXPR_CXX20 void test2() {
     assert(s.size() == 0);
     assert(s.capacity() >= s.size());
     assert(s.get_allocator() == typename S::allocator_type());
+    LIBCPP_ASSERT(is_string_asan_correct(s));
   }
 }
 
@@ -89,6 +94,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
   test<std::basic_string<char, std::char_traits<char>, test_allocator<char> > >();
 #if TEST_STD_VER >= 11
   test2<std::basic_string<char, std::char_traits<char>, min_allocator<char> > >();
+  test2<std::basic_string<char, std::char_traits<char>, safe_allocator<char> > >();
   test2<std::basic_string<char, std::char_traits<char>, explicit_allocator<char> > >();
 #endif
 
diff --git a/libcxx/test/std/strings/basic.string/string.cons/brace_assignment.pass.cpp b/libcxx/test/std/strings/basic.string/string.cons/brace_assignment.pass.cpp
index e7d18b4ca87179d..6b3a665ced447f5 100644
--- a/libcxx/test/std/strings/basic.string/string.cons/brace_assignment.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.cons/brace_assignment.pass.cpp
@@ -17,6 +17,7 @@
 #include <cassert>
 
 #include "test_macros.h"
+#include "asan_testing.h"
 
 TEST_CONSTEXPR_CXX20 bool test() {
   // Test that assignment from {} and {ptr, len} are allowed and are not
@@ -25,11 +26,13 @@ TEST_CONSTEXPR_CXX20 bool test() {
     std::string s = "hello world";
     s             = {};
     assert(s.empty());
+    LIBCPP_ASSERT(is_string_asan_correct(s));
   }
   {
     std::string s = "hello world";
     s             = {"abc", 2};
     assert(s == "ab");
+    LIBCPP_ASSERT(is_string_asan_correct(s));
   }
 
   return true;
diff --git a/libcxx/test/std/strings/basic.string/string.cons/char_assignment.pass.cpp b/libcxx/test/std/strings/basic.string/string.cons/char_assignment.pass.cpp
index 3cffc82e9483526..1019dc8bca5df52 100644
--- a/libcxx/test/std/strings/basic.string/string.cons/char_assignment.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.cons/char_assignment.pass.cpp
@@ -15,6 +15,7 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void test(S s1, typename S::value_type s2) {
@@ -24,6 +25,7 @@ TEST_CONSTEXPR_CXX20 void test(S s1, typename S::value_type s2) {
   assert(s1.size() == 1);
   assert(T::eq(s1[0], s2));
   assert(s1.capacity() >= s1.size());
+  LIBCPP_ASSERT(is_string_asan_correct(s1));
 }
 
 template <class S>
@@ -38,6 +40,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
   test_string<std::string>();
 #if TEST_STD_VER >= 11
   test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
+  test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char>>>();
 #endif
 
   return true;
diff --git a/libcxx/test/std/strings/basic.string/string.cons/copy.pass.cpp b/libcxx/test/std/strings/basic.string/string.cons/copy.pass.cpp
index 3afe76e88316f99..f65f8e97c98249c 100644
--- a/libcxx/test/std/strings/basic.string/string.cons/copy.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.cons/copy.pass.cpp
@@ -16,6 +16,7 @@
 #include "test_macros.h"
 #include "test_allocator.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void test(S s1) {
@@ -24,6 +25,8 @@ TEST_CONSTEXPR_CXX20 void test(S s1) {
   assert(s2 == s1);
   assert(s2.capacity() >= s2.size());
   assert(s2.get_allocator() == s1.get_allocator());
+  LIBCPP_ASSERT(is_string_asan_correct(s1));
+  LIBCPP_ASSERT(is_string_asan_correct(s2));
 }
 
 template <class Alloc>
@@ -40,6 +43,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
   test_string(test_allocator<char>(3));
 #if TEST_STD_VER >= 11
   test_string(min_allocator<char>());
+  test_string(safe_allocator<char>());
 #endif
 
   return true;
diff --git a/libcxx/test/std/strings/basic.string/string.cons/copy_alloc.pass.cpp b/libcxx/test/std/strings/basic.string/string.cons/copy_alloc.pass.cpp
index 6b0040376a424ec..b0045cb4afbba58 100644
--- a/libcxx/test/std/strings/basic.string/string.cons/copy_alloc.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.cons/copy_alloc.pass.cpp
@@ -16,6 +16,7 @@
 #include "test_macros.h"
 #include "test_allocator.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 #ifndef TEST_HAS_NO_EXCEPTIONS
 struct alloc_imp {
@@ -83,6 +84,8 @@ TEST_CONSTEXPR_CXX20 void test(S s1, const typename S::allocator_type& a) {
   assert(s2 == s1);
   assert(s2.capacity() >= s2.size());
   assert(s2.get_allocator() == a);
+  LIBCPP_ASSERT(is_string_asan_correct(s1));
+  LIBCPP_ASSERT(is_string_asan_correct(s2));
 }
 
 template <class Alloc>
@@ -99,6 +102,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
   test_string(test_allocator<char>(3));
 #if TEST_STD_VER >= 11
   test_string(min_allocator<char>());
+  test_string(safe_allocator<char>());
 #endif
 
 #if TEST_STD_VER >= 11
diff --git a/libcxx/test/std/strings/basic.string/string.cons/copy_assignment.pass.cpp b/libcxx/test/std/strings/basic.string/string.cons/copy_assignment.pass.cpp
index 8d0fcb3c6d2943f..8d993829f6add35 100644
--- a/libcxx/test/std/strings/basic.string/string.cons/copy_assignment.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.cons/copy_assignment.pass.cpp
@@ -16,6 +16,7 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void test(S s1, const S& s2) {
@@ -23,6 +24,8 @@ TEST_CONSTEXPR_CXX20 void test(S s1, const S& s2) {
   LIBCPP_ASSERT(s1.__invariants());
   assert(s1 == s2);
   assert(s1.capacity() >= s1.size());
+  LIBCPP_ASSERT(is_string_asan_correct(s1));
+  LIBCPP_ASSERT(is_string_asan_correct(s2));
 }
 
 template <class S>
@@ -46,6 +49,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
   test_string<std::string>();
 #if TEST_STD_VER >= 11
   test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
+  test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char>>>();
 #endif
 
 #if TEST_STD_VER >= 11
diff --git a/libcxx/test/std/strings/basic.string/string.cons/default.pass.cpp b/libcxx/test/std/strings/basic.string/string.cons/default.pass.cpp
index 3993a40dd5a165c..fc263f9820cb5b7 100644
--- a/libcxx/test/std/strings/basic.string/string.cons/default.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.cons/default.pass.cpp
@@ -15,6 +15,7 @@
 
 #include "test_macros.h"
 #include "test_allocator.h"
+#include "asan_testing.h"
 
 #if TEST_STD_VER >= 11
 // Test the noexcept specification, which is a conforming extension
@@ -30,6 +31,7 @@ LIBCPP_STATIC_ASSERT(!std::is_nothrow_default_constructible<
 TEST_CONSTEXPR_CXX20 bool test() {
   std::string str;
   assert(str.empty());
+  LIBCPP_ASSERT(is_string_asan_correct(str));
 
   return true;
 }
diff --git a/libcxx/test/std/strings/basic.string/string.cons/initializer_list.pass.cpp b/libcxx/test/std/strings/basic.string/string.cons/initializer_list.pass.cpp
index 5b7e8bde2e6e877..e326a71dc7a461f 100644
--- a/libcxx/test/std/strings/basic.string/string.cons/initializer_list.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.cons/initializer_list.pass.cpp
@@ -18,6 +18,7 @@
 #include "test_macros.h"
 #include "test_allocator.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 // clang-format off
 template <template <class> class Alloc>
@@ -25,11 +26,19 @@ TEST_CONSTEXPR_CXX20 void test_string() {
   {
     std::basic_string<char, std::char_traits<char>, Alloc<char> > s = {'a', 'b', 'c'};
     assert(s == "abc");
+    LIBCPP_ASSERT(is_string_asan_correct(s));
+  }
+  {
+    std::basic_string<char, std::char_traits<char>, Alloc<char> > s;
+    s = {'a', 'b', 'c'};
+    assert(s == "abc");
+    LIBCPP_ASSERT(is_string_asan_correct(s));
   }
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
   {
     std::basic_string<wchar_t, std::char_traits<wchar_t>, Alloc<wchar_t> > s = {L'a', L'b', L'c'};
     assert(s == L"abc");
+    LIBCPP_ASSERT(is_string_asan_correct(s));
   }
 #endif
 }
@@ -38,6 +47,7 @@ TEST_CONSTEXPR_CXX20 void test_string() {
 TEST_CONSTEXPR_CXX20 bool test() {
   test_string<std::allocator>();
   test_string<min_allocator>();
+  test_string<safe_allocator>();
 
   return true;
 }
diff --git a/libcxx/test/std/strings/basic.string/string.cons/initializer_list_assignment.pass.cpp b/libcxx/test/std/strings/basic.string/string.cons/initializer_list_assignment.pass.cpp
index 88597daba121ac4..90be1cc58857ba2 100644
--- a/libcxx/test/std/strings/basic.string/string.cons/initializer_list_assignment.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.cons/initializer_list_assignment.pass.cpp
@@ -17,6 +17,7 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 // clang-format off
 template <template <class> class Alloc>
@@ -27,6 +28,16 @@ TEST_CONSTEXPR_CXX20 void test_string() {
     S& result = (s = {'a', 'b', 'c'});
     assert(s == "abc");
     assert(&result == &s);
+    LIBCPP_ASSERT(is_string_asan_correct(s));
+    LIBCPP_ASSERT(is_string_asan_correct(result));
+  }
+  {
+    typedef std::basic_string<char, std::char_traits<char>, Alloc<char>> S;
+    S s;
+    s = {'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a',
+         'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a'};
+    assert(s == "aaaaaaaaaaaaaaaaaaaaaaaa");
+    LIBCPP_ASSERT(is_string_asan_correct(s));
   }
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
   {
@@ -35,6 +46,8 @@ TEST_CONSTEXPR_CXX20 void test_string() {
     S& result = (s = {L'a', L'b', L'c'});
     assert(s == L"abc");
     assert(&result == &s);
+    LIBCPP_ASSERT(is_string_asan_correct(s));
+    LIBCPP_ASSERT(is_string_asan_correct(result));
   }
 #endif
 }
@@ -43,6 +56,7 @@ TEST_CONSTEXPR_CXX20 void test_string() {
 TEST_CONSTEXPR_CXX20 bool test() {
   test_string<std::allocator>();
   test_string<min_allocator>();
+  test_string<safe_allocator>();
 
   return true;
 }
diff --git a/libcxx/test/std/strings/basic.string/string.cons/iter_alloc.pass.cpp b/libcxx/test/std/strings/basic.string/string.cons/iter_alloc.pass.cpp
index 2ef73e5ec8c1ade..e14227d1a77171e 100644
--- a/libcxx/test/std/strings/basic.string/string.cons/iter_alloc.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.cons/iter_alloc.pass.cpp
@@ -21,6 +21,7 @@
 #include "test_allocator.h"
 #include "test_iterators.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class Alloc, class It>
 TEST_CONSTEXPR_CXX20 void test(It first, It last) {
@@ -37,6 +38,7 @@ TEST_CONSTEXPR_CXX20 void test(It first, It last) {
   }
   assert(s2.get_allocator() == Alloc());
   assert(s2.capacity() >= s2.size());
+  LIBCPP_ASSERT(is_string_asan_correct(s2));
 }
 
 template <class Alloc, class It>
@@ -54,6 +56,7 @@ TEST_CONSTEXPR_CXX20 void test(It first, It last, const Alloc& a) {
   }
   assert(s2.get_allocator() == a);
   assert(s2.capacity() >= s2.size());
+  LIBCPP_ASSERT(is_string_asan_correct(s2));
 }
 
 template <class Alloc>
diff --git a/libcxx/test/std/strings/basic.string/string.cons/iter_alloc_deduction.pass.cpp b/libcxx/test/std/strings/basic.string/string.cons/iter_alloc_deduction.pass.cpp
index 65762ccf89d9304..d9176da63d0dc73 100644
--- a/libcxx/test/std/strings/basic.string/string.cons/iter_alloc_deduction.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.cons/iter_alloc_deduction.pass.cpp
@@ -29,6 +29,7 @@
 #include "test_macros.h"
 #include "test_allocator.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 class NotAnIterator {};
 using NotAnInputIterator = std::back_insert_iterator<std::basic_string<char16_t>>;
@@ -66,6 +67,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
     static_assert(std::is_same_v<S::allocator_type, std::allocator<char>>, "");
     assert(s1.size() == 10);
     assert(s1.compare(0, s1.size(), s, s1.size()) == 0);
+    LIBCPP_ASSERT(is_string_asan_correct(s1));
   }
   {
     const char* s = "12345678901234";
@@ -76,6 +78,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
     static_assert(std::is_same_v<S::allocator_type, std::allocator<char>>, "");
     assert(s1.size() == 10);
     assert(s1.compare(0, s1.size(), s, s1.size()) == 0);
+    LIBCPP_ASSERT(is_string_asan_correct(s1));
   }
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
   {
@@ -87,6 +90,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
     static_assert(std::is_same_v<S::allocator_type, test_allocator<wchar_t>>, "");
     assert(s1.size() == 10);
     assert(s1.compare(0, s1.size(), s, s1.size()) == 0);
+    LIBCPP_ASSERT(is_string_asan_correct(s1));
   }
 #endif
   {
@@ -98,6 +102,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
     static_assert(std::is_same_v<S::allocator_type, min_allocator<char16_t>>, "");
     assert(s1.size() == 10);
     assert(s1.compare(0, s1.size(), s, s1.size()) == 0);
+    LIBCPP_ASSERT(is_string_asan_correct(s1));
   }
   {
     const char32_t* s = U"12345678901234";
@@ -108,6 +113,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
     static_assert(std::is_same_v<S::allocator_type, explicit_allocator<char32_t>>, "");
     assert(s1.size() == 10);
     assert(s1.compare(0, s1.size(), s, s1.size()) == 0);
+    LIBCPP_ASSERT(is_string_asan_correct(s1));
   }
 
   return true;
diff --git a/libcxx/test/std/strings/basic.string/string.cons/move.pass.cpp b/libcxx/test/std/strings/basic.string/string.cons/move.pass.cpp
index f5ffebd35d50e92..5d2d774b3606383 100644
--- a/libcxx/test/std/strings/basic.string/string.cons/move.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.cons/move.pass.cpp
@@ -18,6 +18,7 @@
 #include "test_macros.h"
 #include "test_allocator.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void test(S s0) {
@@ -28,6 +29,9 @@ TEST_CONSTEXPR_CXX20 void test(S s0) {
   assert(s2 == s1);
   assert(s2.capacity() >= s2.size());
   assert(s2.get_allocator() == s1.get_allocator());
+  LIBCPP_ASSERT(is_string_asan_correct(s0));
+  LIBCPP_ASSERT(is_string_asan_correct(s1));
+  LIBCPP_ASSERT(is_string_asan_correct(s2));
 }
 
 template <class Alloc>
diff --git a/libcxx/test/std/strings/basic.string/string.cons/move_alloc.pass.cpp b/libcxx/test/std/strings/basic.string/string.cons/move_alloc.pass.cpp
index 0e5c72192adea64..7210d67b5cb226b 100644
--- a/libcxx/test/std/strings/basic.string/string.cons/move_alloc.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.cons/move_alloc.pass.cpp
@@ -18,6 +18,7 @@
 #include "test_macros.h"
 #include "test_allocator.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void test(S s0, const typename S::allocator_type& a) {
@@ -28,6 +29,9 @@ TEST_CONSTEXPR_CXX20 void test(S s0, const typename S::allocator_type& a) {
   assert(s2 == s1);
   assert(s2.capacity() >= s2.size());
   assert(s2.get_allocator() == a);
+  LIBCPP_ASSERT(is_string_asan_correct(s0));
+  LIBCPP_ASSERT(is_string_asan_correct(s1));
+  LIBCPP_ASSERT(is_string_asan_correct(s2));
 }
 
 TEST_CONSTEXPR_CXX20 bool test() {
diff --git a/libcxx/test/std/strings/basic.string/string.cons/move_assignment.pass.cpp b/libcxx/test/std/strings/basic.string/string.cons/move_assignment.pass.cpp
index 89cb44d62ef2b6c..3e42dfcb23d437c 100644
--- a/libcxx/test/std/strings/basic.string/string.cons/move_assignment.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.cons/move_assignment.pass.cpp
@@ -19,6 +19,7 @@
 #include "test_macros.h"
 #include "test_allocator.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void test(S s1, S s2) {
@@ -28,6 +29,9 @@ TEST_CONSTEXPR_CXX20 void test(S s1, S s2) {
   LIBCPP_ASSERT(s2.__invariants());
   assert(s1 == s0);
   assert(s1.capacity() >= s1.size());
+  LIBCPP_ASSERT(is_string_asan_correct(s0));
+  LIBCPP_ASSERT(is_string_asan_correct(s1));
+  LIBCPP_ASSERT(is_string_asan_correct(s2));
 }
 
 template <class S>
@@ -51,6 +55,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
   test_string<std::string>();
 #if TEST_STD_VER >= 11
   test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
+  test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char>>>();
 #endif
 
   return true;
diff --git a/libcxx/test/std/strings/basic.string/string.cons/pointer_alloc.pass.cpp b/libcxx/test/std/strings/basic.string/string.cons/pointer_alloc.pass.cpp
index 84a29df5b57bf16..d5d5152e11e54e9 100644
--- a/libcxx/test/std/strings/basic.string/string.cons/pointer_alloc.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.cons/pointer_alloc.pass.cpp
@@ -19,6 +19,7 @@
 #include "test_macros.h"
 #include "test_allocator.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class Alloc, class charT>
 TEST_CONSTEXPR_CXX20 void test(const charT* s) {
@@ -31,6 +32,7 @@ TEST_CONSTEXPR_CXX20 void test(const charT* s) {
   assert(T::compare(s2.data(), s, n) == 0);
   assert(s2.get_allocator() == Alloc());
   assert(s2.capacity() >= s2.size());
+  LIBCPP_ASSERT(is_string_asan_correct(s2));
 }
 
 template <class Alloc, class charT>
@@ -44,6 +46,7 @@ TEST_CONSTEXPR_CXX20 void test(const charT* s, const Alloc& a) {
   assert(T::compare(s2.data(), s, n) == 0);
   assert(s2.get_allocator() == a);
   assert(s2.capacity() >= s2.size());
+  LIBCPP_ASSERT(is_string_asan_correct(s2));
 }
 
 template <class Alloc>
@@ -67,6 +70,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
   test(test_allocator<char>(2));
 #if TEST_STD_VER >= 11
   test(min_allocator<char>());
+  test(safe_allocator<char>());
 #endif
 
   return true;
diff --git a/libcxx/test/std/strings/basic.string/string.cons/pointer_assignment.pass.cpp b/libcxx/test/std/strings/basic.string/string.cons/pointer_assignment.pass.cpp
index 4b3027a2aad1183..d463763223c90ee 100644
--- a/libcxx/test/std/strings/basic.string/string.cons/pointer_assignment.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.cons/pointer_assignment.pass.cpp
@@ -16,6 +16,7 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void test(S s1, const typename S::value_type* s2) {
@@ -25,6 +26,7 @@ TEST_CONSTEXPR_CXX20 void test(S s1, const typename S::value_type* s2) {
   assert(s1.size() == T::length(s2));
   assert(T::compare(s1.data(), s2, s1.size()) == 0);
   assert(s1.capacity() >= s1.size());
+  LIBCPP_ASSERT(is_string_asan_correct(s1));
 }
 
 template <class S>
@@ -48,6 +50,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
   test_string<std::string>();
 #if TEST_STD_VER >= 11
   test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
+  test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char>>>();
 #endif
 
   return true;
diff --git a/libcxx/test/std/strings/basic.string/string.cons/pointer_size_alloc.pass.cpp b/libcxx/test/std/strings/basic.string/string.cons/pointer_size_alloc.pass.cpp
index e0dab72b5c6327e..b6ba2be46d18bd0 100644
--- a/libcxx/test/std/strings/basic.string/string.cons/pointer_size_alloc.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.cons/pointer_size_alloc.pass.cpp
@@ -18,6 +18,7 @@
 #include "test_macros.h"
 #include "test_allocator.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class Alloc, class CharT>
 TEST_CONSTEXPR_CXX20 void test(const CharT* s, unsigned n) {
@@ -29,6 +30,7 @@ TEST_CONSTEXPR_CXX20 void test(const CharT* s, unsigned n) {
   assert(T::compare(s2.data(), s, n) == 0);
   assert(s2.get_allocator() == Alloc());
   assert(s2.capacity() >= s2.size());
+  LIBCPP_ASSERT(is_string_asan_correct(s2));
 }
 
 template <class Alloc, class CharT>
@@ -41,6 +43,7 @@ TEST_CONSTEXPR_CXX20 void test(const CharT* s, unsigned n, const Alloc& a) {
   assert(T::compare(s2.data(), s, n) == 0);
   assert(s2.get_allocator() == a);
   assert(s2.capacity() >= s2.size());
+  LIBCPP_ASSERT(is_string_asan_correct(s2));
 }
 
 template <class Alloc>
@@ -64,6 +67,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
   test(test_allocator<char>(2));
 #if TEST_STD_VER >= 11
   test(min_allocator<char>());
+  test(safe_allocator<char>());
 #endif
 
 #if TEST_STD_VER >= 11
diff --git a/libcxx/test/std/strings/basic.string/string.cons/size_char_alloc.pass.cpp b/libcxx/test/std/strings/basic.string/string.cons/size_char_alloc.pass.cpp
index 3afbcdffa6bdfe3..a87e01f0fc0881f 100644
--- a/libcxx/test/std/strings/basic.string/string.cons/size_char_alloc.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.cons/size_char_alloc.pass.cpp
@@ -19,6 +19,7 @@
 #include "test_macros.h"
 #include "test_allocator.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class Alloc, class charT>
 TEST_CONSTEXPR_CXX20 void test(unsigned n, charT c) {
@@ -30,6 +31,7 @@ TEST_CONSTEXPR_CXX20 void test(unsigned n, charT c) {
     assert(s2[i] == c);
   assert(s2.get_allocator() == Alloc());
   assert(s2.capacity() >= s2.size());
+  LIBCPP_ASSERT(is_string_asan_correct(s2));
 }
 
 template <class Alloc, class charT>
@@ -42,6 +44,7 @@ TEST_CONSTEXPR_CXX20 void test(unsigned n, charT c, const Alloc& a) {
     assert(s2[i] == c);
   assert(s2.get_allocator() == a);
   assert(s2.capacity() >= s2.size());
+  LIBCPP_ASSERT(is_string_asan_correct(s2));
 }
 
 template <class Alloc, class Tp>
@@ -94,6 +97,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
   test_string(test_allocator<char>(2));
 #if TEST_STD_VER >= 11
   test_string(min_allocator<char>());
+  test_string(safe_allocator<char>());
 #endif
 
   return true;
diff --git a/libcxx/test/std/strings/basic.string/string.cons/string_view.pass.cpp b/libcxx/test/std/strings/basic.string/string.cons/string_view.pass.cpp
index 74fa76a013a6926..ad37b50b83ba7ee 100644
--- a/libcxx/test/std/strings/basic.string/string.cons/string_view.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.cons/string_view.pass.cpp
@@ -20,6 +20,7 @@
 #include "min_allocator.h"
 #include "test_allocator.h"
 #include "test_macros.h"
+#include "asan_testing.h"
 
 static_assert(!std::is_convertible<std::string_view, std::string const&>::value, "");
 static_assert(!std::is_convertible<std::string_view, std::string>::value, "");
@@ -35,6 +36,7 @@ TEST_CONSTEXPR_CXX20 void test(std::basic_string_view<CharT> sv) {
     assert(T::compare(s2.data(), sv.data(), sv.size()) == 0);
     assert(s2.get_allocator() == Alloc());
     assert(s2.capacity() >= s2.size());
+    LIBCPP_ASSERT(is_string_asan_correct(s2));
   }
   {
     S s2;
@@ -44,6 +46,7 @@ TEST_CONSTEXPR_CXX20 void test(std::basic_string_view<CharT> sv) {
     assert(T::compare(s2.data(), sv.data(), sv.size()) == 0);
     assert(s2.get_allocator() == Alloc());
     assert(s2.capacity() >= s2.size());
+    LIBCPP_ASSERT(is_string_asan_correct(s2));
   }
 }
 
@@ -58,6 +61,7 @@ TEST_CONSTEXPR_CXX20 void test(std::basic_string_view<CharT> sv, const Alloc& a)
     assert(T::compare(s2.data(), sv.data(), sv.size()) == 0);
     assert(s2.get_allocator() == a);
     assert(s2.capacity() >= s2.size());
+    LIBCPP_ASSERT(is_string_asan_correct(s2));
   }
   {
     S s2(a);
@@ -67,6 +71,7 @@ TEST_CONSTEXPR_CXX20 void test(std::basic_string_view<CharT> sv, const Alloc& a)
     assert(T::compare(s2.data(), sv.data(), sv.size()) == 0);
     assert(s2.get_allocator() == a);
     assert(s2.capacity() >= s2.size());
+    LIBCPP_ASSERT(is_string_asan_correct(s2));
   }
 }
 
@@ -93,6 +98,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
   test_string(test_allocator<char>(2));
 #if TEST_STD_VER >= 11
   test_string(min_allocator<char>());
+  test_string(safe_allocator<char>());
 #endif
 
   return true;
diff --git a/libcxx/test/std/strings/basic.string/string.cons/string_view_assignment.pass.cpp b/libcxx/test/std/strings/basic.string/string.cons/string_view_assignment.pass.cpp
index 3c88f9e40fc0b3b..31d2c231e4cb877 100644
--- a/libcxx/test/std/strings/basic.string/string.cons/string_view_assignment.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.cons/string_view_assignment.pass.cpp
@@ -15,6 +15,7 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S, class SV>
 TEST_CONSTEXPR_CXX20 void test(S s1, SV sv) {
@@ -24,6 +25,7 @@ TEST_CONSTEXPR_CXX20 void test(S s1, SV sv) {
   assert(s1.size() == sv.size());
   assert(T::compare(s1.data(), sv.data(), s1.size()) == 0);
   assert(s1.capacity() >= s1.size());
+  LIBCPP_ASSERT(is_string_asan_correct(s1));
 }
 
 template <class S>
@@ -48,6 +50,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
   test_string<std::string>();
 #if TEST_STD_VER >= 11
   test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
+  test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char>>>();
 #endif
 
   return true;
diff --git a/libcxx/test/std/strings/basic.string/string.cons/substr.pass.cpp b/libcxx/test/std/strings/basic.string/string.cons/substr.pass.cpp
index 693edab76b17b0c..cafd9674f4898d7 100644
--- a/libcxx/test/std/strings/basic.string/string.cons/substr.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.cons/substr.pass.cpp
@@ -26,6 +26,7 @@
 #include "test_macros.h"
 #include "test_allocator.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void test(S str, unsigned pos) {
@@ -40,6 +41,7 @@ TEST_CONSTEXPR_CXX20 void test(S str, unsigned pos) {
     assert(T::compare(s2.data(), str.data() + pos, rlen) == 0);
     assert(s2.get_allocator() == A());
     assert(s2.capacity() >= s2.size());
+    LIBCPP_ASSERT(is_string_asan_correct(s2));
   }
 #ifndef TEST_HAS_NO_EXCEPTIONS
   else if (!TEST_IS_CONSTANT_EVALUATED) {
@@ -65,6 +67,7 @@ TEST_CONSTEXPR_CXX20 void test(S str, unsigned pos, unsigned n) {
     assert(T::compare(s2.data(), str.data() + pos, rlen) == 0);
     assert(s2.get_allocator() == A());
     assert(s2.capacity() >= s2.size());
+    LIBCPP_ASSERT(is_string_asan_correct(s2));
   }
 #ifndef TEST_HAS_NO_EXCEPTIONS
   else if (!TEST_IS_CONSTANT_EVALUATED) {
@@ -90,6 +93,7 @@ TEST_CONSTEXPR_CXX20 void test(S str, unsigned pos, unsigned n, const typename S
     assert(T::compare(s2.data(), str.data() + pos, rlen) == 0);
     assert(s2.get_allocator() == a);
     assert(s2.capacity() >= s2.size());
+    LIBCPP_ASSERT(is_string_asan_correct(s2));
   }
 #ifndef TEST_HAS_NO_EXCEPTIONS
   else if (!TEST_IS_CONSTANT_EVALUATED) {
@@ -164,6 +168,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
   test_string(test_allocator<char>(3), test_allocator<char>(5));
 #if TEST_STD_VER >= 11
   test_string(min_allocator<char>(), min_allocator<char>());
+  test_string(safe_allocator<char>(), safe_allocator<char>());
 #endif
 
   return true;
diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_append/initializer_list.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_append/initializer_list.pass.cpp
index 6c5049f16bc6192..f1f5828bfe21d7a 100644
--- a/libcxx/test/std/strings/basic.string/string.modifiers/string_append/initializer_list.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_append/initializer_list.pass.cpp
@@ -18,6 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 #include "nasty_string.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void test() {
@@ -26,6 +27,7 @@ TEST_CONSTEXPR_CXX20 void test() {
   S s(CONVERT_TO_CSTRING(CharT, "123"));
   s.append({CharT('a'), CharT('b'), CharT('c')});
   assert(s == CONVERT_TO_CSTRING(CharT, "123abc"));
+  LIBCPP_ASSERT(is_string_asan_correct(s));
 }
 
 TEST_CONSTEXPR_CXX20 bool test() {
@@ -40,6 +42,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
   test<std::u32string>();
 
   test<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
+  test<std::basic_string<char, std::char_traits<char>, safe_allocator<char>>>();
 #ifndef TEST_HAS_NO_NASTY_STRING
   test<nasty_string>();
 #endif
diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_append/iterator.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_append/iterator.pass.cpp
index 2982cfe77042c0d..684661aeab8fc3d 100644
--- a/libcxx/test/std/strings/basic.string/string.modifiers/string_append/iterator.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_append/iterator.pass.cpp
@@ -17,12 +17,14 @@
 #include "test_macros.h"
 #include "test_iterators.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S, class It>
 TEST_CONSTEXPR_CXX20 void test(S s, It first, It last, S expected) {
   s.append(first, last);
   LIBCPP_ASSERT(s.__invariants());
   assert(s == expected);
+  LIBCPP_ASSERT(is_string_asan_correct(s));
 }
 
 #ifndef TEST_HAS_NO_EXCEPTIONS
@@ -225,6 +227,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
   test_string<std::string>();
 #if TEST_STD_VER >= 11
   test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char> > >();
+  test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char> > >();
 #endif
 
   return true;
diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_append/pointer.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_append/pointer.pass.cpp
index 68f5838ccf2f02a..af334eb080c37ec 100644
--- a/libcxx/test/std/strings/basic.string/string.modifiers/string_append/pointer.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_append/pointer.pass.cpp
@@ -16,12 +16,14 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void test(S s, const typename S::value_type* str, S expected) {
   s.append(str);
   LIBCPP_ASSERT(s.__invariants());
   assert(s == expected);
+  LIBCPP_ASSERT(is_string_asan_correct(s));
 }
 
 template <class S>
@@ -43,6 +45,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
   test_string<std::string>();
 #if TEST_STD_VER >= 11
   test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
+  test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char>>>();
 #endif
 
   { // test appending to self
diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_append/pointer_size.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_append/pointer_size.pass.cpp
index b98c91cf097b1a0..93e7500a11967ce 100644
--- a/libcxx/test/std/strings/basic.string/string.modifiers/string_append/pointer_size.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_append/pointer_size.pass.cpp
@@ -17,12 +17,14 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void test(S s, const typename S::value_type* str, typename S::size_type n, S expected) {
   s.append(str, n);
   LIBCPP_ASSERT(s.__invariants());
   assert(s == expected);
+  LIBCPP_ASSERT(is_string_asan_correct(s));
 }
 
 template <class S>
@@ -48,6 +50,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
   test_string<std::string>();
 #if TEST_STD_VER >= 11
   test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
+  test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char>>>();
 #endif
 
   { // test appending to self
diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_append/push_back.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_append/push_back.pass.cpp
index 0b8e5f65c15e94d..30a38781bd47ebc 100644
--- a/libcxx/test/std/strings/basic.string/string.modifiers/string_append/push_back.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_append/push_back.pass.cpp
@@ -16,6 +16,7 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 struct VeryLarge {
   long long a;
@@ -61,6 +62,7 @@ TEST_CONSTEXPR_CXX20 void test(S s, typename S::value_type c, S expected) {
   s.push_back(c);
   LIBCPP_ASSERT(s.__invariants());
   assert(s == expected);
+  LIBCPP_ASSERT(is_string_asan_correct(s));
 }
 
 template <class S>
@@ -74,6 +76,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
   test_string<std::string>();
 #if TEST_STD_VER >= 11
   test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char> > >();
+  test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char> > >();
 #endif
   {
     // https://llvm.org/PR31454
diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_append/size_char.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_append/size_char.pass.cpp
index eb0728456df5be4..0d0318fc6955f1a 100644
--- a/libcxx/test/std/strings/basic.string/string.modifiers/string_append/size_char.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_append/size_char.pass.cpp
@@ -16,12 +16,14 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void test(S s, typename S::size_type n, typename S::value_type c, S expected) {
   s.append(n, c);
   LIBCPP_ASSERT(s.__invariants());
   assert(s == expected);
+  LIBCPP_ASSERT(is_string_asan_correct(s));
 }
 
 template <class S>
@@ -44,6 +46,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
   test_string<std::string>();
 #if TEST_STD_VER >= 11
   test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
+  test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char>>>();
 #endif
 
   return true;
diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_append/string.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_append/string.pass.cpp
index 21f2ff53004d158..79bdb0c04b32fad 100644
--- a/libcxx/test/std/strings/basic.string/string.modifiers/string_append/string.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_append/string.pass.cpp
@@ -16,12 +16,14 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void test(S s, S str, S expected) {
   s.append(str);
   LIBCPP_ASSERT(s.__invariants());
   assert(s == expected);
+  LIBCPP_ASSERT(is_string_asan_correct(s));
 }
 
 template <class S>
@@ -51,6 +53,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
   test_string<std::string>();
 #if TEST_STD_VER >= 11
   test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
+  test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char>>>();
 #endif
 
 #if TEST_STD_VER >= 11
diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_append/string_size_size.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_append/string_size_size.pass.cpp
index 902d29c53ac69f5..96ddc539170754d 100644
--- a/libcxx/test/std/strings/basic.string/string.modifiers/string_append/string_size_size.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_append/string_size_size.pass.cpp
@@ -18,6 +18,7 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void test(S s, S str, typename S::size_type pos, typename S::size_type n, S expected) {
@@ -25,6 +26,7 @@ TEST_CONSTEXPR_CXX20 void test(S s, S str, typename S::size_type pos, typename S
     s.append(str, pos, n);
     LIBCPP_ASSERT(s.__invariants());
     assert(s == expected);
+    LIBCPP_ASSERT(is_string_asan_correct(s));
   }
 #ifndef TEST_HAS_NO_EXCEPTIONS
   else if (!TEST_IS_CONSTANT_EVALUATED) {
@@ -85,6 +87,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
   test_string<std::string>();
 #if TEST_STD_VER >= 11
   test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
+  test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char>>>();
 #endif
 
   {
diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/T_size_size.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/T_size_size.pass.cpp
index a2a954cb94aac6a..b35f6b9497d03bc 100644
--- a/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/T_size_size.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/T_size_size.pass.cpp
@@ -17,6 +17,7 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S, class SV>
 TEST_CONSTEXPR_CXX20 void test(S s, SV sv, typename S::size_type pos, typename S::size_type n, S expected) {
@@ -24,6 +25,7 @@ TEST_CONSTEXPR_CXX20 void test(S s, SV sv, typename S::size_type pos, typename S
     s.assign(sv, pos, n);
     LIBCPP_ASSERT(s.__invariants());
     assert(s == expected);
+    LIBCPP_ASSERT(is_string_asan_correct(s));
   }
 #ifndef TEST_HAS_NO_EXCEPTIONS
   else if (!TEST_IS_CONSTANT_EVALUATED) {
@@ -85,6 +87,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
   test_string<std::string>();
 #if TEST_STD_VER >= 11
   test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
+  test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char>>>();
 #endif
 
   {
diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/initializer_list.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/initializer_list.pass.cpp
index ba55f30785e94f4..484ad723e7c7e21 100644
--- a/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/initializer_list.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/initializer_list.pass.cpp
@@ -17,18 +17,21 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void test_string() {
   S s("123");
   s.assign({'a', 'b', 'c'});
   assert(s == "abc");
+  LIBCPP_ASSERT(is_string_asan_correct(s));
 }
 
 TEST_CONSTEXPR_CXX20 bool test() {
   test_string<std::string>();
 #if TEST_STD_VER >= 11
   test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char> > >();
+  test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char> > >();
 #endif
 
   return true;
diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/iterator.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/iterator.pass.cpp
index b2ca4fb5ddd9370..c183def9ee75fee 100644
--- a/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/iterator.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/iterator.pass.cpp
@@ -17,12 +17,14 @@
 #include "test_macros.h"
 #include "test_iterators.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S, class It>
 TEST_CONSTEXPR_CXX20 void test(S s, It first, It last, S expected) {
   s.assign(first, last);
   LIBCPP_ASSERT(s.__invariants());
   assert(s == expected);
+  LIBCPP_ASSERT(is_string_asan_correct(s));
 }
 
 #ifndef TEST_HAS_NO_EXCEPTIONS
@@ -171,6 +173,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
   test_string<std::string>();
 #if TEST_STD_VER >= 11
   test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char> > >();
+  test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char> > >();
 #endif
 
   return true;
diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/pointer.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/pointer.pass.cpp
index 11e12c318a2e800..0619c91d59e25a7 100644
--- a/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/pointer.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/pointer.pass.cpp
@@ -16,12 +16,14 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void test(S s, const typename S::value_type* str, S expected) {
   s.assign(str);
   LIBCPP_ASSERT(s.__invariants());
   assert(s == expected);
+  LIBCPP_ASSERT(is_string_asan_correct(s));
 }
 
 template <class S>
@@ -43,6 +45,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
   test_string<std::string>();
 #if TEST_STD_VER >= 11
   test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
+  test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char>>>();
 #endif
 
   { // test assignment to self
diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/pointer_size.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/pointer_size.pass.cpp
index 2738a9a6ffad594..3fed2c7ee9ba6df 100644
--- a/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/pointer_size.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/pointer_size.pass.cpp
@@ -17,12 +17,14 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void test(S s, const typename S::value_type* str, typename S::size_type n, S expected) {
   s.assign(str, n);
   LIBCPP_ASSERT(s.__invariants());
   assert(s == expected);
+  LIBCPP_ASSERT(is_string_asan_correct(s));
 }
 
 template <class S>
@@ -48,6 +50,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
   test_string<std::string>();
 #if TEST_STD_VER >= 11
   test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
+  test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char>>>();
 #endif
 
   { // test assign to self
diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/size_char.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/size_char.pass.cpp
index b52c66103395f19..3c7651135f1aa11 100644
--- a/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/size_char.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/size_char.pass.cpp
@@ -16,12 +16,14 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void test(S s, typename S::size_type n, typename S::value_type c, S expected) {
   s.assign(n, c);
   LIBCPP_ASSERT(s.__invariants());
   assert(s == expected);
+  LIBCPP_ASSERT(is_string_asan_correct(s));
 }
 
 template <class S>
@@ -44,6 +46,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
   test_string<std::string>();
 #if TEST_STD_VER >= 11
   test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
+  test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char>>>();
 #endif
 
   return true;
diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/string.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/string.pass.cpp
index f39b7f66c4b187a..8b310630bf07ab8 100644
--- a/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/string.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/string.pass.cpp
@@ -18,12 +18,15 @@
 #include "nasty_string.h"
 #include "min_allocator.h"
 #include "test_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void test(S dest, S src) {
   dest.assign(src);
   LIBCPP_ASSERT(dest.__invariants());
   assert(dest == src);
+  LIBCPP_ASSERT(is_string_asan_correct(src));
+  LIBCPP_ASSERT(is_string_asan_correct(dest));
 }
 
 template <class S>
diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/string_size_size.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/string_size_size.pass.cpp
index 9448452bafad9c3..1cd2368b2a6b3f2 100644
--- a/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/string_size_size.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/string_size_size.pass.cpp
@@ -18,6 +18,7 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void test(S s, S str, typename S::size_type pos, typename S::size_type n, S expected) {
@@ -25,6 +26,7 @@ TEST_CONSTEXPR_CXX20 void test(S s, S str, typename S::size_type pos, typename S
     s.assign(str, pos, n);
     LIBCPP_ASSERT(s.__invariants());
     assert(s == expected);
+    LIBCPP_ASSERT(is_string_asan_correct(s));
   }
 #ifndef TEST_HAS_NO_EXCEPTIONS
   else if (!TEST_IS_CONSTANT_EVALUATED) {
@@ -85,6 +87,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
   test_string<std::string>();
 #if TEST_STD_VER >= 11
   test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
+  test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char>>>();
 #endif
 
   {
diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_copy/copy.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_copy/copy.pass.cpp
index e1904baeb9033b0..bf9eefb90c1ffec 100644
--- a/libcxx/test/std/strings/basic.string/string.modifiers/string_copy/copy.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_copy/copy.pass.cpp
@@ -17,6 +17,7 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void test(S str, typename S::value_type* s, typename S::size_type n, typename S::size_type pos) {
@@ -25,6 +26,9 @@ TEST_CONSTEXPR_CXX20 void test(S str, typename S::value_type* s, typename S::siz
     typename S::size_type r    = cs.copy(s, n, pos);
     typename S::size_type rlen = std::min(n, cs.size() - pos);
     assert(r == rlen);
+    LIBCPP_ASSERT(is_string_asan_correct(str));
+    LIBCPP_ASSERT(is_string_asan_correct(cs));
+
     for (r = 0; r < rlen; ++r)
       assert(S::traits_type::eq(cs[pos + r], s[r]));
   }
@@ -108,6 +112,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
   test_string<std::string>();
 #if TEST_STD_VER >= 11
   test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
+  test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char>>>();
 #endif
 
   return true;
diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_erase/iter.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_erase/iter.pass.cpp
index 03c5b965537c6fe..b724973150296b5 100644
--- a/libcxx/test/std/strings/basic.string/string.modifiers/string_erase/iter.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_erase/iter.pass.cpp
@@ -15,6 +15,7 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void test(S s, typename S::difference_type pos, S expected) {
@@ -24,6 +25,7 @@ TEST_CONSTEXPR_CXX20 void test(S s, typename S::difference_type pos, S expected)
   assert(s[s.size()] == typename S::value_type());
   assert(s == expected);
   assert(i - s.begin() == pos);
+  LIBCPP_ASSERT(is_string_asan_correct(s));
 }
 
 template <class S>
@@ -46,6 +48,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
   test_string<std::string>();
 #if TEST_STD_VER >= 11
   test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
+  test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char>>>();
 #endif
 
   return true;
diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_erase/iter_iter.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_erase/iter_iter.pass.cpp
index f092ad718cdaa89..5270335aa9ff3dd 100644
--- a/libcxx/test/std/strings/basic.string/string.modifiers/string_erase/iter_iter.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_erase/iter_iter.pass.cpp
@@ -15,6 +15,7 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void test(S s, typename S::difference_type pos, typename S::difference_type n, S expected) {
@@ -25,6 +26,7 @@ TEST_CONSTEXPR_CXX20 void test(S s, typename S::difference_type pos, typename S:
   assert(s[s.size()] == typename S::value_type());
   assert(s == expected);
   assert(i - s.begin() == pos);
+  LIBCPP_ASSERT(is_string_asan_correct(s));
 }
 
 template <class S>
@@ -89,6 +91,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
   test_string<std::string>();
 #if TEST_STD_VER >= 11
   test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
+  test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char>>>();
 #endif
 
   return true;
diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_erase/pop_back.asan.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_erase/pop_back.asan.pass.cpp
new file mode 100644
index 000000000000000..a115d6b1a5e154e
--- /dev/null
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_erase/pop_back.asan.pass.cpp
@@ -0,0 +1,50 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03
+
+// <string>
+
+#include <string>
+#include <cassert>
+
+#include "test_macros.h"
+#include "asan_testing.h"
+
+template <class CharT>
+void test(const CharT val) {
+  using S = std::basic_string<CharT>;
+
+  S s;
+  while (s.size() < 8000) {
+    s.push_back(val);
+
+    LIBCPP_ASSERT(is_string_asan_correct(s));
+  }
+  while (s.size() > 0) {
+    s.pop_back();
+
+    LIBCPP_ASSERT(is_string_asan_correct(s));
+  }
+}
+
+int main(int, char**) {
+  test<char>('x');
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  test<wchar_t>(L'x');
+#endif
+#if TEST_STD_VER >= 11
+  test<char16_t>(u'x');
+  test<char32_t>(U'x');
+#endif
+#if TEST_STD_VER >= 20
+  test<char8_t>(u8'x');
+#endif
+
+  return 0;
+}
diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_erase/pop_back.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_erase/pop_back.pass.cpp
index 9c63fa47a7e4a7f..7fc715f9d21daa6 100644
--- a/libcxx/test/std/strings/basic.string/string.modifiers/string_erase/pop_back.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_erase/pop_back.pass.cpp
@@ -15,6 +15,7 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void test(S s, S expected) {
@@ -22,6 +23,7 @@ TEST_CONSTEXPR_CXX20 void test(S s, S expected) {
   LIBCPP_ASSERT(s.__invariants());
   assert(s[s.size()] == typename S::value_type());
   assert(s == expected);
+  LIBCPP_ASSERT(is_string_asan_correct(s));
 }
 
 template <class S>
@@ -35,6 +37,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
   test_string<std::string>();
 #if TEST_STD_VER >= 11
   test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
+  test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char>>>();
 #endif
 
   return true;
diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_erase/size_size.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_erase/size_size.pass.cpp
index 610256061a5f2cd..97acffd484e646c 100644
--- a/libcxx/test/std/strings/basic.string/string.modifiers/string_erase/size_size.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_erase/size_size.pass.cpp
@@ -17,6 +17,7 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void test(S s, typename S::size_type pos, typename S::size_type n, S expected) {
@@ -27,6 +28,7 @@ TEST_CONSTEXPR_CXX20 void test(S s, typename S::size_type pos, typename S::size_
     LIBCPP_ASSERT(s.__invariants());
     assert(s[s.size()] == typename S::value_type());
     assert(s == expected);
+    LIBCPP_ASSERT(is_string_asan_correct(s));
   }
 #ifndef TEST_HAS_NO_EXCEPTIONS
   else if (!TEST_IS_CONSTANT_EVALUATED) {
@@ -180,6 +182,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
   test_string<std::string>();
 #if TEST_STD_VER >= 11
   test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
+  test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char>>>();
 #endif
 
   return true;
diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_insert/iter_char.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_insert/iter_char.pass.cpp
index bc3ce511ab5c221..f13e0ee37d2651a 100644
--- a/libcxx/test/std/strings/basic.string/string.modifiers/string_insert/iter_char.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_insert/iter_char.pass.cpp
@@ -16,9 +16,11 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void test(S& s, typename S::const_iterator p, typename S::value_type c, S expected) {
+  LIBCPP_ASSERT(is_string_asan_correct(s));
   bool sufficient_cap             = s.size() < s.capacity();
   typename S::difference_type pos = p - s.begin();
   typename S::iterator i          = s.insert(p, c);
@@ -28,6 +30,7 @@ TEST_CONSTEXPR_CXX20 void test(S& s, typename S::const_iterator p, typename S::v
   assert(*i == c);
   if (sufficient_cap)
     assert(i == p);
+  LIBCPP_ASSERT(is_string_asan_correct(s));
 }
 
 template <class S>
@@ -53,6 +56,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
   test_string<std::string>();
 #if TEST_STD_VER >= 11
   test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
+  test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char>>>();
 #endif
 
   return true;
diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_insert/iter_initializer_list.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_insert/iter_initializer_list.pass.cpp
index 876cc9ecd14c6a8..52594ab7ece5386 100644
--- a/libcxx/test/std/strings/basic.string/string.modifiers/string_insert/iter_initializer_list.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_insert/iter_initializer_list.pass.cpp
@@ -17,6 +17,7 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void test_string() {
@@ -24,11 +25,13 @@ TEST_CONSTEXPR_CXX20 void test_string() {
   typename S::iterator i = s.insert(s.begin() + 3, {'a', 'b', 'c'});
   assert(i - s.begin() == 3);
   assert(s == "123abc456");
+  LIBCPP_ASSERT(is_string_asan_correct(s));
 }
 
 TEST_CONSTEXPR_CXX20 bool test() {
   test_string<std::string>();
   test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char> > >();
+  test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char> > >();
 
   return true;
 }
diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_insert/iter_iter_iter.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_insert/iter_iter_iter.pass.cpp
index e3d0f1322e85de2..060216e7a82b306 100644
--- a/libcxx/test/std/strings/basic.string/string.modifiers/string_insert/iter_iter_iter.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_insert/iter_iter_iter.pass.cpp
@@ -17,6 +17,7 @@
 #include "test_macros.h"
 #include "test_iterators.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S, class It>
 TEST_CONSTEXPR_CXX20 void test(S s, typename S::difference_type pos, It first, It last, S expected) {
@@ -25,6 +26,7 @@ TEST_CONSTEXPR_CXX20 void test(S s, typename S::difference_type pos, It first, I
   LIBCPP_ASSERT(s.__invariants());
   assert(i - s.begin() == pos);
   assert(s == expected);
+  LIBCPP_ASSERT(is_string_asan_correct(s));
 }
 
 #ifndef TEST_HAS_NO_EXCEPTIONS
@@ -147,6 +149,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
   test_string<std::string>();
 #if TEST_STD_VER >= 11
   test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
+  test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
 #endif
 
 #ifndef TEST_HAS_NO_EXCEPTIONS
diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_insert/iter_size_char.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_insert/iter_size_char.pass.cpp
index 1e0d16e4cc2e172..6937ccc0d540a47 100644
--- a/libcxx/test/std/strings/basic.string/string.modifiers/string_insert/iter_size_char.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_insert/iter_size_char.pass.cpp
@@ -15,6 +15,7 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void
@@ -24,6 +25,7 @@ test(S s, typename S::difference_type pos, typename S::size_type n, typename S::
   LIBCPP_ASSERT(s.__invariants());
   assert(i - s.begin() == pos);
   assert(s == expected);
+  LIBCPP_ASSERT(is_string_asan_correct(s));
 }
 
 template <class S>
@@ -98,6 +100,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
   test_string<std::string>();
 #if TEST_STD_VER >= 11
   test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
+  test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char>>>();
 #endif
 
   return true;
diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_insert/size_pointer.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_insert/size_pointer.pass.cpp
index 359fd999598f889..9e2d00279b96d65 100644
--- a/libcxx/test/std/strings/basic.string/string.modifiers/string_insert/size_pointer.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_insert/size_pointer.pass.cpp
@@ -17,6 +17,7 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void test(S s, typename S::size_type pos, const typename S::value_type* str, S expected) {
@@ -26,6 +27,7 @@ TEST_CONSTEXPR_CXX20 void test(S s, typename S::size_type pos, const typename S:
     s.insert(pos, str);
     LIBCPP_ASSERT(s.__invariants());
     assert(s == expected);
+    LIBCPP_ASSERT(is_string_asan_correct(s));
   }
 #ifndef TEST_HAS_NO_EXCEPTIONS
   else if (!TEST_IS_CONSTANT_EVALUATED) {
@@ -128,6 +130,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
   test_string<std::string>();
 #if TEST_STD_VER >= 11
   test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
+  test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char>>>();
 #endif
 
   { // test inserting into self
diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_insert/size_pointer_size.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_insert/size_pointer_size.pass.cpp
index 44f20ff63cc0ca5..3e581b56a639019 100644
--- a/libcxx/test/std/strings/basic.string/string.modifiers/string_insert/size_pointer_size.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_insert/size_pointer_size.pass.cpp
@@ -17,6 +17,7 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void
@@ -27,6 +28,7 @@ test(S s, typename S::size_type pos, const typename S::value_type* str, typename
     s.insert(pos, str, n);
     LIBCPP_ASSERT(s.__invariants());
     assert(s == expected);
+    LIBCPP_ASSERT(is_string_asan_correct(s));
   }
 #ifndef TEST_HAS_NO_EXCEPTIONS
   else if (!TEST_IS_CONSTANT_EVALUATED) {
@@ -388,6 +390,7 @@ int main(int, char**) {
   test<std::string>();
 #if TEST_STD_VER >= 11
   test<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
+  test<std::basic_string<char, std::char_traits<char>, safe_allocator<char>>>();
 #endif
 
 #if TEST_STD_VER > 17
diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_insert/size_size_char.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_insert/size_size_char.pass.cpp
index 2396b7e2f54df73..f18ef484b5a0475 100644
--- a/libcxx/test/std/strings/basic.string/string.modifiers/string_insert/size_size_char.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_insert/size_size_char.pass.cpp
@@ -17,6 +17,7 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void
@@ -27,6 +28,7 @@ test(S s, typename S::size_type pos, typename S::size_type n, typename S::value_
     s.insert(pos, n, str);
     LIBCPP_ASSERT(s.__invariants());
     assert(s == expected);
+    LIBCPP_ASSERT(is_string_asan_correct(s));
   }
 #ifndef TEST_HAS_NO_EXCEPTIONS
   else if (!TEST_IS_CONSTANT_EVALUATED) {
@@ -129,6 +131,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
   test_string<std::string>();
 #if TEST_STD_VER >= 11
   test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
+  test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char>>>();
 #endif
 
   return true;
diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_insert/size_string.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_insert/size_string.pass.cpp
index 1b908ac464c3134..49870ba05e35726 100644
--- a/libcxx/test/std/strings/basic.string/string.modifiers/string_insert/size_string.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_insert/size_string.pass.cpp
@@ -17,6 +17,7 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void test(S s, typename S::size_type pos, S str, S expected) {
@@ -26,6 +27,7 @@ TEST_CONSTEXPR_CXX20 void test(S s, typename S::size_type pos, S str, S expected
     s.insert(pos, str);
     LIBCPP_ASSERT(s.__invariants());
     assert(s == expected);
+    LIBCPP_ASSERT(is_string_asan_correct(s));
   }
 #ifndef TEST_HAS_NO_EXCEPTIONS
   else if (!TEST_IS_CONSTANT_EVALUATED) {
@@ -128,6 +130,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
   test_string<std::string>();
 #if TEST_STD_VER >= 11
   test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
+  test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char>>>();
 #endif
 
 #if TEST_STD_VER >= 11
diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_insert/size_string_size_size.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_insert/size_string_size_size.pass.cpp
index 1a8f6b35b42c445..2c0579485d0fb54 100644
--- a/libcxx/test/std/strings/basic.string/string.modifiers/string_insert/size_string_size_size.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_insert/size_string_size_size.pass.cpp
@@ -19,6 +19,7 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void
@@ -29,6 +30,7 @@ test(S s, typename S::size_type pos1, S str, typename S::size_type pos2, typenam
     s.insert(pos1, str, pos2, n);
     LIBCPP_ASSERT(s.__invariants());
     assert(s == expected);
+    LIBCPP_ASSERT(is_string_asan_correct(s));
   }
 #ifndef TEST_HAS_NO_EXCEPTIONS
   else if (!TEST_IS_CONSTANT_EVALUATED) {
@@ -1816,6 +1818,7 @@ int main(int, char**) {
   test<std::string>();
 #if TEST_STD_VER >= 11
   test<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
+  test<std::basic_string<char, std::char_traits<char>, safe_allocator<char>>>();
 #endif
 
   return 0;
diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_op_plus_equal/char.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_op_plus_equal/char.pass.cpp
index 1e03d8c1b614741..a3a23d1ce7716d8 100644
--- a/libcxx/test/std/strings/basic.string/string.modifiers/string_op_plus_equal/char.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_op_plus_equal/char.pass.cpp
@@ -15,12 +15,14 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void test(S s, typename S::value_type str, S expected) {
   s += str;
   LIBCPP_ASSERT(s.__invariants());
   assert(s == expected);
+  LIBCPP_ASSERT(is_string_asan_correct(s));
 }
 
 template <class S>
@@ -35,6 +37,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
   test_string<std::string>();
 #if TEST_STD_VER >= 11
   test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char> > >();
+  test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char> > >();
 #endif
 
   return true;
diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_op_plus_equal/initializer_list.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_op_plus_equal/initializer_list.pass.cpp
index e7334b96276a5f8..6e631ab264b4756 100644
--- a/libcxx/test/std/strings/basic.string/string.modifiers/string_op_plus_equal/initializer_list.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_op_plus_equal/initializer_list.pass.cpp
@@ -17,17 +17,20 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void test_string() {
   S s("123");
   s += {'a', 'b', 'c'};
   assert(s == "123abc");
+  LIBCPP_ASSERT(is_string_asan_correct(s));
 }
 
 TEST_CONSTEXPR_CXX20 bool test() {
   test_string<std::string>();
   test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char> > >();
+  test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char> > >();
   return true;
 }
 
diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_op_plus_equal/pointer.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_op_plus_equal/pointer.pass.cpp
index 2d8c535b354c22f..32de8bfcc3156f1 100644
--- a/libcxx/test/std/strings/basic.string/string.modifiers/string_op_plus_equal/pointer.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_op_plus_equal/pointer.pass.cpp
@@ -15,12 +15,14 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void test(S s, const typename S::value_type* str, S expected) {
   s += str;
   LIBCPP_ASSERT(s.__invariants());
   assert(s == expected);
+  LIBCPP_ASSERT(is_string_asan_correct(s));
 }
 
 template <class S>
@@ -50,6 +52,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
   test_string<std::string>();
 #if TEST_STD_VER >= 11
   test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
+  test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char>>>();
 #endif
 
   return true;
diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_op_plus_equal/string.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_op_plus_equal/string.pass.cpp
index e02ced737ae16a2..20f0e257663e61a 100644
--- a/libcxx/test/std/strings/basic.string/string.modifiers/string_op_plus_equal/string.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_op_plus_equal/string.pass.cpp
@@ -16,12 +16,14 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void test(S s, S str, S expected) {
   s += str;
   LIBCPP_ASSERT(s.__invariants());
   assert(s == expected);
+  LIBCPP_ASSERT(is_string_asan_correct(s));
 }
 
 template <class S>
@@ -51,11 +53,13 @@ TEST_CONSTEXPR_CXX20 bool test() {
   test_string<std::string>();
 #if TEST_STD_VER >= 11
   test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char> > >();
+  test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char> > >();
   { // LWG 2946
     std::string s;
     s += {"abc", 1};
     assert(s.size() == 1);
     assert(s == "a");
+    LIBCPP_ASSERT(is_string_asan_correct(s));
   }
 #endif
 
diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_replace/iter_iter_string.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_replace/iter_iter_string.pass.cpp
index 35db1d636819169..eb0c62a66504ce6 100644
--- a/libcxx/test/std/strings/basic.string/string.modifiers/string_replace/iter_iter_string.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_replace/iter_iter_string.pass.cpp
@@ -17,6 +17,7 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void test(S s, typename S::size_type pos1, typename S::size_type n1, S str, S expected) {
@@ -29,6 +30,7 @@ TEST_CONSTEXPR_CXX20 void test(S s, typename S::size_type pos1, typename S::size
   assert(s == expected);
   typename S::size_type rlen = str.size();
   assert(s.size() == old_size - xlen + rlen);
+  LIBCPP_ASSERT(is_string_asan_correct(s));
 }
 
 template <class S>
@@ -292,6 +294,7 @@ int main(int, char**) {
   test<std::string>();
 #if TEST_STD_VER >= 11
   test<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
+  test<std::basic_string<char, std::char_traits<char>, safe_allocator<char>>>();
 #endif
 
   return 0;
diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_replace/size_size_T_size_size.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_replace/size_size_T_size_size.pass.cpp
index 004a51926285394..df08ae675a11e51 100644
--- a/libcxx/test/std/strings/basic.string/string.modifiers/string_replace/size_size_T_size_size.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_replace/size_size_T_size_size.pass.cpp
@@ -23,6 +23,7 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S, class SV>
 TEST_CONSTEXPR_CXX20 void
@@ -49,6 +50,7 @@ test(S s,
     SizeT xlen = std::min<SizeT>(n1, old_size - pos1);
     SizeT rlen = std::min<SizeT>(n2, sv.size() - pos2);
     assert(s.size() == old_size - xlen + rlen);
+    LIBCPP_ASSERT(is_string_asan_correct(s));
   }
 #ifndef TEST_HAS_NO_EXCEPTIONS
   else if (!TEST_IS_CONSTANT_EVALUATED) {
@@ -6279,6 +6281,7 @@ int main(int, char**) {
   test<char, std::allocator>();
 #if TEST_STD_VER >= 11
   test<char, min_allocator>();
+  test<char, safe_allocator>();
 #endif
 
   return 0;
diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_replace/size_size_pointer.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_replace/size_size_pointer.pass.cpp
index a7282615e485541..aefe3ced9765ef1 100644
--- a/libcxx/test/std/strings/basic.string/string.modifiers/string_replace/size_size_pointer.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_replace/size_size_pointer.pass.cpp
@@ -18,6 +18,7 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void
@@ -31,6 +32,7 @@ test(S s, typename S::size_type pos, typename S::size_type n1, const typename S:
     typename S::size_type xlen = std::min(n1, old_size - pos);
     typename S::size_type rlen = S::traits_type::length(str);
     assert(s.size() == old_size - xlen + rlen);
+    LIBCPP_ASSERT(is_string_asan_correct(s));
   }
 #ifndef TEST_HAS_NO_EXCEPTIONS
   else if (!TEST_IS_CONSTANT_EVALUATED) {
@@ -377,6 +379,7 @@ int main(int, char**) {
   test<std::string>();
 #if TEST_STD_VER >= 11
   test<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
+  test<std::basic_string<char, std::char_traits<char>, safe_allocator<char>>>();
 #endif
 
   return 0;
diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_replace/size_size_pointer_size.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_replace/size_size_pointer_size.pass.cpp
index 7ef63bdf313ddaf..cb01c34e43d4aa1 100644
--- a/libcxx/test/std/strings/basic.string/string.modifiers/string_replace/size_size_pointer_size.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_replace/size_size_pointer_size.pass.cpp
@@ -18,6 +18,7 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void
@@ -36,6 +37,7 @@ test(S s,
     typename S::size_type xlen = std::min(n1, old_size - pos);
     typename S::size_type rlen = n2;
     assert(s.size() == old_size - xlen + rlen);
+    LIBCPP_ASSERT(is_string_asan_correct(s));
   }
 #ifndef TEST_HAS_NO_EXCEPTIONS
   else if (!TEST_IS_CONSTANT_EVALUATED) {
@@ -1341,6 +1343,7 @@ int main(int, char**) {
   test<std::string>();
 #if TEST_STD_VER >= 11
   test<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
+  test<std::basic_string<char, std::char_traits<char>, safe_allocator<char>>>();
 #endif
 
   return 0;
diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_replace/size_size_size_char.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_replace/size_size_size_char.pass.cpp
index 8841318c1d4df5b..67e931679422503 100644
--- a/libcxx/test/std/strings/basic.string/string.modifiers/string_replace/size_size_size_char.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_replace/size_size_size_char.pass.cpp
@@ -18,6 +18,7 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void
@@ -36,6 +37,7 @@ test(S s,
     typename S::size_type xlen = std::min(n1, old_size - pos);
     typename S::size_type rlen = n2;
     assert(s.size() == old_size - xlen + rlen);
+    LIBCPP_ASSERT(is_string_asan_correct(s));
   }
 #ifndef TEST_HAS_NO_EXCEPTIONS
   else if (!TEST_IS_CONSTANT_EVALUATED) {
@@ -381,6 +383,7 @@ int main(int, char**) {
   test<std::string>();
 #if TEST_STD_VER >= 11
   test<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
+  test<std::basic_string<char, std::char_traits<char>, safe_allocator<char>>>();
 #endif
 
   return 0;
diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_replace/size_size_string.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_replace/size_size_string.pass.cpp
index fab2beeabd5721a..47fe7030c83b35e 100644
--- a/libcxx/test/std/strings/basic.string/string.modifiers/string_replace/size_size_string.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_replace/size_size_string.pass.cpp
@@ -18,6 +18,7 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void test(S s, typename S::size_type pos1, typename S::size_type n1, S str, S expected) {
@@ -30,6 +31,7 @@ TEST_CONSTEXPR_CXX20 void test(S s, typename S::size_type pos1, typename S::size
     typename S::size_type xlen = std::min(n1, old_size - pos1);
     typename S::size_type rlen = str.size();
     assert(s.size() == old_size - xlen + rlen);
+    LIBCPP_ASSERT(is_string_asan_correct(s));
   }
 #ifndef TEST_HAS_NO_EXCEPTIONS
   else if (!TEST_IS_CONSTANT_EVALUATED) {
@@ -385,6 +387,7 @@ int main(int, char**) {
   test<std::string>();
 #if TEST_STD_VER >= 11
   test<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
+  test<std::basic_string<char, std::char_traits<char>, safe_allocator<char>>>();
 #endif
   return 0;
 }
diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_replace/size_size_string_size_size.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_replace/size_size_string_size_size.pass.cpp
index 073de60289857ae..48645f628099095 100644
--- a/libcxx/test/std/strings/basic.string/string.modifiers/string_replace/size_size_string_size_size.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_replace/size_size_string_size_size.pass.cpp
@@ -22,6 +22,7 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void
@@ -41,6 +42,7 @@ test(S s,
     typename S::size_type xlen = std::min(n1, old_size - pos1);
     typename S::size_type rlen = std::min(n2, str.size() - pos2);
     assert(s.size() == old_size - xlen + rlen);
+    LIBCPP_ASSERT(is_string_asan_correct(s));
   }
 #ifndef TEST_HAS_NO_EXCEPTIONS
   else if (!TEST_IS_CONSTANT_EVALUATED) {
@@ -6099,6 +6101,7 @@ int main(int, char**) {
   test<std::string>();
 #if TEST_STD_VER >= 11
   test<std::basic_string<char, std::char_traits<char>, min_allocator<char> > >();
+  test<std::basic_string<char, std::char_traits<char>, safe_allocator<char> > >();
 #endif
 
   return 0;
diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_replace/size_size_string_view.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_replace/size_size_string_view.pass.cpp
index cec8cc0c52dd9f3..7f17160419065cb 100644
--- a/libcxx/test/std/strings/basic.string/string.modifiers/string_replace/size_size_string_view.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_replace/size_size_string_view.pass.cpp
@@ -18,6 +18,7 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S, class SV>
 TEST_CONSTEXPR_CXX20 void test(S s, typename S::size_type pos1, typename S::size_type n1, SV sv, S expected) {
@@ -30,6 +31,7 @@ TEST_CONSTEXPR_CXX20 void test(S s, typename S::size_type pos1, typename S::size
     typename S::size_type xlen = std::min(n1, old_size - pos1);
     typename S::size_type rlen = sv.size();
     assert(s.size() == old_size - xlen + rlen);
+    LIBCPP_ASSERT(is_string_asan_correct(s));
   }
 #ifndef TEST_HAS_NO_EXCEPTIONS
   else if (!TEST_IS_CONSTANT_EVALUATED) {
@@ -376,6 +378,7 @@ int main(int, char**) {
   test<char, std::allocator>();
 #if TEST_STD_VER >= 11
   test<char, min_allocator>();
+  test<char, safe_allocator>();
 #endif
 
   return 0;
diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_swap/swap.asan.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_swap/swap.asan.pass.cpp
new file mode 100644
index 000000000000000..ded4c56d34e4891
--- /dev/null
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_swap/swap.asan.pass.cpp
@@ -0,0 +1,87 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// <string>
+
+#include <string>
+#include <cassert>
+
+#include "test_macros.h"
+#include "asan_testing.h"
+
+template <class CharT>
+void test(const CharT val) {
+  using S = std::basic_string<CharT>;
+
+  S empty_s;
+  S short_s(3, val);
+  S long_s(1100, val);
+
+  std::swap(empty_s, empty_s);
+  std::swap(short_s, short_s);
+  std::swap(long_s, long_s);
+  LIBCPP_ASSERT(is_string_asan_correct(empty_s));
+  LIBCPP_ASSERT(is_string_asan_correct(short_s));
+  LIBCPP_ASSERT(is_string_asan_correct(long_s));
+
+  std::swap(empty_s, short_s);
+  LIBCPP_ASSERT(is_string_asan_correct(empty_s));
+  LIBCPP_ASSERT(is_string_asan_correct(short_s));
+
+  std::swap(empty_s, short_s);
+  LIBCPP_ASSERT(is_string_asan_correct(empty_s));
+  LIBCPP_ASSERT(is_string_asan_correct(short_s));
+
+  std::swap(empty_s, long_s);
+  LIBCPP_ASSERT(is_string_asan_correct(empty_s));
+  LIBCPP_ASSERT(is_string_asan_correct(long_s));
+
+  std::swap(empty_s, long_s);
+  LIBCPP_ASSERT(is_string_asan_correct(empty_s));
+  LIBCPP_ASSERT(is_string_asan_correct(long_s));
+
+  std::swap(short_s, long_s);
+  LIBCPP_ASSERT(is_string_asan_correct(short_s));
+  LIBCPP_ASSERT(is_string_asan_correct(long_s));
+
+  std::swap(short_s, long_s);
+  LIBCPP_ASSERT(is_string_asan_correct(short_s));
+  LIBCPP_ASSERT(is_string_asan_correct(long_s));
+
+  S long_s2(11100, val);
+
+  std::swap(long_s, long_s2);
+  LIBCPP_ASSERT(is_string_asan_correct(long_s));
+  LIBCPP_ASSERT(is_string_asan_correct(long_s2));
+
+  std::swap(long_s, long_s2);
+  LIBCPP_ASSERT(is_string_asan_correct(long_s));
+  LIBCPP_ASSERT(is_string_asan_correct(long_s2));
+
+  S long_s3(111, val);
+
+  std::swap(long_s, long_s3);
+  LIBCPP_ASSERT(is_string_asan_correct(long_s));
+  LIBCPP_ASSERT(is_string_asan_correct(long_s2));
+}
+
+int main(int, char**) {
+  test<char>('x');
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  test<wchar_t>(L'x');
+#endif
+#if TEST_STD_VER >= 11
+  test<char16_t>(u'x');
+  test<char32_t>(U'x');
+#endif
+#if TEST_STD_VER >= 20
+  test<char8_t>(u8'x');
+#endif
+
+  return 0;
+}
diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_swap/swap.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_swap/swap.pass.cpp
index 3d9ec016bcfbd3a..46f19bb51885d9d 100644
--- a/libcxx/test/std/strings/basic.string/string.modifiers/string_swap/swap.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_swap/swap.pass.cpp
@@ -17,6 +17,7 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void test(S s1, S s2) {
@@ -27,6 +28,8 @@ TEST_CONSTEXPR_CXX20 void test(S s1, S s2) {
   LIBCPP_ASSERT(s2.__invariants());
   assert(s1 == s2_);
   assert(s2 == s1_);
+  LIBCPP_ASSERT(is_string_asan_correct(s1));
+  LIBCPP_ASSERT(is_string_asan_correct(s2));
 }
 
 template <class S>
@@ -53,6 +56,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
   test_string<std::string>();
 #if TEST_STD_VER >= 11
   test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
+  test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char>>>();
 #endif
 
   return true;
diff --git a/libcxx/test/std/strings/basic.string/string.nonmembers/string.special/swap.pass.cpp b/libcxx/test/std/strings/basic.string/string.nonmembers/string.special/swap.pass.cpp
index da6fc11c14329f4..0d637df40662bd4 100644
--- a/libcxx/test/std/strings/basic.string/string.nonmembers/string.special/swap.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.nonmembers/string.special/swap.pass.cpp
@@ -19,6 +19,7 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void test(S s1, S s2) {
@@ -29,6 +30,8 @@ TEST_CONSTEXPR_CXX20 void test(S s1, S s2) {
   LIBCPP_ASSERT(s2.__invariants());
   assert(s1 == s2_);
   assert(s2 == s1_);
+  LIBCPP_ASSERT(is_string_asan_correct(s1));
+  LIBCPP_ASSERT(is_string_asan_correct(s2));
 }
 
 template <class S>
@@ -55,6 +58,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
   test_string<std::string>();
 #if TEST_STD_VER >= 11
   test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char> > >();
+  test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char> > >();
 #endif
 
   return true;
diff --git a/libcxx/test/std/strings/basic.string/string.nonmembers/string_op+/char_string.pass.cpp b/libcxx/test/std/strings/basic.string/string.nonmembers/string_op+/char_string.pass.cpp
index 82a591e0533628d..2ca5788e6d8a301 100644
--- a/libcxx/test/std/strings/basic.string/string.nonmembers/string_op+/char_string.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.nonmembers/string_op+/char_string.pass.cpp
@@ -22,10 +22,12 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void test0(typename S::value_type lhs, const S& rhs, const S& x) {
   assert(lhs + rhs == x);
+  LIBCPP_ASSERT(is_string_asan_correct(lhs + rhs));
 }
 
 #if TEST_STD_VER >= 11
@@ -53,6 +55,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
   test_string<std::string>();
 #if TEST_STD_VER >= 11
   test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char> > >();
+  test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char> > >();
 #endif
 
   return true;
diff --git a/libcxx/test/std/strings/basic.string/string.nonmembers/string_op+/string_char.pass.cpp b/libcxx/test/std/strings/basic.string/string.nonmembers/string_op+/string_char.pass.cpp
index 66cbac28f9f2c3a..9c0fb56e1f1c995 100644
--- a/libcxx/test/std/strings/basic.string/string.nonmembers/string_op+/string_char.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.nonmembers/string_op+/string_char.pass.cpp
@@ -22,10 +22,12 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void test0(const S& lhs, typename S::value_type rhs, const S& x) {
   assert(lhs + rhs == x);
+  LIBCPP_ASSERT(is_string_asan_correct(lhs + rhs));
 }
 
 #if TEST_STD_VER >= 11
@@ -55,6 +57,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
   test_string<std::string>();
 #if TEST_STD_VER >= 11
   test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char> > >();
+  test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char> > >();
 #endif
 
   return true;
diff --git a/libcxx/test/std/strings/basic.string/string.nonmembers/string_op+/string_pointer.pass.cpp b/libcxx/test/std/strings/basic.string/string.nonmembers/string_op+/string_pointer.pass.cpp
index 2802189f843c59c..3e1aaebf112eb5e 100644
--- a/libcxx/test/std/strings/basic.string/string.nonmembers/string_op+/string_pointer.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.nonmembers/string_op+/string_pointer.pass.cpp
@@ -22,10 +22,12 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void test0(const S& lhs, const typename S::value_type* rhs, const S& x) {
   assert(lhs + rhs == x);
+  LIBCPP_ASSERT(is_string_asan_correct(lhs + rhs));
 }
 
 #if TEST_STD_VER >= 11
@@ -77,6 +79,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
   test_string<std::string>();
 #if TEST_STD_VER >= 11
   test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char> > >();
+  test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char> > >();
 #endif
 
   return true;
diff --git a/libcxx/test/std/strings/basic.string/string.nonmembers/string_op+/string_string.pass.cpp b/libcxx/test/std/strings/basic.string/string.nonmembers/string_op+/string_string.pass.cpp
index f8ebb98767a8375..fcc8ceab870715c 100644
--- a/libcxx/test/std/strings/basic.string/string.nonmembers/string_op+/string_string.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.nonmembers/string_op+/string_string.pass.cpp
@@ -34,10 +34,12 @@
 
 #include "test_macros.h"
 #include "min_allocator.h"
+#include "asan_testing.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void test0(const S& lhs, const S& rhs, const S& x) {
   assert(lhs + rhs == x);
+  LIBCPP_ASSERT(is_string_asan_correct(lhs + rhs));
 }
 
 #if TEST_STD_VER >= 11
@@ -133,6 +135,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
   test_string<std::string>();
 #if TEST_STD_VER >= 11
   test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char> > >();
+  test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char> > >();
 #endif
 
   return true;
diff --git a/libcxx/test/support/asan_testing.h b/libcxx/test/support/asan_testing.h
index 5d044374044dea7..aaddff0879d6c98 100644
--- a/libcxx/test/support/asan_testing.h
+++ b/libcxx/test/support/asan_testing.h
@@ -11,26 +11,25 @@
 
 #include "test_macros.h"
 #include <vector>
+#include <string>
+#include <memory>
+#include <type_traits>
 
 #if TEST_HAS_FEATURE(address_sanitizer)
-extern "C" int __sanitizer_verify_contiguous_container
-     ( const void *beg, const void *mid, const void *end );
+extern "C" int __sanitizer_verify_contiguous_container(const void* beg, const void* mid, const void* end);
 
 template <typename T, typename Alloc>
-TEST_CONSTEXPR bool is_contiguous_container_asan_correct ( const std::vector<T, Alloc> &c )
-{
-    if (TEST_IS_CONSTANT_EVALUATED)
-        return true;
-    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;
+TEST_CONSTEXPR bool is_contiguous_container_asan_correct(const std::vector<T, Alloc>& c) {
+  if (TEST_IS_CONSTANT_EVALUATED)
     return true;
+  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;
 }
 #else
 template <typename T, typename Alloc>
-TEST_CONSTEXPR bool is_contiguous_container_asan_correct ( const std::vector<T, Alloc> &)
-{
-    return true;
+TEST_CONSTEXPR bool is_contiguous_container_asan_correct(const std::vector<T, Alloc>&) {
+  return true;
 }
 #endif // TEST_HAS_FEATURE(address_sanitizer)
 
@@ -56,4 +55,44 @@ TEST_CONSTEXPR bool is_double_ended_contiguous_container_asan_correct(const std:
 }
 #endif
 
+#if TEST_HAS_FEATURE(address_sanitizer)
+template <typename S>
+bool is_string_short(S const& s) {
+  // We do not have access to __is_long(), but we can check if strings
+  // buffer is inside strings memory. If strings memory contains its content,
+  // SSO is in use. To check it, we can just confirm that the beginning is in
+  // the string object memory block.
+  // &s    - beginning of objects memory
+  // &s[0] - beginning of the buffer
+  // (&s+1) - end of objects memory
+  return (void*)std::addressof(s) <= (void*)std::addressof(s[0]) &&
+         (void*)std::addressof(s[0]) < (void*)(std::addressof(s) + 1);
+}
+
+template <typename ChrT, typename TraitsT, typename Alloc>
+TEST_CONSTEXPR bool is_string_asan_correct(const std::basic_string<ChrT, TraitsT, Alloc>& c) {
+  if (TEST_IS_CONSTANT_EVALUATED)
+    return true;
+  if (c.data() != NULL) {
+    if (!is_string_short(c) || _LIBCPP_SHORT_STRING_ANNOTATIONS_ALLOWED) {
+      if (std::is_same<Alloc, std::allocator<ChrT>>::value)
+        return __sanitizer_verify_contiguous_container(
+                   c.data(), c.data() + c.size() + 1, c.data() + c.capacity() + 1) != 0;
+      else
+        return __sanitizer_verify_contiguous_container(
+                   c.data(), c.data() + c.capacity() + 1, c.data() + c.capacity() + 1) != 0;
+    } else {
+      return __sanitizer_verify_contiguous_container(std::addressof(c), std::addressof(c) + 1, std::addressof(c) + 1) !=
+             0;
+    }
+  }
+  return true;
+}
+#else
+#  include <string>
+template <typename ChrT, typename TraitsT, typename Alloc>
+TEST_CONSTEXPR bool is_string_asan_correct(const std::basic_string<ChrT, TraitsT, Alloc>&) {
+  return true;
+}
+#endif // TEST_HAS_FEATURE(address_sanitizer)
 #endif // ASAN_TESTING_H

>From 6da17753d4377af4c5d6b35ecedbe02f9f89bbba Mon Sep 17 00:00:00 2001
From: Advenam Tacet <advenam.tacet at trailofbits.com>
Date: Fri, 24 Nov 2023 07:08:54 +0400
Subject: [PATCH 2/3] Use [[maybe_unused]] instead of keeping two versions of
 one function

Described (on deque example) here:
https://github.com/llvm/llvm-project/issues/73043
---
 libcxx/include/string | 9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/string b/libcxx/include/string
index 6f6b2752960a506..399805586efd838 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -1892,18 +1892,15 @@ private:
         {return __is_long() ? __get_long_pointer() : __get_short_pointer();}
 
     // The following functions are no-ops outside of AddressSanitizer mode.
-#ifndef _LIBCPP_HAS_NO_ASAN
     _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __annotate_contiguous_container(
-        const void* __old_mid, const void* __new_mid) const {
+        [[maybe_unused]] const void* __old_mid, [[maybe_unused]] const void* __new_mid) const {
+#ifndef _LIBCPP_HAS_NO_ASAN
         const void* __begin = data();
         const void* __end = data() + capacity() + 1;
         if (!__libcpp_is_constant_evaluated() && __begin != nullptr && is_same<allocator_type, __default_allocator_type>::value)
           __sanitizer_annotate_contiguous_container(__begin, __end, __old_mid, __new_mid);
-    }
-#else
-    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
-    __annotate_contiguous_container(const void*, const void*) const {}
 #endif
+    }
 
     // ASan: short string is poisoned if and only if this function returns true.
     _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool __asan_short_string_is_annotated() const _NOEXCEPT {

>From 3e9c22b6dcd71e9f32547811eca999e94b6c7365 Mon Sep 17 00:00:00 2001
From: Advenam Tacet <advenam.tacet at trailofbits.com>
Date: Fri, 24 Nov 2023 08:08:05 +0400
Subject: [PATCH 3/3] Revert "Use [[maybe_unused]] instead of keeping two
 versions of one function"

This reverts commit 6da17753d4377af4c5d6b35ecedbe02f9f89bbba.

C++03 tests fail.
---
 libcxx/include/string | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/string b/libcxx/include/string
index 399805586efd838..6f6b2752960a506 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -1892,15 +1892,18 @@ private:
         {return __is_long() ? __get_long_pointer() : __get_short_pointer();}
 
     // The following functions are no-ops outside of AddressSanitizer mode.
-    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __annotate_contiguous_container(
-        [[maybe_unused]] const void* __old_mid, [[maybe_unused]] const void* __new_mid) const {
 #ifndef _LIBCPP_HAS_NO_ASAN
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __annotate_contiguous_container(
+        const void* __old_mid, const void* __new_mid) const {
         const void* __begin = data();
         const void* __end = data() + capacity() + 1;
         if (!__libcpp_is_constant_evaluated() && __begin != nullptr && is_same<allocator_type, __default_allocator_type>::value)
           __sanitizer_annotate_contiguous_container(__begin, __end, __old_mid, __new_mid);
-#endif
     }
+#else
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
+    __annotate_contiguous_container(const void*, const void*) const {}
+#endif
 
     // ASan: short string is poisoned if and only if this function returns true.
     _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool __asan_short_string_is_annotated() const _NOEXCEPT {



More information about the libcxx-commits mailing list