diff --git a/libcxx/include/vector b/libcxx/include/vector --- a/libcxx/include/vector +++ b/libcxx/include/vector @@ -300,6 +300,7 @@ #include <__iterator/reverse_iterator.h> #include <__iterator/wrap_iter.h> #include <__memory/allocate_at_least.h> +#include <__memory/allocator_traits.h> #include <__memory/pointer_traits.h> #include <__memory/swap_allocator.h> #include <__memory/temp_value.h> @@ -439,11 +440,11 @@ _LIBCPP_CONSTEXPR __destroy_vector(vector& __vec) : __vec_(__vec) {} _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void operator()() { - __vec_.__annotate_delete(); std::__debug_db_erase_c(std::addressof(__vec_)); if (__vec_.__begin_ != nullptr) { __vec_.__clear(); + __vec_.__annotate_delete(); __alloc_traits::deallocate(__vec_.__alloc(), __vec_.__begin_, __vec_.capacity()); } } @@ -743,8 +744,25 @@ const void *__old_mid, const void *__new_mid) const { - - if (!__libcpp_is_constant_evaluated() && __beg && is_same::value) +# if _LIBCPP_CLANG_VER >= 1600 + if (!__libcpp_is_constant_evaluated() && __beg != nullptr && __asan_annotate_container_with_allocator<_Allocator>::value) + // Implementation of __sanitizer_annotate_contiguous_container function changed with commit + // rGdd1b7b797a116eed588fd752fbe61d34deeb24e4 and supports: + // - unaligned beginnings of buffers, + // - shared first/last granule (if memory granule is shared with a different object + // just after the end of unaligned end/before the unaligned beginning, memory of that object won't be poisoned). + // + // Therefore, check for standard allocator is not necessary. +# else + // TODO LLVM18: Remove the special-casing + // + // Vectors annotations rely on __sanitizer_annotate_contiguous_container function, + // its implementation from LLVM15 (and earlier) requires that + // beginning of a containers data buffer is aligned to shadow granularity and + // memory just after is not shared with another object. + // Default allocator satisfies that. + if (!__libcpp_is_constant_evaluated() && __beg != nullptr && is_same::value) +# endif __sanitizer_annotate_contiguous_container(__beg, __end, __old_mid, __new_mid); } #else @@ -866,6 +884,7 @@ if (__alloc() != __c.__alloc()) { __clear(); + __annotate_delete(); __alloc_traits::deallocate(__alloc(), this->__begin_, capacity()); this->__begin_ = this->__end_ = __end_cap() = nullptr; } @@ -954,6 +973,7 @@ if (this->__begin_ != nullptr) { clear(); + __annotate_delete(); __alloc_traits::deallocate(this->__alloc(), this->__begin_, capacity()); this->__begin_ = this->__end_ = this->__end_cap() = nullptr; } diff --git a/libcxx/test/libcxx/containers/sequences/vector/asan.pass.cpp b/libcxx/test/libcxx/containers/sequences/vector/asan.pass.cpp --- a/libcxx/test/libcxx/containers/sequences/vector/asan.pass.cpp +++ b/libcxx/test/libcxx/containers/sequences/vector/asan.pass.cpp @@ -29,40 +29,65 @@ int main(int, char**) { -#if TEST_STD_VER >= 11 - { - typedef int T; - typedef std::vector> C; - const T t[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; - C c(std::begin(t), std::end(t)); - c.reserve(2*c.size()); - volatile T foo = c[c.size()]; // bad, but not caught by ASAN - ((void)foo); - } +#if TEST_STD_VER >= 11 && TEST_CLANG_VER < 1600 + // TODO LLVM18: Remove the special-test + { + typedef int T; + typedef std::vector> C; + const T t[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + C c(std::begin(t), std::end(t)); + c.reserve(2 * c.size()); + volatile T foo = c[c.size()]; // bad, but not caught by ASAN + ((void)foo); + } #endif - { - typedef cpp17_input_iterator MyInputIter; - // Sould not trigger ASan. - std::vector v; - v.reserve(1); - int i[] = {42}; - v.insert(v.begin(), MyInputIter(i), MyInputIter(i + 1)); - assert(v[0] == 42); - assert(is_contiguous_container_asan_correct(v)); - } +#if TEST_STD_VER >= 11 && TEST_CLANG_VER >= 1600 + // TODO LLVM18: Remove the special-casing + { + typedef int T; + typedef cpp17_input_iterator MyInputIter; + std::vector> v; + v.reserve(1); + int i[] = {42}; + v.insert(v.begin(), MyInputIter(i), MyInputIter(i + 1)); + assert(v[0] == 42); + assert(is_contiguous_container_asan_correct(v)); + } + { + typedef char T; + typedef cpp17_input_iterator MyInputIter; + std::vector> v; + v.reserve(1); + char i[] = {'a', 'b'}; + v.insert(v.begin(), MyInputIter(i), MyInputIter(i + 2)); + assert(v[0] == 'a'); + assert(v[1] == 'b'); + assert(is_contiguous_container_asan_correct(v)); + } +#endif + { + typedef cpp17_input_iterator MyInputIter; + // Sould not trigger ASan. + std::vector v; + v.reserve(1); + int i[] = {42}; + v.insert(v.begin(), MyInputIter(i), MyInputIter(i + 1)); + assert(v[0] == 42); + assert(is_contiguous_container_asan_correct(v)); + } - __sanitizer_set_death_callback(do_exit); - { - typedef int T; - typedef std::vector C; - const T t[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; - C c(std::begin(t), std::end(t)); - c.reserve(2*c.size()); - assert(is_contiguous_container_asan_correct(c)); - assert(!__sanitizer_verify_contiguous_container( c.data(), c.data() + 1, c.data() + c.capacity())); - volatile T foo = c[c.size()]; // should trigger ASAN. Use volatile to prevent being optimized away. - assert(false); // if we got here, ASAN didn't trigger - ((void)foo); - } + __sanitizer_set_death_callback(do_exit); + { + typedef int T; + typedef std::vector C; + const T t[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + C c(std::begin(t), std::end(t)); + c.reserve(2 * c.size()); + assert(is_contiguous_container_asan_correct(c)); + assert(!__sanitizer_verify_contiguous_container(c.data(), c.data() + 1, c.data() + c.capacity())); + volatile T foo = c[c.size()]; // should trigger ASAN. Use volatile to prevent being optimized away. + assert(false); // if we got here, ASAN didn't trigger + ((void)foo); + } } diff --git a/libcxx/test/libcxx/containers/sequences/vector/no_asan.pass.cpp b/libcxx/test/libcxx/containers/sequences/vector/no_asan.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/containers/sequences/vector/no_asan.pass.cpp @@ -0,0 +1,73 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// + +// Test based on: https://bugs.chromium.org/p/chromium/issues/detail?id=1419798#c5 + +#include +#include +#include +#include + +struct reuse_allocator { + static size_t const N = 100; + reuse_allocator() { + for (size_t i = 0; i < N; ++i) + __buffers[i] = malloc(8*1024); + } + ~reuse_allocator() { + for (size_t i = 0; i < N; ++i) + free(__buffers[i]); + } + void* alloc() { + assert(__next_id < N); + return __buffers[__next_id++]; + } + void reset() { __next_id = 0; } + void* __buffers[N]; + size_t __next_id = 0; +} reuse_buffers; + +template +struct user_allocator { + using value_type = T; + user_allocator() = default; + template + user_allocator(user_allocator) {} + friend bool operator==(user_allocator, user_allocator) {return true;} + friend bool operator!=(user_allocator x, user_allocator y) {return !(x == y);} + + T* allocate(size_t) { return (T*)reuse_buffers.alloc(); } + void deallocate(T*, size_t) noexcept {} +}; + +template +struct std::__asan_annotate_container_with_allocator> { + static bool const value = false; +}; + +int main() { + using V = std::vector>; + + { + V* v = new (reuse_buffers.alloc()) V(); + for (int i = 0; i < 100; i++) + v->push_back(i); + } + reuse_buffers.reset(); + { + V v; + for (int i = 0; i < 1000; i++) + v.push_back(i); + } + + return 0; +}