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.