[libcxx-commits] [libcxx] [libc++] Honor __SANITIZER_DISABLE_CONTAINER_OVERFLOW__ in libc++ (PR #168955)
Louis Dionne via libcxx-commits
libcxx-commits at lists.llvm.org
Wed Dec 10 09:28:35 PST 2025
https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/168955
>From 7a4cab0107055344d385f0d81e8c21ed9a1dee8b Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 20 Nov 2025 17:03:13 -0500
Subject: [PATCH 1/9] [libc++] Honor __SANITIZER_DISABLE_CONTAINER_OVERFLOW__
in libc++
Address sanitizer recently got a new macro __SANITIZER_DISABLE_CONTAINER_OVERFLOW__
which is intended to disable container overflow checks in libraries
(either the standard library or user libraries that might provide such
checks). This patch makes libc++ honor that macro and, in addition,
cleans up how these checks are enabled for string (which is special)
by introducing a macro just for string.
---
libcxx/include/__debug_utils/sanitizers.h | 19 ++++++++++----
libcxx/include/deque | 26 +++++++++----------
libcxx/include/string | 22 +++++++++-------
.../disable_container_overflow_checks.sh.cpp | 26 +++++++++++++++++++
.../is_trivially_relocatable.compile.pass.cpp | 2 +-
5 files changed, 67 insertions(+), 28 deletions(-)
create mode 100644 libcxx/test/extensions/libcxx/asan/disable_container_overflow_checks.sh.cpp
diff --git a/libcxx/include/__debug_utils/sanitizers.h b/libcxx/include/__debug_utils/sanitizers.h
index 058feab0269e1..3dc69fff38ffc 100644
--- a/libcxx/include/__debug_utils/sanitizers.h
+++ b/libcxx/include/__debug_utils/sanitizers.h
@@ -17,7 +17,16 @@
# pragma GCC system_header
#endif
-#if __has_feature(address_sanitizer)
+// _LIBCPP_ENABLE_ASAN_CONTAINER_CHECKS determines whether the containers should provide ASAN container
+// overflow checks. Some containers like std::string need stricter requirements in order to enable these
+// checks and also need to check that the library was built with sanitizer support (_LIBCPP_INSTRUMENTED_WITH_ASAN).
+#if __has_feature(address_sanitizer) && !defined(__SANITIZER_DISABLE_CONTAINER_OVERFLOW__)
+# define _LIBCPP_ENABLE_ASAN_CONTAINER_CHECKS 1
+#else
+# define _LIBCPP_ENABLE_ASAN_CONTAINER_CHECKS 0
+#endif
+
+#if _LIBCPP_ENABLE_ASAN_CONTAINER_CHECKS
extern "C" {
_LIBCPP_EXPORTED_FROM_ABI void
@@ -28,12 +37,12 @@ _LIBCPP_EXPORTED_FROM_ABI int
__sanitizer_verify_double_ended_contiguous_container(const void*, const void*, const void*, const void*);
}
-#endif // __has_feature(address_sanitizer)
+#endif // _LIBCPP_ENABLE_ASAN_CONTAINER_CHECKS
_LIBCPP_BEGIN_NAMESPACE_STD
// ASan choices
-#if __has_feature(address_sanitizer)
+#if _LIBCPP_ENABLE_ASAN_CONTAINER_CHECKS
# define _LIBCPP_HAS_ASAN_CONTAINER_ANNOTATIONS_FOR_ALL_ALLOCATORS 1
#endif
@@ -57,7 +66,7 @@ _LIBCPP_HIDE_FROM_ABI void __annotate_double_ended_contiguous_container(
const void* __last_old_contained,
const void* __first_new_contained,
const void* __last_new_contained) {
-#if !__has_feature(address_sanitizer)
+#if !_LIBCPP_ENABLE_ASAN_CONTAINER_CHECKS
(void)__first_storage;
(void)__last_storage;
(void)__first_old_contained;
@@ -86,7 +95,7 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __annotate_contiguous_c
const void* __last_storage,
const void* __old_last_contained,
const void* __new_last_contained) {
-#if !__has_feature(address_sanitizer)
+#if !_LIBCPP_ENABLE_ASAN_CONTAINER_CHECKS
(void)__first_storage;
(void)__last_storage;
(void)__old_last_contained;
diff --git a/libcxx/include/deque b/libcxx/include/deque
index 08bf8141eb782..649b3dc41efa6 100644
--- a/libcxx/include/deque
+++ b/libcxx/include/deque
@@ -932,7 +932,7 @@ private:
(void)__end;
(void)__annotation_type;
(void)__place;
-# if __has_feature(address_sanitizer)
+# if _LIBCPP_ENABLE_ASAN_CONTAINER_CHECKS
// __beg - index of the first item to annotate
// __end - index behind the last item to annotate (so last item + 1)
// __annotation_type - __asan_unposion or __asan_poison
@@ -1025,23 +1025,23 @@ private:
std::__annotate_double_ended_contiguous_container<_Allocator>(
__mem_beg, __mem_end, __old_beg, __old_end, __new_beg, __new_end);
}
-# endif // __has_feature(address_sanitizer)
+# endif // _LIBCPP_ENABLE_ASAN_CONTAINER_CHECKS
}
_LIBCPP_HIDE_FROM_ABI void __annotate_new(size_type __current_size) const _NOEXCEPT {
(void)__current_size;
-# if __has_feature(address_sanitizer)
+# if _LIBCPP_ENABLE_ASAN_CONTAINER_CHECKS
if (__current_size == 0)
__annotate_from_to(0, __map_.size() * __block_size, __asan_poison, __asan_back_moved);
else {
__annotate_from_to(0, __start_, __asan_poison, __asan_front_moved);
__annotate_from_to(__start_ + __current_size, __map_.size() * __block_size, __asan_poison, __asan_back_moved);
}
-# endif // __has_feature(address_sanitizer)
+# endif // _LIBCPP_ENABLE_ASAN_CONTAINER_CHECKS
}
_LIBCPP_HIDE_FROM_ABI void __annotate_delete() const _NOEXCEPT {
-# if __has_feature(address_sanitizer)
+# if _LIBCPP_ENABLE_ASAN_CONTAINER_CHECKS
if (empty()) {
for (size_t __i = 0; __i < __map_.size(); ++__i) {
__annotate_whole_block(__i, __asan_unposion);
@@ -1050,19 +1050,19 @@ private:
__annotate_from_to(0, __start_, __asan_unposion, __asan_front_moved);
__annotate_from_to(__start_ + size(), __map_.size() * __block_size, __asan_unposion, __asan_back_moved);
}
-# endif // __has_feature(address_sanitizer)
+# endif // _LIBCPP_ENABLE_ASAN_CONTAINER_CHECKS
}
_LIBCPP_HIDE_FROM_ABI void __annotate_increase_front(size_type __n) const _NOEXCEPT {
(void)__n;
-# if __has_feature(address_sanitizer)
+# if _LIBCPP_ENABLE_ASAN_CONTAINER_CHECKS
__annotate_from_to(__start_ - __n, __start_, __asan_unposion, __asan_front_moved);
# endif
}
_LIBCPP_HIDE_FROM_ABI void __annotate_increase_back(size_type __n) const _NOEXCEPT {
(void)__n;
-# if __has_feature(address_sanitizer)
+# if _LIBCPP_ENABLE_ASAN_CONTAINER_CHECKS
__annotate_from_to(__start_ + size(), __start_ + size() + __n, __asan_unposion, __asan_back_moved);
# endif
}
@@ -1070,7 +1070,7 @@ private:
_LIBCPP_HIDE_FROM_ABI void __annotate_shrink_front(size_type __old_size, size_type __old_start) const _NOEXCEPT {
(void)__old_size;
(void)__old_start;
-# if __has_feature(address_sanitizer)
+# if _LIBCPP_ENABLE_ASAN_CONTAINER_CHECKS
__annotate_from_to(__old_start, __old_start + (__old_size - size()), __asan_poison, __asan_front_moved);
# endif
}
@@ -1078,7 +1078,7 @@ private:
_LIBCPP_HIDE_FROM_ABI void __annotate_shrink_back(size_type __old_size, size_type __old_start) const _NOEXCEPT {
(void)__old_size;
(void)__old_start;
-# if __has_feature(address_sanitizer)
+# if _LIBCPP_ENABLE_ASAN_CONTAINER_CHECKS
__annotate_from_to(__old_start + size(), __old_start + __old_size, __asan_poison, __asan_back_moved);
# endif
}
@@ -1091,7 +1091,7 @@ private:
__annotate_whole_block(size_t __block_index, __asan_annotation_type __annotation_type) const _NOEXCEPT {
(void)__block_index;
(void)__annotation_type;
-# if __has_feature(address_sanitizer)
+# if _LIBCPP_ENABLE_ASAN_CONTAINER_CHECKS
__map_const_iterator __block_it = __map_.begin() + __block_index;
const void* __block_start = std::__to_address(*__block_it);
const void* __block_end = std::__to_address(*__block_it + __block_size);
@@ -1104,7 +1104,7 @@ private:
}
# endif
}
-# if __has_feature(address_sanitizer)
+# if _LIBCPP_ENABLE_ASAN_CONTAINER_CHECKS
public:
_LIBCPP_HIDE_FROM_ABI bool __verify_asan_annotations() const _NOEXCEPT {
@@ -1166,7 +1166,7 @@ public:
}
private:
-# endif // __has_feature(address_sanitizer)
+# endif // _LIBCPP_ENABLE_ASAN_CONTAINER_CHECKS
_LIBCPP_HIDE_FROM_ABI bool __maybe_remove_front_spare(bool __keep_one = true) {
if (__front_spare_blocks() >= 2 || (!__keep_one && __front_spare_blocks())) {
__annotate_whole_block(0, __asan_unposion);
diff --git a/libcxx/include/string b/libcxx/include/string
index c4806069d0b44..4848cb27dcb95 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -678,14 +678,18 @@ basic_string<char32_t> operator""s( const char32_t *str, size_t len );
_LIBCPP_PUSH_MACROS
# include <__undef_macros>
-# if __has_feature(address_sanitizer) && _LIBCPP_INSTRUMENTED_WITH_ASAN
+// Since std::string is partially instantiated in the built library, we require that the library
+// has been built with ASAN support in order to enable the container checks in std::string.
+//
+// The _LIBCPP_STRING_INTERNAL_MEMORY_ACCESS 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.
+# if _LIBCPP_ENABLE_ASAN_CONTAINER_CHECKS && _LIBCPP_INSTRUMENTED_WITH_ASAN
+# define _LIBCPP_ENABLE_ASAN_CONTAINER_CHECKS_FOR_STRING 1
# 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_ENABLE_ASAN_CONTAINER_CHECKS_FOR_STRING 0
# define _LIBCPP_STRING_INTERNAL_MEMORY_ACCESS
# endif
@@ -749,7 +753,7 @@ public:
//
// This string implementation doesn't contain any references into itself. It only contains a bit that says whether
// it is in small or large string mode, so the entire structure is trivially relocatable if its members are.
-# if __has_feature(address_sanitizer) && _LIBCPP_INSTRUMENTED_WITH_ASAN
+# if _LIBCPP_ENABLE_ASAN_CONTAINER_CHECKS_FOR_STRING
// When compiling with AddressSanitizer (ASan), basic_string cannot be trivially
// relocatable. Because the object's memory might be poisoned when its content
// is kept inside objects memory (short string optimization), instead of in allocated
@@ -764,7 +768,7 @@ public:
void>;
# endif
-# if __has_feature(address_sanitizer) && _LIBCPP_INSTRUMENTED_WITH_ASAN
+# if _LIBCPP_ENABLE_ASAN_CONTAINER_CHECKS_FOR_STRING
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 pointer __asan_volatile_wrapper(pointer const& __ptr) const {
if (__libcpp_is_constant_evaluated())
return __ptr;
@@ -2321,7 +2325,7 @@ private:
__annotate_contiguous_container(const void* __old_mid, const void* __new_mid) const {
(void)__old_mid;
(void)__new_mid;
-# if _LIBCPP_INSTRUMENTED_WITH_ASAN
+# if _LIBCPP_ENABLE_ASAN_CONTAINER_CHECKS_FOR_STRING
# if defined(__APPLE__)
// TODO: remove after addressing issue #96099 (https://llvm.org/PR96099)
if (!__is_long())
diff --git a/libcxx/test/extensions/libcxx/asan/disable_container_overflow_checks.sh.cpp b/libcxx/test/extensions/libcxx/asan/disable_container_overflow_checks.sh.cpp
new file mode 100644
index 0000000000000..231f8c3c86d61
--- /dev/null
+++ b/libcxx/test/extensions/libcxx/asan/disable_container_overflow_checks.sh.cpp
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME
+
+//
+// Check that libc++ honors when __SANITIZER_DISABLE_CONTAINER_OVERFLOW__ is set.
+//
+
+// RUN: %{cxx} %s %{flags} %{compile_flags} -D__SANITIZER_DISABLE_CONTAINER_OVERFLOW__
+// RUN: %{cxx} %s %{flags} %{compile_flags} -fsanitize=address -D__SANITIZER_DISABLE_CONTAINER_OVERFLOW__
+
+#include <vector>
+#include <string>
+#include <deque>
+
+#if _LIBCPP_ENABLE_ASAN_CONTAINER_CHECKS
+# error "Container overflow checks should be disabled in libc++"
+#endif
+
+int main(int, char**) {}
diff --git a/libcxx/test/libcxx/type_traits/is_trivially_relocatable.compile.pass.cpp b/libcxx/test/libcxx/type_traits/is_trivially_relocatable.compile.pass.cpp
index 10889eb50870d..33d405edcb7b7 100644
--- a/libcxx/test/libcxx/type_traits/is_trivially_relocatable.compile.pass.cpp
+++ b/libcxx/test/libcxx/type_traits/is_trivially_relocatable.compile.pass.cpp
@@ -103,7 +103,7 @@ static_assert(!std::__libcpp_is_trivially_relocatable<std::array<NotTriviallyCop
static_assert(std::__libcpp_is_trivially_relocatable<std::array<std::unique_ptr<int>, 1> >::value, "");
// basic_string
-#if !__has_feature(address_sanitizer) || !_LIBCPP_INSTRUMENTED_WITH_ASAN
+#if !_LIBCPP_ENABLE_ASAN_CONTAINER_CHECKS_FOR_STRING
struct MyChar {
char c;
};
>From 172c16aa085f81e9841154a2ec0422eb81976b89 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 4 Dec 2025 11:46:38 -0500
Subject: [PATCH 2/9] Improve test
---
...disable_container_overflow_checks.pass.cpp | 75 +++++++++++++++++++
.../disable_container_overflow_checks.sh.cpp | 26 -------
2 files changed, 75 insertions(+), 26 deletions(-)
create mode 100644 libcxx/test/extensions/libcxx/asan/disable_container_overflow_checks.pass.cpp
delete mode 100644 libcxx/test/extensions/libcxx/asan/disable_container_overflow_checks.sh.cpp
diff --git a/libcxx/test/extensions/libcxx/asan/disable_container_overflow_checks.pass.cpp b/libcxx/test/extensions/libcxx/asan/disable_container_overflow_checks.pass.cpp
new file mode 100644
index 0000000000000..8766f48875ee8
--- /dev/null
+++ b/libcxx/test/extensions/libcxx/asan/disable_container_overflow_checks.pass.cpp
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME
+// REQUIRES: asan
+
+// Check that libc++ honors when __SANITIZER_DISABLE_CONTAINER_OVERFLOW__ is set
+// and disables the container overflow checks.
+
+// ADDITIONAL_COMPILE_FLAGS: -D__SANITIZER_DISABLE_CONTAINER_OVERFLOW__
+
+#include <deque>
+#include <string>
+#include <vector>
+
+// This check is somewhat weak because it would pass if we renamed the libc++-internal
+// macro and forgot to update this test. But it doesn't hurt to check it in addition to
+// the tests below.
+#if _LIBCPP_ENABLE_ASAN_CONTAINER_CHECKS
+# error "Container overflow checks should be disabled in libc++"
+#endif
+
+void vector() {
+ std::vector<int> v;
+ v.reserve(100);
+ int* data = v.data();
+
+ // This is illegal with respect to std::vector, but legal from the core language perspective since
+ // we do own that allocated memory and `int` is an implicit lifetime type. If container overflow
+ // checks are enabled, this would fail.
+ data[4] = 42;
+}
+
+// For std::string, we must use a custom char_traits class to reliably test this behavior. Since
+// std::string is externally instantiated in the built library, __SANITIZER_DISABLE_CONTAINER_OVERFLOW__
+// will not be honored for any function that happens to be in the built library. Using a custom
+// char_traits class ensures that this doesn't get in the way.
+struct my_char_traits : std::char_traits<char> {};
+
+void string() {
+ std::basic_string<char, my_char_traits> s;
+ s.reserve(100);
+ char* data = s.data();
+ data[4] = 'x';
+}
+
+void deque() {
+ std::deque<int> d;
+ d.push_back(1);
+ d.push_back(2);
+ d.push_back(3);
+ int* last_element = &d[2];
+ d.pop_back();
+
+ // This reference is technically invalidated according to the library. However since
+ // we know std::deque is implemented using segments of a fairly large size and we know
+ // the non-erased elements are not invalidated by pop_front() (per the Standard), we can
+ // rely on the fact that the last element still exists in memory that is owned by the
+ // std::deque.
+ //
+ // If container overflow checks were enabled, this would obviously fail.
+ *last_element = 42;
+}
+
+int main(int, char**) {
+ vector();
+ string();
+ deque();
+ return 0;
+}
diff --git a/libcxx/test/extensions/libcxx/asan/disable_container_overflow_checks.sh.cpp b/libcxx/test/extensions/libcxx/asan/disable_container_overflow_checks.sh.cpp
deleted file mode 100644
index 231f8c3c86d61..0000000000000
--- a/libcxx/test/extensions/libcxx/asan/disable_container_overflow_checks.sh.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// XFAIL: FROZEN-CXX03-HEADERS-FIXME
-
-//
-// Check that libc++ honors when __SANITIZER_DISABLE_CONTAINER_OVERFLOW__ is set.
-//
-
-// RUN: %{cxx} %s %{flags} %{compile_flags} -D__SANITIZER_DISABLE_CONTAINER_OVERFLOW__
-// RUN: %{cxx} %s %{flags} %{compile_flags} -fsanitize=address -D__SANITIZER_DISABLE_CONTAINER_OVERFLOW__
-
-#include <vector>
-#include <string>
-#include <deque>
-
-#if _LIBCPP_ENABLE_ASAN_CONTAINER_CHECKS
-# error "Container overflow checks should be disabled in libc++"
-#endif
-
-int main(int, char**) {}
>From 5d91a59a320a31771e2e541ed1569df244744d0a Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 4 Dec 2025 11:51:34 -0500
Subject: [PATCH 3/9] Add comment about disablement not being honored in string
---
libcxx/include/string | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/libcxx/include/string b/libcxx/include/string
index b8f9654b8a18f..596ae64f8e74a 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -681,6 +681,10 @@ _LIBCPP_PUSH_MACROS
// Since std::string is partially instantiated in the built library, we require that the library
// has been built with ASAN support in order to enable the container checks in std::string.
//
+// This also means that if the built library has been compiled with sanitizer checks enabled
+// and the user requests for the container checks to be disabled, that request will not be
+// honored.
+//
// The _LIBCPP_STRING_INTERNAL_MEMORY_ACCESS 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,
>From 109e94013cde84658a1848ac2c1d7f68db789334 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 4 Dec 2025 17:03:23 -0500
Subject: [PATCH 4/9] Make it an error to disable container checks when libc++
is built with them
---
libcxx/include/__debug_utils/sanitizers.h | 22 +++++++++++++++---
...ner_overflow_checks_disablement.verify.cpp | 23 +++++++++++++++++++
...disable_container_overflow_checks.pass.cpp | 10 ++++++--
.../libcxx/test/features/libcxx_macros.py | 1 +
4 files changed, 51 insertions(+), 5 deletions(-)
create mode 100644 libcxx/test/extensions/libcxx/asan/diagnose_container_overflow_checks_disablement.verify.cpp
diff --git a/libcxx/include/__debug_utils/sanitizers.h b/libcxx/include/__debug_utils/sanitizers.h
index 3dc69fff38ffc..1549474f3f832 100644
--- a/libcxx/include/__debug_utils/sanitizers.h
+++ b/libcxx/include/__debug_utils/sanitizers.h
@@ -17,15 +17,31 @@
# pragma GCC system_header
#endif
-// _LIBCPP_ENABLE_ASAN_CONTAINER_CHECKS determines whether the containers should provide ASAN container
-// overflow checks. Some containers like std::string need stricter requirements in order to enable these
-// checks and also need to check that the library was built with sanitizer support (_LIBCPP_INSTRUMENTED_WITH_ASAN).
+// Within libc++, _LIBCPP_ENABLE_ASAN_CONTAINER_CHECKS determines whether the containers should
+// provide ASAN container overflow checks. That setting attempts to honour ASAN's documented option
+// __SANITIZER_DISABLE_CONTAINER_OVERFLOW__ which can be defined by users to disable container overflow
+// checks.
+//
+// However, since parts of some containers (e.g. std::string) are compiled separately into the built
+// library, there are caveats:
+// - __SANITIZER_DISABLE_CONTAINER_OVERFLOW__ can't always be honoured, i.e. if the built library
+// was compiled with ASAN container checks, it's impossible to turn them off afterwards. We diagnose
+// this with an error to avoid the proliferation of invalid configurations that appear to work.
+//
+// - The container overflow checks themselves are not always available even when the user is compiling
+// with -fsanitize=address. If a container is compiled separately like std::string, it can't provide
+// container checks unless the separately compiled code was built with container checks enabled. These
+// containers need to also conditionalize whether they provide overflow checks on `_LIBCPP_INSTRUMENTED_WITH_ASAN`.
#if __has_feature(address_sanitizer) && !defined(__SANITIZER_DISABLE_CONTAINER_OVERFLOW__)
# define _LIBCPP_ENABLE_ASAN_CONTAINER_CHECKS 1
#else
# define _LIBCPP_ENABLE_ASAN_CONTAINER_CHECKS 0
#endif
+#if _LIBCPP_INSTRUMENTED_WITH_ASAN && !_LIBCPP_ENABLE_ASAN_CONTAINER_CHECKS
+# error "We can't disable ASAN container checks when libc++ has been built with these checks enabled"
+#endif
+
#if _LIBCPP_ENABLE_ASAN_CONTAINER_CHECKS
extern "C" {
diff --git a/libcxx/test/extensions/libcxx/asan/diagnose_container_overflow_checks_disablement.verify.cpp b/libcxx/test/extensions/libcxx/asan/diagnose_container_overflow_checks_disablement.verify.cpp
new file mode 100644
index 0000000000000..04037cd6c0b6f
--- /dev/null
+++ b/libcxx/test/extensions/libcxx/asan/diagnose_container_overflow_checks_disablement.verify.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME
+
+// REQUIRES: libcpp-instrumented-with-asan
+
+// Check that we diagnose when libc++ has been built with ASAN instrumentation
+// and the user requests turning off the ASAN container checks. Since that is
+// impossible to implement, we diagnose this with an error instead.
+//
+// ADDITIONAL_COMPILE_FLAGS: -D__SANITIZER_DISABLE_CONTAINER_OVERFLOW__
+
+#include <deque>
+#include <string>
+#include <vector>
+
+// expected-error@*:* {{We can't disable ASAN container checks when libc++ has been built with these checks enabled}}
diff --git a/libcxx/test/extensions/libcxx/asan/disable_container_overflow_checks.pass.cpp b/libcxx/test/extensions/libcxx/asan/disable_container_overflow_checks.pass.cpp
index 8766f48875ee8..56f9f255765c4 100644
--- a/libcxx/test/extensions/libcxx/asan/disable_container_overflow_checks.pass.cpp
+++ b/libcxx/test/extensions/libcxx/asan/disable_container_overflow_checks.pass.cpp
@@ -7,12 +7,18 @@
//===----------------------------------------------------------------------===//
// XFAIL: FROZEN-CXX03-HEADERS-FIXME
-// REQUIRES: asan
// Check that libc++ honors when __SANITIZER_DISABLE_CONTAINER_OVERFLOW__ is set
// and disables the container overflow checks.
+//
+// ADDITIONAL_COMPILE_FLAGS: -fsanitize=address -D__SANITIZER_DISABLE_CONTAINER_OVERFLOW__
+
+// When libc++ is build with ASAN instrumentation, we can't turn off the ASAN checks,
+// and that is diagnosed as an error.
+// UNSUPPORTED: libcpp-instrumented-with-asan
-// ADDITIONAL_COMPILE_FLAGS: -D__SANITIZER_DISABLE_CONTAINER_OVERFLOW__
+// MSAN, TSAN and ASAN are mutually exclusive
+// UNSUPPORTED: msan, tsan
#include <deque>
#include <string>
diff --git a/libcxx/utils/libcxx/test/features/libcxx_macros.py b/libcxx/utils/libcxx/test/features/libcxx_macros.py
index 7a465f2e87866..5b291f9d3c4a4 100644
--- a/libcxx/utils/libcxx/test/features/libcxx_macros.py
+++ b/libcxx/utils/libcxx/test/features/libcxx_macros.py
@@ -44,6 +44,7 @@
true_false_macros = {
"_LIBCPP_HAS_THREAD_API_EXTERNAL": "libcpp-has-thread-api-external",
"_LIBCPP_HAS_THREAD_API_PTHREAD": "libcpp-has-thread-api-pthread",
+ "_LIBCPP_INSTRUMENTED_WITH_ASAN": "libcpp-instrumented-with-asan",
}
for macro, feature in true_false_macros.items():
features.append(
>From e87eb82d746928dd3a7edcf13da4f4285643715d Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 4 Dec 2025 17:07:25 -0500
Subject: [PATCH 5/9] Clarify and deduplicate string comment
---
libcxx/include/string | 12 +++++-------
1 file changed, 5 insertions(+), 7 deletions(-)
diff --git a/libcxx/include/string b/libcxx/include/string
index 596ae64f8e74a..f433a8c43e903 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -680,14 +680,12 @@ _LIBCPP_PUSH_MACROS
// Since std::string is partially instantiated in the built library, we require that the library
// has been built with ASAN support in order to enable the container checks in std::string.
+// Within the <string> implementation, use `_LIBCPP_ENABLE_ASAN_CONTAINER_CHECKS_FOR_STRING`
+// to determine whether ASAN container checks should be provided.
//
-// This also means that if the built library has been compiled with sanitizer checks enabled
-// and the user requests for the container checks to be disabled, that request will not be
-// honored.
-//
-// The _LIBCPP_STRING_INTERNAL_MEMORY_ACCESS 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,
+// The _LIBCPP_STRING_INTERNAL_MEMORY_ACCESS macro disables 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.
# if _LIBCPP_ENABLE_ASAN_CONTAINER_CHECKS && _LIBCPP_INSTRUMENTED_WITH_ASAN
# define _LIBCPP_ENABLE_ASAN_CONTAINER_CHECKS_FOR_STRING 1
>From ed6955878d306190e5434c195170a79ede58aca3 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 9 Dec 2025 16:38:52 -0500
Subject: [PATCH 6/9] Fix typo in comment
---
.../libcxx/asan/disable_container_overflow_checks.pass.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libcxx/test/extensions/libcxx/asan/disable_container_overflow_checks.pass.cpp b/libcxx/test/extensions/libcxx/asan/disable_container_overflow_checks.pass.cpp
index 56f9f255765c4..4d5e4e174eb87 100644
--- a/libcxx/test/extensions/libcxx/asan/disable_container_overflow_checks.pass.cpp
+++ b/libcxx/test/extensions/libcxx/asan/disable_container_overflow_checks.pass.cpp
@@ -13,7 +13,7 @@
//
// ADDITIONAL_COMPILE_FLAGS: -fsanitize=address -D__SANITIZER_DISABLE_CONTAINER_OVERFLOW__
-// When libc++ is build with ASAN instrumentation, we can't turn off the ASAN checks,
+// When libc++ is built with ASAN instrumentation, we can't turn off the ASAN checks,
// and that is diagnosed as an error.
// UNSUPPORTED: libcpp-instrumented-with-asan
>From b4a74c68c4334f312dc27d15e925e56946fae7a6 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 9 Dec 2025 16:41:55 -0500
Subject: [PATCH 7/9] Disable test in C++03 due to string::data()
---
.../libcxx/asan/disable_container_overflow_checks.pass.cpp | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/libcxx/test/extensions/libcxx/asan/disable_container_overflow_checks.pass.cpp b/libcxx/test/extensions/libcxx/asan/disable_container_overflow_checks.pass.cpp
index 4d5e4e174eb87..48fb1422bab45 100644
--- a/libcxx/test/extensions/libcxx/asan/disable_container_overflow_checks.pass.cpp
+++ b/libcxx/test/extensions/libcxx/asan/disable_container_overflow_checks.pass.cpp
@@ -6,8 +6,6 @@
//
//===----------------------------------------------------------------------===//
-// XFAIL: FROZEN-CXX03-HEADERS-FIXME
-
// Check that libc++ honors when __SANITIZER_DISABLE_CONTAINER_OVERFLOW__ is set
// and disables the container overflow checks.
//
@@ -20,6 +18,9 @@
// MSAN, TSAN and ASAN are mutually exclusive
// UNSUPPORTED: msan, tsan
+// std::basic_string::data is const in C++03
+// UNSUPPORTED: c++03
+
#include <deque>
#include <string>
#include <vector>
>From 252e21198f0390b70686c7343751f87acdd727e7 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 9 Dec 2025 16:44:25 -0500
Subject: [PATCH 8/9] Add UNSUPPORTED: gcc
---
.../libcxx/asan/disable_container_overflow_checks.pass.cpp | 3 +++
1 file changed, 3 insertions(+)
diff --git a/libcxx/test/extensions/libcxx/asan/disable_container_overflow_checks.pass.cpp b/libcxx/test/extensions/libcxx/asan/disable_container_overflow_checks.pass.cpp
index 48fb1422bab45..ac7c1fada17b0 100644
--- a/libcxx/test/extensions/libcxx/asan/disable_container_overflow_checks.pass.cpp
+++ b/libcxx/test/extensions/libcxx/asan/disable_container_overflow_checks.pass.cpp
@@ -21,6 +21,9 @@
// std::basic_string::data is const in C++03
// UNSUPPORTED: c++03
+// The protocol checked by this test is specific to Clang and compiler-rt
+// UNSUPPORTED: gcc
+
#include <deque>
#include <string>
#include <vector>
>From 51504054387f83eac5ec8bf023d81bc807c6280a Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Wed, 10 Dec 2025 12:28:19 -0500
Subject: [PATCH 9/9] UNSUPPORTED until C++17
---
.../libcxx/asan/disable_container_overflow_checks.pass.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/libcxx/test/extensions/libcxx/asan/disable_container_overflow_checks.pass.cpp b/libcxx/test/extensions/libcxx/asan/disable_container_overflow_checks.pass.cpp
index ac7c1fada17b0..67e3ed462b8f5 100644
--- a/libcxx/test/extensions/libcxx/asan/disable_container_overflow_checks.pass.cpp
+++ b/libcxx/test/extensions/libcxx/asan/disable_container_overflow_checks.pass.cpp
@@ -18,8 +18,8 @@
// MSAN, TSAN and ASAN are mutually exclusive
// UNSUPPORTED: msan, tsan
-// std::basic_string::data is const in C++03
-// UNSUPPORTED: c++03
+// std::basic_string::data is const util C++17
+// UNSUPPORTED: c++03, c++11, c++14
// The protocol checked by this test is specific to Clang and compiler-rt
// UNSUPPORTED: gcc
More information about the libcxx-commits
mailing list