diff --git a/compiler-rt/include/sanitizer/common_interface_defs.h b/compiler-rt/include/sanitizer/common_interface_defs.h
--- a/compiler-rt/include/sanitizer/common_interface_defs.h
+++ b/compiler-rt/include/sanitizer/common_interface_defs.h
@@ -159,6 +159,62 @@
const void *old_mid,
const void *new_mid);
+/// Similar to __sanitizer_annotate_contiguous_container(const void *beg,
+/// const void *end,
+/// const void *old_mid,
+/// const void *new_mid)
+///
+/// Annotates the current state of a contiguous container memory,
+/// such as std::deque's single chunk, when the beginning is moved.
+///
+/// A contiguous chunk is a chunk that keeps all of its elements
+/// in a contiguous region of memory. The container owns the region of memory
+/// [beg, end); the memory [con_beg, con_end) is used to store the
+/// current elements, and the memory [beg, con_beg), [con_end, end) is
+/// reserved for future elements (beg <= con_beg <= con_end <= end). For
+/// example, in std::deque :
+/// - chunk with a frist deques element will have con_beg equal to address
+/// of the first element.
+/// - in every next chunk with elements, true is con_beg == beg .
+///
+/// Argument requirements:
+/// During unpoisoning memory of empty container (before first element is
+/// added):
+/// - con_end_p == old_con_beg_p
+/// During poisoning after last element was removed:
+/// - new_con_beg_p == con_end_p
+void __sanitizer_annotate_de_contiguous_container_front(const void *beg,
+ const void *end,
+ const void *new_con_beg,
+ const void *old_con_beg,
+ const void *con_end);
+
+// Similar to __sanitizer_annotate_de_contiguous_container_front(const void
+// *beg,
+/// const void *end,
+/// const void *new_con_beg,
+/// const void *old_con_beg,
+/// const void *con_end)
+///
+/// Annotates the current state of a contiguous container memory,
+/// such as std::deque's single chunk, when the end is moved.
+///
+/// A contiguous chunk is a chunk that keeps all of its elements
+/// in a contiguous region of memory. The container owns the region of memory
+/// [beg, end); the memory [con_beg, con_end) is used to store the
+/// current elements, and the memory [beg, con_beg), [con_end, end) is
+/// reserved for future elements (beg <= con_beg <= con_end <= end).
+///
+/// Argument requirements:
+/// During unpoisoning memory of empty container (before first element is
+/// added):
+/// - con_beg_p == old_con_end_p
+/// During poisoning after last element was removed (empty container case):
+/// - new_con_end_p == con_beg_p
+void __sanitizer_annotate_de_contiguous_container_back(
+ const void *beg_p, const void *end_p, const void *old_con_end_p,
+ const void *new_con_end_p, const void *con_beg_p);
+
/// Returns true if the contiguous container [beg, end) is properly
/// poisoned.
///
@@ -178,6 +234,30 @@
int __sanitizer_verify_contiguous_container(const void *beg, const void *mid,
const void *end);
+/// Returns true if the double ended contiguous
+/// container [beg, end) is properly poisoned.
+///
+/// Proper poisoning could occur, for example, with
+/// __sanitizer_annotate_de_contiguous_container), that is, if
+/// [beg, con_beg) is not addressable, [con_beg, con_end) is
+/// addressable and [con_end, end) is unaddressable. Full verification
+/// requires O (end - beg) time; this function tries to avoid such
+/// complexity by touching only parts of the container around beg,
+/// con_beg, con_end, and end.
+///
+/// \param beg Beginning of memory region.
+/// \param con_beg Beginning of used region.
+/// \param con_end End of used region.
+/// \param end Old end of memory region.
+///
+/// \returns True if the double-ended contiguous container [beg, con_beg,
+/// con_end, end) is properly
+/// poisoned - only [con_beg; con_end) is addressable.
+int __sanitizer_verify_de_contiguous_container(const void *beg,
+ const void *con_beg,
+ const void *con_end,
+ const void *end);
+
/// Similar to __sanitizer_verify_contiguous_container() but also
/// returns the address of the first improperly poisoned byte.
///
diff --git a/compiler-rt/lib/asan/asan_errors.h b/compiler-rt/lib/asan/asan_errors.h
--- a/compiler-rt/lib/asan/asan_errors.h
+++ b/compiler-rt/lib/asan/asan_errors.h
@@ -331,6 +331,44 @@
void Print();
};
+struct ErrorBadParamsToAnnotateDeContiguousContainerFront : ErrorBase {
+ const BufferedStackTrace *stack;
+ uptr beg, end, new_con_beg, old_con_beg, con_end;
+
+ ErrorBadParamsToAnnotateDeContiguousContainerFront() = default; // (*)
+ ErrorBadParamsToAnnotateDeContiguousContainerFront(
+ u32 tid, BufferedStackTrace *stack_, uptr beg_, uptr end_,
+ uptr new_con_beg_, uptr old_con_beg_, uptr con_end_)
+ : ErrorBase(tid, 10,
+ "bad-__sanitizer_annotate_de_contiguous_container_front"),
+ stack(stack_),
+ beg(beg_),
+ end(end_),
+ new_con_beg(new_con_beg_),
+ old_con_beg(old_con_beg_),
+ con_end(con_end_) {}
+ void Print();
+};
+
+struct ErrorBadParamsToAnnotateDeContiguousContainerBack : ErrorBase {
+ const BufferedStackTrace *stack;
+ uptr beg, end, old_con_end, new_con_end, con_beg;
+
+ ErrorBadParamsToAnnotateDeContiguousContainerBack() = default; // (*)
+ ErrorBadParamsToAnnotateDeContiguousContainerBack(
+ u32 tid, BufferedStackTrace *stack_, uptr beg_, uptr end_,
+ uptr old_con_end_, uptr new_con_end_, uptr con_beg_)
+ : ErrorBase(tid, 10,
+ "bad-__sanitizer_annotate_de_contiguous_container_back"),
+ stack(stack_),
+ beg(beg_),
+ end(end_),
+ old_con_end(old_con_end_),
+ new_con_end(new_con_end_),
+ con_beg(con_beg_) {}
+ void Print();
+};
+
struct ErrorODRViolation : ErrorBase {
__asan_global global1, global2;
u32 stack_id1, stack_id2;
@@ -398,6 +436,8 @@
macro(StringFunctionMemoryRangesOverlap) \
macro(StringFunctionSizeOverflow) \
macro(BadParamsToAnnotateContiguousContainer) \
+ macro(BadParamsToAnnotateDeContiguousContainerFront) \
+ macro(BadParamsToAnnotateDeContiguousContainerBack) \
macro(ODRViolation) \
macro(InvalidPointerPair) \
macro(Generic)
diff --git a/compiler-rt/lib/asan/asan_errors.cpp b/compiler-rt/lib/asan/asan_errors.cpp
--- a/compiler-rt/lib/asan/asan_errors.cpp
+++ b/compiler-rt/lib/asan/asan_errors.cpp
@@ -334,6 +334,42 @@
ReportErrorSummary(scariness.GetDescription(), stack);
}
+void ErrorBadParamsToAnnotateDeContiguousContainerFront::Print() {
+ Report(
+ "ERROR: AddressSanitizer: bad parameters to "
+ "__sanitizer_annotate_de_contiguous_container_front:\n"
+ " beg : %p\n"
+ " end : %p\n"
+ " new_con_beg : %p\n"
+ " old_con_beg : %p\n"
+ " con_end : %p\n",
+ (void *)beg, (void *)end, (void *)new_con_beg, (void *)old_con_beg,
+ (void *)con_end);
+ uptr granularity = ASAN_SHADOW_GRANULARITY;
+ if (!IsAligned(beg, granularity))
+ Report("ERROR: beg is not aligned by %zu\n", granularity);
+ stack->Print();
+ ReportErrorSummary(scariness.GetDescription(), stack);
+}
+
+void ErrorBadParamsToAnnotateDeContiguousContainerBack::Print() {
+ Report(
+ "ERROR: AddressSanitizer: bad parameters to "
+ "__sanitizer_annotate_de_contiguous_container_back:\n"
+ " beg : %p\n"
+ " end : %p\n"
+ " old_con_end : %p\n"
+ " new_con_end : %p\n"
+ " con_beg : %p\n",
+ (void *)beg, (void *)end, (void *)old_con_end, (void *)new_con_end,
+ (void *)con_beg);
+ uptr granularity = ASAN_SHADOW_GRANULARITY;
+ if (!IsAligned(beg, granularity))
+ Report("ERROR: beg is not aligned by %zu\n", granularity);
+ stack->Print();
+ ReportErrorSummary(scariness.GetDescription(), stack);
+}
+
void ErrorODRViolation::Print() {
Decorator d;
Printf("%s", d.Error());
diff --git a/compiler-rt/lib/asan/asan_poisoning.cpp b/compiler-rt/lib/asan/asan_poisoning.cpp
--- a/compiler-rt/lib/asan/asan_poisoning.cpp
+++ b/compiler-rt/lib/asan/asan_poisoning.cpp
@@ -393,6 +393,148 @@
}
}
+// Annotates front for a double ended contiguous container like std::deque
+// It allows detecting buggy accesses to allocated but not used begining or end
+// items of such a container
+void __sanitizer_annotate_de_contiguous_container_front(
+ const void *beg_p, const void *end_p, const void *new_con_beg_p,
+ const void *old_con_beg_p, const void *con_end_p) {
+ // Unchecked argument requirements:
+ // During unpoisoning memory of empty container (before first element is
+ // added):
+ // - con_end_p == old_con_beg_p
+ // During poisoning after last element was removed:
+ // - new_con_beg_p == con_end_p
+ if (!flags()->detect_container_overflow)
+ return;
+ VPrintf(2, "de_contiguous_container (front): %p %p %p %p %p\n", beg_p, end_p,
+ new_con_beg_p, old_con_beg_p, con_end_p);
+ uptr beg = reinterpret_cast(beg_p);
+ uptr end = reinterpret_cast(end_p);
+ uptr new_con_beg =
+ reinterpret_cast(new_con_beg_p); // new container beginning
+ uptr old_con_beg =
+ reinterpret_cast(old_con_beg_p); // old container beginning
+ uptr con_end = reinterpret_cast(con_end_p); // container end
+
+ uptr granularity = ASAN_SHADOW_GRANULARITY;
+ if (!(beg <= new_con_beg && beg <= old_con_beg && new_con_beg <= end &&
+ old_con_beg <= end && con_end <= end && old_con_beg <= con_end &&
+ IsAligned(beg, granularity))) {
+ GET_STACK_TRACE_FATAL_HERE;
+ ReportBadParamsToAnnotateDeContiguousContainerFront(
+ beg, end, new_con_beg, old_con_beg, con_end, &stack);
+ }
+ CHECK_LE(end - beg,
+ FIRST_32_SECOND_64(1UL << 30, 1ULL << 40)); // Sanity check.
+
+ // There are two situations: we are poisoning or unpoisoning.
+
+ uptr a = RoundDownTo(Min(new_con_beg, old_con_beg), granularity);
+ uptr c = RoundDownTo(Max(new_con_beg, old_con_beg), granularity);
+ // WARNING: at the moment we do not poison prefixes of blocks described by one
+ // byte in shadow memory, so we have to unpoison prefixes of blocks with
+ // content. Up to 7 bytes not-in-use may not be poisoned.
+
+ if (new_con_beg < old_con_beg) { // We are unpoisoning
+ // State at the moment is:
+ // [beg, a] is poisoned and should remain like that.
+ // [a, c] is poisoned as well (may be empty if new_con_beg and old_con_beg
+ // are in the same block) if container is not empty, first element starts
+ // between [c, c+granularity]
+ // because we do not poison prefixes, memory [c, con_end] is not poisoned
+ // and we do not have to touch it.
+ // if container is empty, we have to unpoison memory for elements after c,
+ // so [c, con_end]
+ PoisonShadow(a, c - a, 0);
+ if (old_con_beg == con_end &&
+ c != RoundUpTo(
+ old_con_beg,
+ granularity)) // is empty && ends in the middle of a block
+ *(u8 *)MemToShadow(c) = static_cast(con_end - c);
+
+ // else: we cannot poison prefix of a block with elements or there is
+ // nothing to poison.
+ } else { // we are poisoning as beginning moved further in memory
+ // State at the moment is:
+ // [beg, a] is poisoned and should remain like that.
+ // [a, c] is not poisoned (may be empty if new_con_beg and old_con_beg are
+ // in the same block) [c, con_end] is not poisoned If there are remaining
+ // elements in the container:
+ // We have to poison [a, c], but because we do not poison prefixes, we
+ // cannot poison memory after c (even that there are not elements of the
+ // container). Up to granularity-1 unused bytes will not be poisoned.
+ // Otherwise:
+ // We have to poison the last byte as well.
+ PoisonShadow(a, c - a, kAsanContiguousContainerOOBMagic);
+ if (new_con_beg == con_end &&
+ c != RoundUpTo(
+ new_con_beg,
+ granularity)) // is empty && ends in the middle of a block
+ *(u8 *)MemToShadow(c) = static_cast(kAsanContiguousContainerOOBMagic);
+ }
+}
+
+// Annotates back for a double ended contiguous container like std::deque
+// It allows detecting buggy accesses to allocated but not used begining or end
+// items of such a container
+void __sanitizer_annotate_de_contiguous_container_back(
+ const void *beg_p, const void *end_p, const void *old_con_end_p,
+ const void *new_con_end_p, const void *con_beg_p) {
+ // Unchecked argument requirements:
+ // During unpoisoning memory of empty container (before first element is
+ // added):
+ // - con_beg_p == old_con_end_p
+ // During poisoning after last element was removed (empty container case):
+ // - new_con_end_p == con_beg_p
+ if (!flags()->detect_container_overflow)
+ return;
+ VPrintf(2, "contiguous_container: %p %p %p %p %p\n", beg_p, end_p,
+ old_con_end_p, new_con_end_p, con_beg_p);
+ uptr beg = reinterpret_cast(beg_p);
+ uptr end = reinterpret_cast(end_p);
+ uptr old_con_end = reinterpret_cast(old_con_end_p);
+ uptr new_con_end = reinterpret_cast(new_con_end_p);
+ uptr con_beg = reinterpret_cast(con_beg_p);
+ uptr granularity = ASAN_SHADOW_GRANULARITY;
+ if (!(beg <= old_con_end && beg <= new_con_end && old_con_end <= end &&
+ new_con_end <= end && IsAligned(beg, granularity))) {
+ GET_STACK_TRACE_FATAL_HERE;
+ ReportBadParamsToAnnotateDeContiguousContainerBack(
+ beg, end, old_con_end, new_con_end, con_beg, &stack);
+ }
+ CHECK_LE(end - beg,
+ FIRST_32_SECOND_64(1UL << 30, 1ULL << 40)); // Sanity check.
+
+ uptr a = RoundDownTo(Min(old_con_end, new_con_end), granularity);
+ uptr c = RoundDownTo(Max(old_con_end, new_con_end), granularity);
+ if (old_con_end < new_con_end) { // We are unpoisoning memory
+ // State at the moment is:
+ // if con_beg < a : [con_beg, a] is correct and we will not be changing it.
+ // else [a, con_beg] cannot be poisoned, so we do not have to think about
+ // it. we have to makr as unpoisoned [a, c]. [c, end] is correctly poisoned.
+ PoisonShadow(a, c - a, 0);
+ if (c !=
+ RoundUpTo(new_con_end, granularity)) // ends in the middle of a block
+ *(u8 *)MemToShadow(c) = static_cast(new_con_end - c);
+ } else { // We are poisoning memory
+ // State at the moment is:
+ // [beg, a] is correctly addressable
+ // if container is empty after the removal, then a < con_beg and we will
+ // have to poison memory which is adressable only because we are not
+ // poisoning prefixes.
+ uptr a2 = RoundUpTo(Min(old_con_end, new_con_end), granularity);
+ uptr c2 = RoundUpTo(Max(old_con_end, new_con_end), granularity);
+ PoisonShadow(a2, c2 - a2, kAsanContiguousContainerOOBMagic);
+ if (a != a2) { // Starts in the middle of the block
+ if (new_con_end == con_beg) // empty
+ *(u8 *)MemToShadow(a) = kAsanContiguousContainerOOBMagic;
+ else // not empty
+ *(u8 *)MemToShadow(a) = static_cast(new_con_end - a);
+ }
+ }
+}
+
const void *__sanitizer_contiguous_container_find_bad_address(
const void *beg_p, const void *mid_p, const void *end_p) {
if (!flags()->detect_container_overflow)
@@ -433,6 +575,57 @@
end_p) == nullptr;
}
+int __sanitizer_verify_de_contiguous_container(const void *beg_p,
+ const void *con_beg_p,
+ const void *con_end_p,
+ const void *end_p) {
+ uptr granularity = ASAN_SHADOW_GRANULARITY;
+ // This exists to verify double ended containers.
+ // We assume that such collection's internal memory layout
+ // consists of contiguous blocks:
+ // [a; b) [b; c) [c; d)
+ // where
+ // a - beginning address of contiguous memory block,
+ // b - beginning address of contiguous memory in use
+ // (address of the first element in the block)
+ // c - end address of contiguous memory in use
+ // (address just after the last element in the block)
+ // d - end address of contiguous memory block
+ // [a; b) - poisoned
+ // [b; c) - accessible
+ // [c; d) - poisoned
+ // WARNING: We can't poison [a; b) fully in all cases.
+ // This is because the current shadow memory encoding
+ // does not allow for marking/poisoning that a prefix
+ // of an 8-byte block (or, ASAN_SHADOW_GRANULARITY sized block)
+ // cannot be used by the instrumented program. It only has the
+ // 01, 02, 03, 04, 05, 06, 07 and 00 encodings
+ // for usable/addressable memory
+ // (where 00 means that the whole 8-byte block can be used).
+ //
+ // This means that there are cases where not whole of the [a; b)
+ // region is poisoned and instead only the [a; RoundDown(b))
+ // region is poisoned and we may not detect invalid memory accesses on
+ // [RegionDown(b), b).
+ // This is an inherent design limitation of how AddressSanitizer granularity
+ // and shadow memory encoding works at the moment.
+
+ // If empty, beg_p == con_beg_p == con_end_p
+
+ const void *a = beg_p;
+ // We do not suport poisoning prefixes of blocks, so
+ // memory in the first block with data in us,
+ // just before container beginning cannot be poisoned, as described above.
+ const void *b = reinterpret_cast(
+ RoundDownTo(reinterpret_cast(con_beg_p), granularity));
+ const void *c = con_end_p;
+ const void *d = end_p;
+ return (__sanitizer_contiguous_container_find_bad_address(a, a, b) ==
+ nullptr) &&
+ (__sanitizer_contiguous_container_find_bad_address(b, c, d) ==
+ nullptr);
+}
+
extern "C" SANITIZER_INTERFACE_ATTRIBUTE
void __asan_poison_intra_object_redzone(uptr ptr, uptr size) {
AsanPoisonOrUnpoisonIntraObjectRedzone(ptr, size, true);
diff --git a/compiler-rt/lib/asan/asan_report.h b/compiler-rt/lib/asan/asan_report.h
--- a/compiler-rt/lib/asan/asan_report.h
+++ b/compiler-rt/lib/asan/asan_report.h
@@ -83,6 +83,12 @@
void ReportBadParamsToAnnotateContiguousContainer(uptr beg, uptr end,
uptr old_mid, uptr new_mid,
BufferedStackTrace *stack);
+void ReportBadParamsToAnnotateDeContiguousContainerFront(
+ uptr beg, uptr end, uptr new_con_beg, uptr old_con_beg, uptr con_end,
+ BufferedStackTrace *stack);
+void ReportBadParamsToAnnotateDeContiguousContainerBack(
+ uptr beg, uptr end, uptr old_con_end, uptr new_con_end, uptr con_beg,
+ BufferedStackTrace *stack);
void ReportODRViolation(const __asan_global *g1, u32 stack_id1,
const __asan_global *g2, u32 stack_id2);
diff --git a/compiler-rt/lib/asan/asan_report.cpp b/compiler-rt/lib/asan/asan_report.cpp
--- a/compiler-rt/lib/asan/asan_report.cpp
+++ b/compiler-rt/lib/asan/asan_report.cpp
@@ -354,6 +354,26 @@
in_report.ReportError(error);
}
+void ReportBadParamsToAnnotateDeContiguousContainerFront(
+ uptr beg, uptr end, uptr new_con_beg, uptr old_con_beg, uptr con_end,
+ BufferedStackTrace *stack) {
+ ScopedInErrorReport in_report;
+ ErrorBadParamsToAnnotateDeContiguousContainerFront error(
+ GetCurrentTidOrInvalid(), stack, beg, end, new_con_beg, old_con_beg,
+ con_end);
+ in_report.ReportError(error);
+}
+
+void ReportBadParamsToAnnotateDeContiguousContainerBack(
+ uptr beg, uptr end, uptr old_con_end, uptr new_con_end, uptr con_beg,
+ BufferedStackTrace *stack) {
+ ScopedInErrorReport in_report;
+ ErrorBadParamsToAnnotateDeContiguousContainerBack error(
+ GetCurrentTidOrInvalid(), stack, beg, end, old_con_end, new_con_end,
+ con_beg);
+ in_report.ReportError(error);
+}
+
void ReportODRViolation(const __asan_global *g1, u32 stack_id1,
const __asan_global *g2, u32 stack_id2) {
ScopedInErrorReport in_report;
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_common_interface.inc b/compiler-rt/lib/sanitizer_common/sanitizer_common_interface.inc
--- a/compiler-rt/lib/sanitizer_common/sanitizer_common_interface.inc
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_common_interface.inc
@@ -9,12 +9,15 @@
//===----------------------------------------------------------------------===//
INTERFACE_FUNCTION(__sanitizer_acquire_crash_state)
INTERFACE_FUNCTION(__sanitizer_annotate_contiguous_container)
+INTERFACE_FUNCTION(__sanitizer_annotate_de_contiguous_container_front)
+INTERFACE_FUNCTION(__sanitizer_annotate_de_contiguous_container_back)
INTERFACE_FUNCTION(__sanitizer_contiguous_container_find_bad_address)
INTERFACE_FUNCTION(__sanitizer_set_death_callback)
INTERFACE_FUNCTION(__sanitizer_set_report_path)
INTERFACE_FUNCTION(__sanitizer_set_report_fd)
INTERFACE_FUNCTION(__sanitizer_get_report_path)
INTERFACE_FUNCTION(__sanitizer_verify_contiguous_container)
+INTERFACE_FUNCTION(__sanitizer_verify_de_contiguous_container)
INTERFACE_WEAK_FUNCTION(__sanitizer_on_print)
INTERFACE_WEAK_FUNCTION(__sanitizer_report_error_summary)
INTERFACE_WEAK_FUNCTION(__sanitizer_sandbox_on_notify)
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_interface_internal.h b/compiler-rt/lib/sanitizer_common/sanitizer_interface_internal.h
--- a/compiler-rt/lib/sanitizer_common/sanitizer_interface_internal.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_interface_internal.h
@@ -72,11 +72,24 @@
const void *__sanitizer_contiguous_container_find_bad_address(const void *beg,
const void *mid,
const void *end);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_annotate_de_contiguous_container_front(
+ const void *beg, const void *end, const void *new_con_beg_p,
+ const void *old_con_beg_p, const void *con_end_p);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_annotate_de_contiguous_container_back(
+ const void *beg, const void *end, const void *old_con_end_p,
+ const void *new_con_end_p, const void *con_beg_p);
SANITIZER_INTERFACE_ATTRIBUTE
int __sanitizer_get_module_and_offset_for_pc(void *pc, char *module_path,
__sanitizer::uptr module_path_len,
void **pc_offset);
+SANITIZER_INTERFACE_ATTRIBUTE
+int __sanitizer_verify_de_contiguous_container(const void *beg,
+ const void *con_beg,
+ const void *con_end,
+ const void *end);
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void
__sanitizer_cov_trace_cmp();
diff --git a/libcxx/include/__config b/libcxx/include/__config
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -880,6 +880,12 @@
# ifndef _LIBCPP_HAS_NO_ASAN
extern "C" _LIBCPP_FUNC_VIS void
__sanitizer_annotate_contiguous_container(const void*, const void*, const void*, const void*);
+ extern "C" _LIBCPP_FUNC_VIS void
+ __sanitizer_annotate_de_contiguous_container_front(const void *, const void *, const void *, const void *, const void *);
+ extern "C" _LIBCPP_FUNC_VIS void
+ __sanitizer_annotate_de_contiguous_container_back(const void *, const void *, const void *, const void *, const void *);
+ extern "C" _LIBCPP_FUNC_VIS int
+ __sanitizer_verify_de_contiguous_container(const void *, const void *, const void *, const void *);
# endif
// Try to find out if RTTI is disabled.