diff --git a/libcxx/include/string b/libcxx/include/string --- a/libcxx/include/string +++ b/libcxx/include/string @@ -599,6 +599,11 @@ _LIBCPP_PUSH_MACROS #include <__undef_macros> +#ifndef _LIBCPP_HAS_NO_ASAN +# define _LIBCPP_STRING_INTERNAL_MEMORY_ACCESS __attribute__((no_sanitize("address"))) +#else +# define _LIBCPP_STRING_INTERNAL_MEMORY_ACCESS +#endif _LIBCPP_BEGIN_NAMESPACE_STD @@ -667,6 +672,10 @@ _LIBCPP_PREFERRED_NAME(u32string) basic_string { + +private: + using __default_allocator_type = allocator<_CharT>; + public: typedef basic_string __self; typedef basic_string_view<_CharT, _Traits> __self_view; @@ -1505,11 +1514,11 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __shrink_or_extend(size_type __target_capacity); - _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 - bool __is_long() const _NOEXCEPT { - if (__libcpp_is_constant_evaluated()) - return true; - return __r_.first().__s.__is_long_; + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_STRING_INTERNAL_MEMORY_ACCESS bool + __is_long() const _NOEXCEPT { + if (__libcpp_is_constant_evaluated()) + return true; + return __r_.first().__s.__is_long_; } static _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __begin_lifetime(pointer __begin, size_type __n) { @@ -1554,10 +1563,11 @@ value_type* __p; if (__cap - __sz >= __n) { - __p = std::__to_address(__get_pointer()); - size_type __n_move = __sz - __ip; - if (__n_move != 0) - traits_type::move(__p + __ip + __n, __p + __ip, __n_move); + __annotate_increase(__n); + __p = std::__to_address(__get_pointer()); + size_type __n_move = __sz - __ip; + if (__n_move != 0) + traits_type::move(__p + __ip + __n, __p + __ip, __n_move); } else { @@ -1576,17 +1586,17 @@ _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 - void __set_short_size(size_type __s) _NOEXCEPT { - _LIBCPP_ASSERT(__s < __min_cap, "__s should never be greater than or equal to the short string capacity"); - __r_.first().__s.__size_ = __s; - __r_.first().__s.__is_long_ = false; + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_STRING_INTERNAL_MEMORY_ACCESS void + __set_short_size(size_type __s) _NOEXCEPT { + _LIBCPP_ASSERT(__s < __min_cap, "__s should never be greater than or equal to the short string capacity"); + __r_.first().__s.__size_ = __s; + __r_.first().__s.__is_long_ = false; } - _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 - size_type __get_short_size() const _NOEXCEPT { - _LIBCPP_ASSERT(!__r_.first().__s.__is_long_, "String has to be short when trying to get the short size"); - return __r_.first().__s.__size_; + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_STRING_INTERNAL_MEMORY_ACCESS size_type + __get_short_size() const _NOEXCEPT { + _LIBCPP_ASSERT(!__r_.first().__s.__is_long_, "String has to be short when trying to get the short size"); + return __r_.first().__s.__size_; } _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 @@ -1631,10 +1641,164 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 const_pointer __get_pointer() const _NOEXCEPT {return __is_long() ? __get_long_pointer() : __get_short_pointer();} + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_STRING_INTERNAL_MEMORY_ACCESS void + __raw_copy_r(basic_string& __dst, const basic_string& __src) _NOEXCEPT { + __dst.__r_.first().__r = __src.__r_.first().__r; + } + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_STRING_INTERNAL_MEMORY_ACCESS void + __raw_copy_l(basic_string& __dst, const basic_string& __src) _NOEXCEPT { + __dst.__r_.first().__l = __src.__r_.first().__l; + } + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_STRING_INTERNAL_MEMORY_ACCESS void + __raw_copy(basic_string& __dst, const basic_string& __src) _NOEXCEPT { + __dst.__r_.first() = __src.__r_.first(); + } - _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 - void __zero() _NOEXCEPT { - __r_.first() = __rep(); + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_STRING_INTERNAL_MEMORY_ACCESS void __zero() _NOEXCEPT { + // ASan annotations: + // __zero() may be called when object fields contain random data, + // therefore we force caller to handle whole object memory annotations, + // as only caller knows if object is already initialized and + // __annotate_new() can be safely called. + // + // That decision is also good for performance. + __r_.first() = __rep(); + } + + // Implementation based on that from std::vector. + // + // The following functions are no-ops outside of AddressSanitizer mode. + // We call annotatations only for the default Allocator because other allocators + // may not meet the AddressSanitizer alignment constraints. + // See the documentation for __sanitizer_annotate_contiguous_container for more details. + // + // For SSO we need to check if object address is aligned, it is different than + // check for standard allocator (is allocator_type same as __default_allocator_type), + // as object itself could be allocated with different allocator than used inside the object. +#ifdef TEST_HAS_FEATURE +# if TEST_HAS_FEATURE(address_sanitizer) && _LIBCPP_CLANG_VER >= 16000 + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __annotate_contiguous_container( + const void* __beg, const void* __end, const void* __old_mid, const void* __new_mid) const { + if (!__libcpp_is_constant_evaluated()) + if (__beg && is_same::value) + __sanitizer_annotate_contiguous_container(__beg, __end, __old_mid, __new_mid); + } +# else + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void + __annotate_contiguous_container(const void*, const void*, const void*, const void*) const _NOEXCEPT {} +# endif // TEST_HAS_FEATURE(address_sanitizer) && _LIBCPP_CLANG_VER >= 16000 +# if TEST_HAS_FEATURE(address_sanitizer) && _LIBCPP_CLANG_VER >= 16000 + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool __annotate_short_string_check() const _NOEXCEPT { + if (!__libcpp_is_constant_evaluated()) + return __sanitizer_is_annotable(this, sizeof(*this)); + else + return false; + } +# else + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool __annotate_short_string_check() const _NOEXCEPT { + return false; + } +# endif // TEST_HAS_FEATURE(address_sanitizer) && _LIBCPP_CLANG_VER >= 16000 +#else + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void + __annotate_contiguous_container(const void*, const void*, const void*, const void*) const _NOEXCEPT {} + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool __annotate_short_string_check() const _NOEXCEPT { + return false; + } +#endif // TEST_HAS_FEATURE + + template + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __annotate_new(size_type __current_size) const _NOEXCEPT { + if (!__libcpp_is_constant_evaluated()) { + if (!__is_short) + __annotate_contiguous_container( + std::__to_address(__get_long_pointer()), + std::__to_address(__get_long_pointer() + __get_long_cap() + 1), + std::__to_address(__get_long_pointer() + __get_long_cap() + 1), + std::__to_address(__get_long_pointer() + __current_size + 1)); + else if (__annotate_short_string_check()) + __annotate_contiguous_container( + this, this + 1, this + 1, std::__to_address(__get_short_pointer()) + __current_size + 1); + } + } + + template + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __annotate_delete() const _NOEXCEPT { + if (!__libcpp_is_constant_evaluated()) { + if (!__is_short) + __annotate_contiguous_container( + std::__to_address(__get_long_pointer()), + std::__to_address(__get_long_pointer() + __get_long_cap() + 1), + std::__to_address(__get_long_pointer() + __get_long_size() + 1), + std::__to_address(__get_long_pointer() + __get_long_cap() + 1)); + else if (__annotate_short_string_check()) + __annotate_contiguous_container( + this, this + 1, std::__to_address(__get_short_pointer()) + __get_short_size() + 1, this + 1); + } + } + + template + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __annotate_increase(size_type __n) const _NOEXCEPT { + if (!__libcpp_is_constant_evaluated()) { + if (!__is_short) + __annotate_contiguous_container( + std::__to_address(__get_long_pointer()), + std::__to_address(__get_long_pointer() + __get_long_cap() + 1), + std::__to_address(__get_long_pointer() + __get_long_size() + 1), + std::__to_address(__get_long_pointer() + __get_long_size() + 1 + __n)); + else if (__annotate_short_string_check()) + __annotate_contiguous_container( + this, + this + 1, + std::__to_address(__get_short_pointer() + __get_short_size() + 1), + std::__to_address(__get_short_pointer() + __get_short_size() + 1 + __n)); + } + } + + template + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __annotate_shrink(size_type __old_size) const _NOEXCEPT { + if (!__libcpp_is_constant_evaluated()) { + if (!__is_short) + __annotate_contiguous_container( + std::__to_address(__get_long_pointer()), + std::__to_address(__get_long_pointer() + __get_long_cap() + 1), + std::__to_address(__get_long_pointer() + __old_size + 1), + std::__to_address(__get_long_pointer() + __get_long_size() + 1)); + else if (__annotate_short_string_check()) + __annotate_contiguous_container( + this, + this + 1, + std::__to_address(__get_short_pointer() + __old_size + 1), + std::__to_address(__get_short_pointer() + __get_short_size() + 1)); + } + } + + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __annotate_delete() const _NOEXCEPT { + if (__is_long()) + __annotate_delete(); + else + __annotate_delete(); + } + + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __annotate_new(size_type __current_size) const _NOEXCEPT { + if (__is_long()) + __annotate_new(__current_size); + else + __annotate_new(__current_size); + } + + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __annotate_increase(size_type __n) const _NOEXCEPT { + if (__is_long()) + __annotate_increase(__n); + else + __annotate_increase(__n); + } + + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __annotate_shrink(size_type __old_size) const _NOEXCEPT { + if (__is_long()) + __annotate_shrink(__old_size); + else + __annotate_shrink(__old_size); } template static @@ -1738,6 +1902,7 @@ __set_long_pointer(__allocation.ptr); __set_long_cap(__allocation.count); __set_long_size(__str.size()); + __annotate_new(__get_long_size()); } } } @@ -1785,19 +1950,29 @@ // Assigns the value in __s, guaranteed to be __n < __min_cap in length. inline basic_string& __assign_short(const value_type* __s, size_type __n) { + size_type __old_sz = size(); + if (__n > __old_sz) + __annotate_increase(__n - __old_sz); 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_sz > __n) + __annotate_shrink(__old_sz); return *this; } _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& __null_terminate_at(value_type* __p, size_type __newsz) { + size_type __old_sz = size(); + if (__newsz > __old_sz) + __annotate_increase(__newsz - __old_sz); __set_size(__newsz); __invalidate_iterators_past(__newsz); traits_type::assign(__p[__newsz], value_type()); + if (__old_sz > __newsz) + __annotate_shrink(__old_sz); return *this; } @@ -1913,6 +2088,7 @@ { std::__debug_db_insert_c(this); __default_init(); + __annotate_new(0); } template @@ -1927,6 +2103,7 @@ { std::__debug_db_insert_c(this); __default_init(); + __annotate_new(0); } template @@ -1939,6 +2116,14 @@ __zero(); if (__reserve > max_size()) __throw_length_error(); + // ASan annotations: + // __init may be called after setting shadow memory, then we have to + // unpoison object memory. + // Chosen solution is to force caller to unpoison memory before call, + // as caller knows if memory is already poisoned or not. + // It is different than in __zero/__default_init, as there we turned off instrumentation, + // but there also caller has to take care of annotations. + pointer __p; if (__fits_in_sso(__reserve)) { @@ -1956,6 +2141,7 @@ } traits_type::copy(std::__to_address(__p), __s, __sz); traits_type::assign(__p[__sz], value_type()); + __annotate_new(__sz); } template @@ -1984,6 +2170,7 @@ } traits_type::copy(std::__to_address(__p), __s, __sz); traits_type::assign(__p[__sz], value_type()); + __annotate_new(__sz); } template @@ -2022,12 +2209,12 @@ basic_string<_CharT, _Traits, _Allocator>::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().__r = __str.__r_.first().__r; - else - __init_copy_ctor_external(std::__to_address(__str.__get_long_pointer()), - __str.__get_long_size()); - std::__debug_db_insert_c(this); + if (!__str.__is_long()) { + __raw_copy_r(*this, __str); + __annotate_new(__get_short_size()); + } else + __init_copy_ctor_external(std::__to_address(__str.__get_long_pointer()), __str.__get_long_size()); + std::__debug_db_insert_c(this); } template @@ -2036,12 +2223,12 @@ const basic_string& __str, const allocator_type& __a) : __r_(__default_init_tag(), __a) { - if (!__str.__is_long()) - __r_.first().__r = __str.__r_.first().__r; - else - __init_copy_ctor_external(std::__to_address(__str.__get_long_pointer()), - __str.__get_long_size()); - std::__debug_db_insert_c(this); + if (!__str.__is_long()) { + __raw_copy_r(*this, __str); + __annotate_new(__get_short_size()); + } else + __init_copy_ctor_external(std::__to_address(__str.__get_long_pointer()), __str.__get_long_size()); + std::__debug_db_insert_c(this); } template @@ -2065,24 +2252,28 @@ __set_long_size(__sz); } traits_type::copy(std::__to_address(__p), __s, __sz + 1); + __annotate_new(__sz); } #ifndef _LIBCPP_CXX03_LANG template -inline _LIBCPP_CONSTEXPR_SINCE_CXX20 -basic_string<_CharT, _Traits, _Allocator>::basic_string(basic_string&& __str) -#if _LIBCPP_STD_VER <= 14 - _NOEXCEPT_(is_nothrow_move_constructible::value) -#else - _NOEXCEPT -#endif - : __r_(std::move(__str.__r_)) -{ - __str.__default_init(); - std::__debug_db_insert_c(this); - if (__is_long()) - std::__debug_db_swap(this, &__str); +inline _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string<_CharT, _Traits, _Allocator>::basic_string(basic_string&& __str) +# if _LIBCPP_STD_VER <= 14 + _NOEXCEPT_(is_nothrow_move_constructible::value) +# else + _NOEXCEPT +# endif + // TODO (ASan annotations): we could call __annotate_delete only for + // SSO case this would improve performance, but not sure if it is worth + : __r_((__str.__annotate_delete(), std::move(__str.__r_))) { + __str.__default_init(); + std::__debug_db_insert_c(this); + __str.__annotate_new(0); + if (__is_long()) + std::__debug_db_swap(this, &__str); + if (this != &__str) + __annotate_new(size()); } template @@ -2095,12 +2286,22 @@ else { if (__libcpp_is_constant_evaluated()) { - __zero(); - __r_.first().__l = __str.__r_.first().__l; + __zero(); + __raw_copy_l(*this, __str); } else { - __r_.first().__r = __str.__r_.first().__r; + __raw_copy_r(*this, __str); } __str.__default_init(); + if (!__is_long()) { + __annotate_new(size()); + // since we copied __str to ourself and we are short, + // then __str was also short; because of that, some of its memory + // could be poisoned, so we have to call annotate_shrink for it + __str.__annotate_shrink(size()); + } else + // __str was long, so memory is not poisoned at all + // and we have to poison it now + __str.__annotate_new(0); } std::__debug_db_insert_c(this); if (__is_long()) @@ -2135,6 +2336,7 @@ } traits_type::assign(std::__to_address(__p), __n, __c); traits_type::assign(__p[__n], value_type()); + __annotate_new(__n); } template @@ -2228,6 +2430,7 @@ basic_string<_CharT, _Traits, _Allocator>::__init(_InputIterator __first, _InputIterator __last) { __default_init(); + __annotate_new(0); #ifndef _LIBCPP_NO_EXCEPTIONS try { @@ -2238,8 +2441,10 @@ } catch (...) { - if (__is_long()) - __alloc_traits::deallocate(__alloc(), __get_long_pointer(), __get_long_cap()); + if (__is_long()) { + __annotate_delete(); + __alloc_traits::deallocate(__alloc(), __get_long_pointer(), __get_long_cap()); + } throw; } #endif // _LIBCPP_NO_EXCEPTIONS @@ -2291,6 +2496,7 @@ throw; } #endif // _LIBCPP_NO_EXCEPTIONS + __annotate_new(__sz); } template @@ -2343,6 +2549,7 @@ basic_string<_CharT, _Traits, _Allocator>::~basic_string() { std::__debug_db_erase_c(this); + __annotate_delete(); if (__is_long()) __alloc_traits::deallocate(__alloc(), __get_long_pointer(), __get_long_cap()); } @@ -2361,6 +2568,7 @@ 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); @@ -2381,6 +2589,7 @@ __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); } template @@ -2396,6 +2605,7 @@ 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); @@ -2412,6 +2622,7 @@ __alloc_traits::deallocate(__alloc(), __old_p, __old_cap + 1); __set_long_pointer(__p); __set_long_cap(__allocation.count); + __annotate_new(__old_cap + __delta_cap); } // assign @@ -2424,11 +2635,16 @@ const value_type* __s, size_type __n) { size_type __cap = __is_short ? static_cast(__min_cap) : __get_long_cap(); if (__n < __cap) { + size_type __old_sz = __is_short ? __get_short_size() : __get_long_size(); + if (__n > __old_sz) + __annotate_increase(__n - __old_sz); 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()); __invalidate_iterators_past(__n); + if (__old_sz > __n) + __annotate_shrink(__old_sz); } 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); @@ -2443,6 +2659,9 @@ const value_type* __s, size_type __n) { size_type __cap = capacity(); if (__cap >= __n) { + size_type __old_sz = size(); + if (__n > __old_sz) + __annotate_increase(__n - __old_sz); value_type* __p = std::__to_address(__get_pointer()); traits_type::move(__p, __s, __n); return __null_terminate_at(__p, __n); @@ -2470,10 +2689,24 @@ basic_string<_CharT, _Traits, _Allocator>::assign(size_type __n, value_type __c) { size_type __cap = capacity(); + size_type __old_sz = size(); if (__cap < __n) { size_type __sz = size(); __grow_by(__cap, __n - __cap, __sz, 0, __sz); + // We cannot call __null_terminate_at, because state of the string may not be correct, + // after __grow_by and correct state is necessery when changing annotations. + // __null_terminate_at is changing annotations. + value_type* __p = std::__to_address(__get_pointer()); + traits_type::assign(__p, __n, __c); + + __set_size(__n); + __invalidate_iterators_past(__n); + traits_type::assign(__p[__n], value_type()); + + return *this; + } else if (__n > __old_sz) { + __annotate_increase(__n - __old_sz); } value_type* __p = std::__to_address(__get_pointer()); traits_type::assign(__p, __n, __c); @@ -2486,19 +2719,28 @@ basic_string<_CharT, _Traits, _Allocator>::operator=(value_type __c) { pointer __p; + size_type __old_sz; if (__is_long()) { - __p = __get_long_pointer(); - __set_long_size(1); + __old_sz = __get_long_size(); + if (__old_sz == 0) + __annotate_increase(1); + __p = __get_long_pointer(); + __set_long_size(1); } else { - __p = __get_short_pointer(); - __set_short_size(1); + __old_sz = __get_short_size(); + if (__old_sz == 0) + __annotate_increase(1); + __p = __get_short_pointer(); + __set_short_size(1); } traits_type::assign(*__p, __c); traits_type::assign(*++__p, value_type()); __invalidate_iterators_past(1); + if (__old_sz > 1) + __annotate_shrink(__old_sz); return *this; } @@ -2511,7 +2753,15 @@ __copy_assign_alloc(__str); if (!__is_long()) { if (!__str.__is_long()) { - __r_.first().__r = __str.__r_.first().__r; + size_type __old_sz = __get_short_size(); + if (__get_short_size() < __str.__get_short_size()) + __annotate_increase(__str.__get_short_size() - __get_short_size()); + // ASan annotations: The __raw_copy_r also copies annotated + // (poisoned in shadow memory) bytes so normally it should crash but + // we disable its instrumentation for correctness reasons + __raw_copy_r(*this, __str); + if (__old_sz > __get_short_size()) + __annotate_shrink(__old_sz); } else { return __assign_no_alias(__str.data(), __str.size()); } @@ -2546,6 +2796,7 @@ _NOEXCEPT_(is_nothrow_move_assignable::value) #endif { + __annotate_delete(); if (__is_long()) { __alloc_traits::deallocate(__alloc(), __get_long_pointer(), __get_long_cap()); @@ -2553,17 +2804,39 @@ if (!is_nothrow_move_assignable::value) { __set_short_size(0); traits_type::assign(__get_short_pointer()[0], value_type()); + __annotate_new(0); } #endif } __move_assign_alloc(__str); - __r_.first() = __str.__r_.first(); + size_type __str_old_sz = __str.size(); + bool __str_was_short = !__str.__is_long(); + + __raw_copy(*this, __str); if (__libcpp_is_constant_evaluated()) { __str.__default_init(); } else { __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_sz); + 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);` + // Otherwise, we won't fulfill ASAN API's constraints in `__annotate_new(size())` + // 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()); } template @@ -2604,30 +2877,30 @@ > basic_string<_CharT, _Traits, _Allocator>::assign(_ForwardIterator __first, _ForwardIterator __last) { - size_type __cap = capacity(); - size_type __n = __string_is_trivial_iterator<_ForwardIterator>::value ? - static_cast(std::distance(__first, __last)) : 0; - - if (__string_is_trivial_iterator<_ForwardIterator>::value && - (__cap >= __n || !__addr_in_range(*__first))) - { - if (__cap < __n) - { - size_type __sz = size(); - __grow_by(__cap, __n - __cap, __sz, 0, __sz); - } - pointer __p = __get_pointer(); - for (; __first != __last; ++__p, (void) ++__first) - traits_type::assign(*__p, *__first); - traits_type::assign(*__p, value_type()); - __set_size(__n); - __invalidate_iterators_past(__n); - } - else - { - const basic_string __temp(__first, __last, __alloc()); - assign(__temp.data(), __temp.size()); - } + size_type __old_sz = size(); + size_type __cap = capacity(); + size_type __n = __string_is_trivial_iterator<_ForwardIterator>::value + ? static_cast(std::distance(__first, __last)) + : 0; + + if (__string_is_trivial_iterator<_ForwardIterator>::value && (__cap >= __n || !__addr_in_range(*__first))) { + if (__cap < __n) { + size_type __sz = size(); + __grow_by(__cap, __n - __cap, __sz, 0, __sz); + } else if (__n > __old_sz) + __annotate_increase(__n - __old_sz); + pointer __p = __get_pointer(); + for (; __first != __last; ++__p, (void)++__first) + traits_type::assign(*__p, *__first); + traits_type::assign(*__p, value_type()); + __set_size(__n); + __invalidate_iterators_past(__n); + if (__n < __old_sz) + __annotate_shrink(__old_sz); + } else { + const basic_string __temp(__first, __last, __alloc()); + assign(__temp.data(), __temp.size()); + } return *this; } @@ -2694,11 +2967,12 @@ { if (__n) { - value_type* __p = std::__to_address(__get_pointer()); - traits_type::copy(__p + __sz, __s, __n); - __sz += __n; - __set_size(__sz); - traits_type::assign(__p[__sz], value_type()); + __annotate_increase(__n); + value_type* __p = std::__to_address(__get_pointer()); + traits_type::copy(__p + __sz, __s, __n); + __sz += __n; + __set_size(__sz); + traits_type::assign(__p[__sz], value_type()); } } else @@ -2717,6 +2991,8 @@ size_type __sz = size(); if (__cap - __sz < __n) __grow_by(__cap, __sz + __n - __cap, __sz, __sz, 0); + else + __annotate_increase(__n); pointer __p = __get_pointer(); traits_type::assign(std::__to_address(__p) + __sz, __n, __c); __sz += __n; @@ -2736,6 +3012,8 @@ size_type __sz = size(); if (__cap - __sz < __n) __grow_by(__cap, __sz + __n - __cap, __sz, __sz, 0); + else + __annotate_increase(__n); pointer __p = __get_pointer(); __sz += __n; __set_size(__sz); @@ -2765,7 +3043,8 @@ { __grow_by(__cap, 1, __sz, __sz, 0); __is_short = false; // the string is always long after __grow_by - } + } else + __annotate_increase(1); pointer __p = __get_pointer(); if (__is_short) { @@ -2802,6 +3081,8 @@ { if (__cap - __sz < __n) __grow_by(__cap, __sz + __n - __cap, __sz, __sz, 0); + else + __annotate_increase(__n); pointer __p = __get_pointer() + __sz; for (; __first != __last; ++__p, (void) ++__first) traits_type::assign(*__p, *__first); @@ -2885,14 +3166,14 @@ { if (__n) { - value_type* __p = std::__to_address(__get_pointer()); - size_type __n_move = __sz - __pos; - if (__n_move != 0) - { - if (__p + __pos <= __s && __s < __p + __sz) - __s += __n; - traits_type::move(__p + __pos + __n, __p + __pos, __n_move); - } + __annotate_increase(__n); + value_type* __p = std::__to_address(__get_pointer()); + size_type __n_move = __sz - __pos; + if (__n_move != 0) { + if (__p + __pos <= __s && __s < __p + __sz) + __s += __n; + traits_type::move(__p + __pos + __n, __p + __pos, __n_move); + } traits_type::move(__p + __pos, __s, __n); __sz += __n; __set_size(__sz); @@ -2918,10 +3199,11 @@ value_type* __p; if (__cap - __sz >= __n) { - __p = std::__to_address(__get_pointer()); - size_type __n_move = __sz - __pos; - if (__n_move != 0) - traits_type::move(__p + __pos + __n, __p + __pos, __n_move); + __annotate_increase(__n); + __p = std::__to_address(__get_pointer()); + size_type __n_move = __sz - __pos; + if (__n_move != 0) + traits_type::move(__p + __pos + __n, __p + __pos, __n_move); } else { @@ -3049,10 +3331,11 @@ } else { - __p = std::__to_address(__get_pointer()); - size_type __n_move = __sz - __ip; - if (__n_move != 0) - traits_type::move(__p + __ip + 1, __p + __ip, __n_move); + __annotate_increase(1); + __p = std::__to_address(__get_pointer()); + size_type __n_move = __sz - __ip; + if (__n_move != 0) + traits_type::move(__p + __ip + 1, __p + __ip, __n_move); } traits_type::assign(__p[__ip], __c); traits_type::assign(__p[++__sz], value_type()); @@ -3096,30 +3379,29 @@ value_type* __p = std::__to_address(__get_pointer()); if (__n1 != __n2) { - size_type __n_move = __sz - __pos - __n1; - if (__n_move != 0) - { - if (__n1 > __n2) - { - traits_type::move(__p + __pos, __s, __n2); - traits_type::move(__p + __pos + __n2, __p + __pos + __n1, __n_move); - return __null_terminate_at(__p, __sz + (__n2 - __n1)); - } - if (__p + __pos < __s && __s < __p + __sz) - { - if (__p + __pos + __n1 <= __s) - __s += __n2 - __n1; - else // __p + __pos < __s < __p + __pos + __n1 - { - traits_type::move(__p + __pos, __s, __n1); - __pos += __n1; - __s += __n2; - __n2 -= __n1; - __n1 = 0; - } - } - traits_type::move(__p + __pos + __n2, __p + __pos + __n1, __n_move); + if (__n2 > __n1) + __annotate_increase(__n2 - __n1); + size_type __n_move = __sz - __pos - __n1; + if (__n_move != 0) { + if (__n1 > __n2) { + traits_type::move(__p + __pos, __s, __n2); + traits_type::move(__p + __pos + __n2, __p + __pos + __n1, __n_move); + return __null_terminate_at(__p, __sz + (__n2 - __n1)); + } + if (__p + __pos < __s && __s < __p + __sz) { + if (__p + __pos + __n1 <= __s) + __s += __n2 - __n1; + else // __p + __pos < __s < __p + __pos + __n1 + { + traits_type::move(__p + __pos, __s, __n1); + __pos += __n1; + __s += __n2; + __n2 -= __n1; + __n1 = 0; + } } + traits_type::move(__p + __pos + __n2, __p + __pos + __n1, __n_move); + } } traits_type::move(__p + __pos, __s, __n2); return __null_terminate_at(__p, __sz + (__n2 - __n1)); @@ -3145,15 +3427,28 @@ __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); + 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(__cap, __sz - __n1 + __n2 - __cap, __sz, __pos, __n1, __n2); __p = std::__to_address(__get_long_pointer()); + + // We cannot call __null_terminate_at, because state of the string may not be correct, + // after __grow_by and correct state is necessery when changing annotations. + // __null_terminate_at is changing annotations. + traits_type::assign(__p + __pos, __n2, __c); + + __set_size(__sz - (__n1 - __n2)); + __invalidate_iterators_past(__sz - (__n1 - __n2)); + traits_type::assign(__p[__sz - (__n1 - __n2)], value_type()); + + return *this; } traits_type::assign(__p + __pos, __n2, __c); return __null_terminate_at(__p, __sz - (__n1 - __n2)); @@ -3338,15 +3633,20 @@ basic_string<_CharT, _Traits, _Allocator>::clear() _NOEXCEPT { std::__debug_db_invalidate_all(this); + size_type __old_sz; if (__is_long()) { - traits_type::assign(*__get_long_pointer(), value_type()); - __set_long_size(0); + __old_sz = __get_long_size(); + traits_type::assign(*__get_long_pointer(), value_type()); + __set_long_size(0); + __annotate_shrink(__old_sz); } else { - traits_type::assign(*__get_short_pointer(), value_type()); - __set_short_size(0); + __old_sz = __get_short_size(); + traits_type::assign(*__get_short_pointer(), value_type()); + __set_short_size(0); + __annotate_shrink(__old_sz); } } @@ -3432,63 +3732,54 @@ void basic_string<_CharT, _Traits, _Allocator>::__shrink_or_extend(size_type __target_capacity) { - size_type __cap = capacity(); - size_type __sz = size(); - - pointer __new_data, __p; - bool __was_long, __now_long; - if (__fits_in_sso(__target_capacity)) - { - __was_long = true; - __now_long = false; - __new_data = __get_short_pointer(); - __p = __get_long_pointer(); - } - else - { - if (__target_capacity > __cap) { - auto __allocation = std::__allocate_at_least(__alloc(), __target_capacity + 1); - __new_data = __allocation.ptr; - __target_capacity = __allocation.count - 1; - } - else - { - #ifndef _LIBCPP_NO_EXCEPTIONS - try - { - #endif // _LIBCPP_NO_EXCEPTIONS - auto __allocation = std::__allocate_at_least(__alloc(), __target_capacity + 1); - __new_data = __allocation.ptr; - __target_capacity = __allocation.count - 1; - #ifndef _LIBCPP_NO_EXCEPTIONS - } - catch (...) - { - return; - } - #else // _LIBCPP_NO_EXCEPTIONS - if (__new_data == nullptr) - return; - #endif // _LIBCPP_NO_EXCEPTIONS - } - __begin_lifetime(__new_data, __target_capacity + 1); - __now_long = true; - __was_long = __is_long(); - __p = __get_pointer(); - } - traits_type::copy(std::__to_address(__new_data), - std::__to_address(__p), size()+1); - if (__was_long) - __alloc_traits::deallocate(__alloc(), __p, __cap+1); - if (__now_long) - { - __set_long_cap(__target_capacity+1); - __set_long_size(__sz); - __set_long_pointer(__new_data); + __annotate_delete(); + size_type __cap = capacity(); + size_type __sz = size(); + + pointer __new_data, __p; + bool __was_long, __now_long; + if (__fits_in_sso(__target_capacity)) { + __was_long = true; + __now_long = false; + __new_data = __get_short_pointer(); + __p = __get_long_pointer(); + } else { + if (__target_capacity > __cap) { + auto __allocation = std::__allocate_at_least(__alloc(), __target_capacity + 1); + __new_data = __allocation.ptr; + __target_capacity = __allocation.count - 1; + } else { +#ifndef _LIBCPP_NO_EXCEPTIONS + try { +#endif // _LIBCPP_NO_EXCEPTIONS + auto __allocation = std::__allocate_at_least(__alloc(), __target_capacity + 1); + __new_data = __allocation.ptr; + __target_capacity = __allocation.count - 1; +#ifndef _LIBCPP_NO_EXCEPTIONS + } catch (...) { + return; + } +#else // _LIBCPP_NO_EXCEPTIONS + if (__new_data == nullptr) + return; +#endif // _LIBCPP_NO_EXCEPTIONS } - else - __set_short_size(__sz); - std::__debug_db_invalidate_all(this); + __begin_lifetime(__new_data, __target_capacity + 1); + __now_long = true; + __was_long = __is_long(); + __p = __get_pointer(); + } + traits_type::copy(std::__to_address(__new_data), std::__to_address(__p), size() + 1); + if (__was_long) + __alloc_traits::deallocate(__alloc(), __p, __cap + 1); + if (__now_long) { + __set_long_cap(__target_capacity + 1); + __set_long_size(__sz); + __set_long_pointer(__new_data); + } else + __set_short_size(__sz); + std::__debug_db_invalidate_all(this); + __annotate_new(__sz); } template @@ -3607,8 +3898,16 @@ __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 @@ -4147,10 +4446,12 @@ clear(); if(__is_long()) { - __alloc_traits::deallocate(__alloc(), __get_long_pointer(), capacity() + 1); - __set_long_cap(0); - __set_short_size(0); - traits_type::assign(*__get_short_pointer(), value_type()); + __annotate_delete(); + __alloc_traits::deallocate(__alloc(), __get_long_pointer(), capacity() + 1); + __set_long_cap(0); + __set_short_size(0); + traits_type::assign(*__get_short_pointer(), value_type()); + __annotate_new(0); } } @@ -4398,10 +4699,12 @@ _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.data(), __rhs_sz); _Traits::assign(__ptr + __lhs_sz + __rhs_sz, 1, _CharT()); + __r.__annotate_new(__lhs_sz + __rhs_sz); return __r; } @@ -4416,10 +4719,12 @@ _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; } @@ -4433,10 +4738,12 @@ _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; } @@ -4451,10 +4758,12 @@ _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; } @@ -4468,10 +4777,12 @@ _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.asan/append.pass.cpp b/libcxx/test/std/strings/basic.string/string.asan/append.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/strings/basic.string/string.asan/append.pass.cpp @@ -0,0 +1,525 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// + +#include +#include +#include + +#include "test_macros.h" +#include "asan_testing.h" + +template +size_t strlen(const CharT* str); + +template <> +size_t strlen(const char* str) { + return std::strlen(str); +} + +template <> +size_t strlen(const wchar_t* str) { + return std::wcslen(str); +} +#if TEST_STD_VER >= 11 +template <> +size_t strlen(const char16_t* str) { + size_t size = 0; + while (str[size] != u'\0') { + size += 1; + } + + return size; +} + +template <> +size_t strlen(const char32_t* str) { + size_t size = 0; + while (str[size] != U'\0') { + size += 1; + } + + return size; +} +#endif +#if TEST_STD_VER >= 20 +template <> +size_t strlen(const char8_t* str) { + size_t size = 0; + while (str[size] != u8'\0') { + size += 1; + } + + return size; +} +#endif + +template +void test_simple_append(const CharT* s_init) { + typedef std::basic_string S; + S empty_s; + S short_s(2, 'b'); + S long_s(1100, 'a'); + + empty_s.append(empty_s); + short_s.append(empty_s); + long_s.append(empty_s); + + assert(is_string_asan_correct(empty_s)); + assert(is_string_asan_correct(short_s)); + assert(is_string_asan_correct(long_s)); + + S short_s2(short_s); + const CharT val = s_init[0]; + short_s.append(2, val); + long_s.append(2, val); + + assert(is_string_asan_correct(short_s)); + assert(is_string_asan_correct(long_s)); + + short_s2.append(24, val); + assert(is_string_asan_correct(short_s2)); + short_s2.append(1024, val); + assert(is_string_asan_correct(short_s2)); + + empty_s.append(s_init); + short_s.append(short_s); + long_s.append(short_s); + + assert(is_string_asan_correct(empty_s)); + assert(is_string_asan_correct(short_s)); + assert(is_string_asan_correct(long_s)); + + S& not_empty_s = empty_s; + short_s.append(empty_s); + long_s.append(long_s); + + assert(is_string_asan_correct(not_empty_s)); + assert(is_string_asan_correct(short_s)); + assert(is_string_asan_correct(long_s)); + + not_empty_s.append(1024, val); + short_s.append(1024, val); + long_s.append(1024, val); + + assert(is_string_asan_correct(not_empty_s)); + assert(is_string_asan_correct(short_s)); + assert(is_string_asan_correct(long_s)); +} + +template +void test_append( + const CharT Val, const CharT* s_init, const CharT* s_init2, const CharT* s_long_init, const CharT* s_long_init2) { + typedef std::basic_string S; + + const CharT* ptr1 = s_init; + const CharT* ptr2 = s_long_init; + const CharT* ptr3 = s_init2; + const CharT* ptr4 = s_long_init2; + + size_t size1 = strlen(ptr1); + size_t size2 = strlen(ptr2); + size_t size3 = strlen(ptr3); + size_t size4 = strlen(ptr4); + + S s1a(s_init2), s1b(s_long_init2), s1c(s_long_init), s1d(s_init); + s1a.append(ptr1, ptr1 + size1); + s1b.append(ptr2, ptr2 + size2); + s1c.append(ptr3, size3); + s1d.append(ptr4, size4); + + assert(is_string_asan_correct(s1a)); + assert(is_string_asan_correct(s1b)); + assert(is_string_asan_correct(s1c)); + assert(is_string_asan_correct(s1d)); + + S s2a(ptr4), s2b(ptr3), s2c(s1a), s2d(s1b); + s2a.append(s1a); + s2b.append(s1b); + s2c.append(s1c, 1, 1); + s2d.append(s1d, 2); + + assert(is_string_asan_correct(s2a)); + assert(is_string_asan_correct(s2b)); + assert(is_string_asan_correct(s2c)); + assert(is_string_asan_correct(s2d)); + + assert(is_string_asan_correct(s1a)); + assert(is_string_asan_correct(s1b)); + + s2c.append(ptr4); + s2d.append(ptr3); + + assert(is_string_asan_correct(s2c)); + assert(is_string_asan_correct(s2d)); + + s2c.append(ptr2); + s2d.append(ptr1); + + assert(is_string_asan_correct(s2c)); + assert(is_string_asan_correct(s2d)); + + S s3a, s3b; + s3a.append(s1a.data(), s1a.data() + s1a.size()); + s3b.append(s1b.data(), s1b.data() + s1b.size()); + + assert(is_string_asan_correct(s3a)); + assert(is_string_asan_correct(s3b)); + + S s4; + s4.append(s1a + s1b); + + assert(is_string_asan_correct(s4)); + + assert(is_string_asan_correct(s1a)); + assert(is_string_asan_correct(s1b)); + + S s5a, s5b, s5c(ptr1), s5d(ptr2); + s5a.append(std::move(s1c)); + s5b.append(std::move(s1d)); + + assert(is_string_asan_correct(s5a)); + assert(is_string_asan_correct(s5b)); + + assert(is_string_asan_correct(s1c)); + assert(is_string_asan_correct(s1d)); + + s5c.append(std::move(s5a)); + s5d.append(std::move(s5b)); + + assert(is_string_asan_correct(s5a)); + assert(is_string_asan_correct(s5b)); + assert(is_string_asan_correct(s5c)); + assert(is_string_asan_correct(s5d)); + + S s6a, s6b, s6c; + s6a.append(ptr1); + s6b.append(ptr2); + s6c.append(s_init2); + + assert(is_string_asan_correct(s6a)); + assert(is_string_asan_correct(s6b)); + assert(is_string_asan_correct(s6c)); + + s6a.append(ptr2, 26); + s6b.append(ptr1, 1); + s6c.append(s_init2, 3); + + assert(is_string_asan_correct(s6a)); + assert(is_string_asan_correct(s6b)); + assert(is_string_asan_correct(s6c)); +#if TEST_STD_VER >= 11 + S s7a(s_init), s7b(s_long_init), s7c(s_init), s7d(s_long_init2); + s7a.append({Val}); + s7b.append({Val}); + s7c.append({Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, + Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val}); + s7d.append({Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, + Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val}); + + assert(s7c.size() > sizeof(void*) * 3); + + assert(is_string_asan_correct(s7a)); + assert(is_string_asan_correct(s7b)); + assert(is_string_asan_correct(s7c)); + assert(is_string_asan_correct(s7d)); +#endif + + S s8a, s8b, s8c, s8d; +#if TEST_STD_VER >= 11 + s8a.append(s1a.cbegin(), s1a.cend()); + s8b.append(s1b.cbegin(), s1b.cend()); + s8c.append(s1a.crbegin(), s1a.crend()); + s8d.append(s1b.crbegin(), s1b.crend()); +#else + s8a.append(s1a.begin(), s1a.end()); + s8b.append(s1b.begin(), s1b.end()); + s8c.append(s1a.rbegin(), s1a.rend()); + s8d.append(s1b.rbegin(), s1b.rend()); +#endif + + assert(is_string_asan_correct(s8a)); + assert(is_string_asan_correct(s8b)); + assert(is_string_asan_correct(s8c)); + assert(is_string_asan_correct(s8d)); + + assert(is_string_asan_correct(s1a)); + assert(is_string_asan_correct(s1b)); + + S& s9a = s1a; + S& s9b = s1b; +#if TEST_STD_VER >= 11 + s9a.append(s1a.cbegin() + 1, s1a.cend()); + s9b.append(s1b.cbegin() + 4, s1b.cbegin() + 10); +#else + s9a.append(s1a.begin() + 1, s1a.end()); + s9b.append(s1b.begin() + 4, s1b.begin() + 10); +#endif + + assert(is_string_asan_correct(s9a)); + assert(is_string_asan_correct(s9b)); + + assert(is_string_asan_correct(s1a)); + assert(is_string_asan_correct(s1b)); + + S s10a(ptr1), s10b(ptr2), s10c(ptr3), s10d(ptr4); + s2a.append(3, Val); + s2b.append(300, Val); + s2c.append(300, Val); + s2d.append(3, Val); + + assert(is_string_asan_correct(s10a)); + assert(is_string_asan_correct(s10b)); + assert(is_string_asan_correct(s10c)); + assert(is_string_asan_correct(s10d)); +} + +template +void test_add(const CharT* s_init) { + typedef std::basic_string S; + + S empty_s; + S short_s(2, 'b'); + S long_s(1100, 'a'); + + empty_s += empty_s; + short_s += empty_s; + long_s += empty_s; + + assert(is_string_asan_correct(empty_s)); + assert(is_string_asan_correct(short_s)); + assert(is_string_asan_correct(long_s)); + + S empty_s2, short_s2(short_s), long_s2(long_s); + empty_s2 += s_init; + short_s2 += s_init; + long_s2 += s_init; + + assert(is_string_asan_correct(empty_s2)); + assert(is_string_asan_correct(short_s2)); + assert(is_string_asan_correct(long_s2)); + + S empty_s3, short_s3(short_s), long_s3(long_s); + empty_s3 += short_s; + short_s3 += short_s; + long_s3 += short_s; + + assert(is_string_asan_correct(empty_s3)); + assert(is_string_asan_correct(short_s3)); + assert(is_string_asan_correct(long_s3)); + + S& not_empty_s3 = empty_s3; + + assert(is_string_asan_correct(not_empty_s3)); + + not_empty_s3 += long_s; + short_s3 += long_s; + long_s3 += long_s; + + assert(is_string_asan_correct(not_empty_s3)); + assert(is_string_asan_correct(short_s3)); + assert(is_string_asan_correct(long_s3)); + + S short_s4(short_s), long_s4(long_s); + short_s4 += not_empty_s3; + long_s4 += not_empty_s3; + + assert(is_string_asan_correct(short_s4)); + assert(is_string_asan_correct(long_s4)); + + CharT val = s_init[0]; + S empty_s5, short_s5(short_s), long_s5(long_s); + empty_s5 += val; + short_s5 += val; + long_s5 += val; + + assert(is_string_asan_correct(empty_s5)); + assert(is_string_asan_correct(short_s5)); + assert(is_string_asan_correct(long_s5)); +#if TEST_STD_VER >= 11 + S empty_s6, short_s6(short_s), long_s6(long_s); + empty_s6 += {}; + short_s6 += {}; + long_s6 += {}; + + assert(is_string_asan_correct(empty_s6)); + assert(is_string_asan_correct(short_s6)); + assert(is_string_asan_correct(long_s6)); + + empty_s6 += {val}; + short_s6 += {val}; + long_s6 += {val}; + + assert(is_string_asan_correct(empty_s6)); + assert(is_string_asan_correct(short_s6)); + assert(is_string_asan_correct(long_s6)); + + S empty_s7, short_s7(short_s), long_s7(long_s); + empty_s7 += {val, val, val, val, val, val, val, val, val, val, val, val, + val, val, val, val, val, val, val, val, val, val, val, val}; + short_s7 += {val, val, val, val, val, val, val, val, val, val, val, val, + val, val, val, val, val, val, val, val, val, val, val, val}; + long_s7 += {val, val, val, val, val, val, val, val, val, val, val, val, + val, val, val, val, val, val, val, val, val, val, val, val}; + + assert(is_string_asan_correct(empty_s7)); + assert(is_string_asan_correct(short_s7)); + assert(is_string_asan_correct(long_s7)); +#endif +} + +template +void test_view() { + typedef std::basic_string S; + typedef std::basic_string_view SV; + S empty_s; + S short_s(4, 'b'); + S long_s(1100, 'a'); + + SV empty_v(empty_s); + SV short_v(short_s); + SV long_v(long_s); + + assert(is_string_asan_correct(empty_s)); + assert(is_string_asan_correct(short_s)); + assert(is_string_asan_correct(long_s)); + + SV partial_short_v(short_s.data() + 1, 1); + SV partial_long_v(long_s.data() + 10, 200); + + assert(is_string_asan_correct(short_s)); + assert(is_string_asan_correct(long_s)); + + // append + + S empty_s2a(empty_s), short_s2a(short_s), long_s2a(long_s); + empty_s2a.append(empty_v); + short_s2a.append(short_v); + long_s2a.append(long_v); + + assert(is_string_asan_correct(empty_s2a)); + assert(is_string_asan_correct(short_s2a)); + assert(is_string_asan_correct(long_s2a)); + + S partial_short_s2, partial_long_s2(short_s); + partial_short_s2.append(partial_short_v); + partial_long_s2.append(partial_long_v); + + assert(is_string_asan_correct(partial_short_s2)); + assert(is_string_asan_correct(partial_long_s2)); + + S empty_s2b(empty_s), short_s2b(short_s), long_s2b(long_s); + empty_s2b.append(empty_v, 0, 0); + short_s2b.append(short_v, 1, 2); + long_s2b.append(long_v, 4, 280); + + assert(is_string_asan_correct(empty_s2b)); + assert(is_string_asan_correct(short_s2b)); + assert(is_string_asan_correct(long_s2b)); + + // operator+= + + S empty_s3, short_s3, long_s3; + empty_s3 += empty_v; + short_s3 += short_v; + long_s3 += long_v; + + assert(is_string_asan_correct(empty_s3)); + assert(is_string_asan_correct(short_s3)); + assert(is_string_asan_correct(long_s3)); + + S partial_short_s3, partial_long_s3; + partial_short_s3 += partial_short_v; + partial_long_s3 += partial_long_v; + + assert(is_string_asan_correct(partial_short_s3)); + assert(is_string_asan_correct(partial_long_s3)); +} + +template +void test( + const CharT Val, const CharT* s_init, const CharT* s_init2, const CharT* s_long_init, const CharT* s_long_init2) { + test_simple_append(s_init); + test_append(Val, s_init, s_init2, s_long_init, s_long_init2); + test_add(s_init); +} + +char literal[] = "ab"; +wchar_t wliteral[] = L"ab"; +#if TEST_STD_VER >= 11 +char16_t literal16[] = u"ab"; +char32_t literal32[] = U"ab"; +#endif +#if TEST_STD_VER >= 20 +char8_t literal8[] = u8"ab"; +#endif + +char literal_2[] = "abc"; +wchar_t wliteral_2[] = L"abc"; +#if TEST_STD_VER >= 11 +char16_t literal16_2[] = u"abc"; +char32_t literal32_2[] = U"abc"; +#endif +#if TEST_STD_VER >= 20 +char8_t literal8_2[] = u8"abc"; +#endif + +char literal_long[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +wchar_t wliteral_long[] = L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +#if TEST_STD_VER >= 11 +char16_t literal16_long[] = u"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +char32_t literal32_long[] = U"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +#endif +#if TEST_STD_VER >= 20 +char8_t literal8_long[] = u8"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +#endif + +char literal_long2[] = + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +wchar_t wliteral_long2[] = + L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +#if TEST_STD_VER >= 11 +char16_t literal16_long2[] = + u"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +char32_t literal32_long2[] = + U"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +#endif +#if TEST_STD_VER >= 20 +char8_t literal8_long2[] = + u8"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +#endif + +int main(int, char**) { + { + typedef char CharT; + test('x', literal, literal_2, literal_long, literal_long2); + } + { + typedef wchar_t CharT; + test(L'x', wliteral, wliteral_2, wliteral_long, wliteral_long2); + } +#if TEST_STD_VER >= 11 + { + typedef char16_t CharT; + test(u'x', literal16, literal16_2, literal16_long, literal16_long2); + } + { + typedef char32_t CharT; + test(U'x', literal32, literal32_2, literal32_long, literal32_long2); + } +#endif +#if TEST_STD_VER >= 20 + { + typedef char8_t CharT; + test(u8'x', literal8, literal8_2, literal8_long, literal8_long2); + } +#endif + + return 0; +} diff --git a/libcxx/test/std/strings/basic.string/string.asan/assign.pass.cpp b/libcxx/test/std/strings/basic.string/string.asan/assign.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/strings/basic.string/string.asan/assign.pass.cpp @@ -0,0 +1,495 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// + +#include +#include +#include + +#include "test_macros.h" +#include "asan_testing.h" + +template +size_t strlen(const CharT* str); + +template <> +size_t strlen(const char* str) { + return std::strlen(str); +} + +template <> +size_t strlen(const wchar_t* str) { + return std::wcslen(str); +} +#if TEST_STD_VER >= 11 +template <> +size_t strlen(const char16_t* str) { + size_t size = 0; + while (str[size] != u'\0') { + size += 1; + } + + return size; +} + +template <> +size_t strlen(const char32_t* str) { + size_t size = 0; + while (str[size] != U'\0') { + size += 1; + } + + return size; +} +#endif +#if TEST_STD_VER >= 20 +template <> +size_t strlen(const char8_t* str) { + size_t size = 0; + while (str[size] != u8'\0') { + size += 1; + } + + return size; +} +#endif + +template +void test_eq( + const CharT Val, const CharT* s_init, const CharT* s_init2, const CharT* s_long_init, const CharT* s_long_init2) { + typedef std::basic_string S; + + S s1a, s1b, s1c = s_init, s1d = s_long_init; + s1a = s_init2; + s1b = s_long_init2; + + assert(is_string_asan_correct(s1a)); + assert(is_string_asan_correct(s1b)); + assert(is_string_asan_correct(s1c)); + assert(is_string_asan_correct(s1d)); + + S s2a = s1a, s2b = s1b, s2c = s1c; + + assert(is_string_asan_correct(s2a)); + assert(is_string_asan_correct(s2b)); + assert(is_string_asan_correct(s2c)); + + assert(is_string_asan_correct(s1a)); + assert(is_string_asan_correct(s1b)); + assert(is_string_asan_correct(s1c)); + + S s3a, s3b, s3c; + s3a = s1a; + s3b = s1b; + s3c = s1c; + + assert(is_string_asan_correct(s3a)); + assert(is_string_asan_correct(s3b)); + assert(is_string_asan_correct(s3c)); + + assert(is_string_asan_correct(s1a)); + assert(is_string_asan_correct(s1b)); + assert(is_string_asan_correct(s1c)); + + S s4a, s4b(s_init), s4c(s_long_init); + s4a = s1a + s1b; + + assert(is_string_asan_correct(s4a)); + assert(is_string_asan_correct(s1a)); + assert(is_string_asan_correct(s1b)); + + s4b = s1a + s1b; + + assert(is_string_asan_correct(s4b)); + assert(is_string_asan_correct(s1a)); + assert(is_string_asan_correct(s1b)); + + s4c = s1a + s1b; + + assert(is_string_asan_correct(s4c)); + assert(is_string_asan_correct(s1a)); + assert(is_string_asan_correct(s1b)); + + s4a = s4a + s1a; + + assert(is_string_asan_correct(s4a)); + assert(is_string_asan_correct(s1a)); + + s4c = s4c + s4c; + + assert(is_string_asan_correct(s4c)); + + S s5a, s5b(s_init), s5c(s_long_init), s5d, s5e(s_init), s5f(s_long_init); + s5a = std::move(s1c); + s5b = std::move(s2c); + s5c = std::move(s3c); + s5d = std::move(s5d); + s5e = std::move(s5e); + s5f = std::move(s5f); + + assert(is_string_asan_correct(s5a)); + assert(is_string_asan_correct(s5b)); + assert(is_string_asan_correct(s5c)); + assert(is_string_asan_correct(s5d)); + assert(is_string_asan_correct(s5e)); + assert(is_string_asan_correct(s5f)); + assert(is_string_asan_correct(s1c)); + assert(is_string_asan_correct(s2c)); + assert(is_string_asan_correct(s3c)); + + S s6a, s6b(s_init), s6c(s_long_init); + s6a = Val; + s6b = Val; + s6c = Val; + assert(is_string_asan_correct(s6a)); + assert(is_string_asan_correct(s6b)); + assert(is_string_asan_correct(s6c)); + +#if TEST_STD_VER >= 11 + S s7a(s_init), s7b(s_long_init), s7c(s_init), s7d(s_long_init2); + s7a = {Val}; + s7b = {Val}; + s7c = {Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, + Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val}; + s7d = {Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, + Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val}; + + assert(s7c.size() > sizeof(void*) * 3); + + assert(is_string_asan_correct(s7a)); + assert(is_string_asan_correct(s7b)); + assert(is_string_asan_correct(s7c)); + assert(is_string_asan_correct(s7d)); +#endif +} + +template +void test_assign( + const CharT Val, const CharT* s_init, const CharT* s_init2, const CharT* s_long_init, const CharT* s_long_init2) { + typedef std::basic_string S; + + const CharT* ptr1 = s_init; + const CharT* ptr2 = s_long_init; + const CharT* ptr3 = s_init2; + const CharT* ptr4 = s_long_init2; + + size_t size1 = strlen(ptr1); + size_t size2 = strlen(ptr2); + size_t size3 = strlen(ptr3); + size_t size4 = strlen(ptr4); + + S s1a(s_init2), s1b(s_long_init2), s1c(s_long_init), s1d(s_init); + s1a.assign(ptr1, ptr1 + size1); + s1b.assign(ptr2, ptr2 + size2); + s1c.assign(ptr3, size3); + s1d.assign(ptr4, size4); + + assert(is_string_asan_correct(s1a)); + assert(is_string_asan_correct(s1b)); + assert(is_string_asan_correct(s1c)); + assert(is_string_asan_correct(s1d)); + + S s2a(ptr4), s2b(ptr3), s2c(s1a), s2d(s1b); + s2a.assign(s1a); + s2b.assign(s1b); + s2c.assign(s1c, 1, 1); + s2d.assign(s1d, 2); + + assert(is_string_asan_correct(s2a)); + assert(is_string_asan_correct(s2b)); + assert(is_string_asan_correct(s2c)); + assert(is_string_asan_correct(s2d)); + + assert(is_string_asan_correct(s1a)); + assert(is_string_asan_correct(s1b)); + + s2c.assign(ptr4); + s2d.assign(ptr3); + + assert(is_string_asan_correct(s2c)); + assert(is_string_asan_correct(s2d)); + + s2c.assign(ptr2); + s2d.assign(ptr1); + + assert(is_string_asan_correct(s2c)); + assert(is_string_asan_correct(s2d)); + + S s3a, s3b; + s3a.assign(s1a.data(), s1a.data() + s1a.size()); + s3b.assign(s1b.data(), s1b.data() + s1b.size()); + + assert(is_string_asan_correct(s3a)); + assert(is_string_asan_correct(s3b)); + + S s4; + s4.assign(s1a + s1b); + + assert(is_string_asan_correct(s4)); + + assert(is_string_asan_correct(s1a)); + assert(is_string_asan_correct(s1b)); + + S s5a, s5b, s5c(ptr1), s5d(ptr2); + s5a.assign(std::move(s1c)); + s5b.assign(std::move(s1d)); + + assert(is_string_asan_correct(s5a)); + assert(is_string_asan_correct(s5b)); + + assert(is_string_asan_correct(s1c)); + assert(is_string_asan_correct(s1d)); + + s5c.assign(std::move(s5a)); + s5d.assign(std::move(s5b)); + + assert(is_string_asan_correct(s5a)); + assert(is_string_asan_correct(s5b)); + assert(is_string_asan_correct(s5c)); + assert(is_string_asan_correct(s5d)); + + S s6a, s6b, s6c; + s6a.assign(ptr1); + s6b.assign(ptr2); + s6c.assign(s_init2); + + assert(is_string_asan_correct(s6a)); + assert(is_string_asan_correct(s6b)); + assert(is_string_asan_correct(s6c)); + + s6a.assign(ptr2, 26); + s6b.assign(ptr1, 1); + s6c.assign(s_init2, 3); + + assert(is_string_asan_correct(s6a)); + assert(is_string_asan_correct(s6b)); + assert(is_string_asan_correct(s6c)); +#if TEST_STD_VER >= 11 + S s7a(s_init), s7b(s_long_init), s7c(s_init), s7d(s_long_init2); + s7a.assign({Val}); + s7b.assign({Val}); + s7c.assign({Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, + Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val}); + s7d.assign({Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, + Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val}); + + assert(s7c.size() > sizeof(void*) * 3); + + assert(is_string_asan_correct(s7a)); + assert(is_string_asan_correct(s7b)); + assert(is_string_asan_correct(s7c)); + assert(is_string_asan_correct(s7d)); +#endif + + S s8a, s8b, s8c, s8d; +#if TEST_STD_VER >= 11 + s8a.assign(s1a.cbegin(), s1a.cend()); + s8b.assign(s1b.cbegin(), s1b.cend()); + s8c.assign(s1a.crbegin(), s1a.crend()); + s8d.assign(s1b.crbegin(), s1b.crend()); +#else + s8a.assign(s1a.begin(), s1a.end()); + s8b.assign(s1b.begin(), s1b.end()); + s8c.assign(s1a.rbegin(), s1a.rend()); + s8d.assign(s1b.rbegin(), s1b.rend()); +#endif + + assert(is_string_asan_correct(s8a)); + assert(is_string_asan_correct(s8b)); + assert(is_string_asan_correct(s8c)); + assert(is_string_asan_correct(s8d)); + + assert(is_string_asan_correct(s1a)); + assert(is_string_asan_correct(s1b)); + + S& s9a = s1a; + S& s9b = s1b; +#if TEST_STD_VER >= 11 + s9a.assign(s1a.cbegin() + 1, s1a.cend()); + s9b.assign(s1b.cbegin() + 4, s1b.cbegin() + 10); +#else + s9a.assign(s1a.begin() + 1, s1a.end()); + s9b.assign(s1b.begin() + 4, s1b.begin() + 10); +#endif + + assert(is_string_asan_correct(s9a)); + assert(is_string_asan_correct(s9b)); + + assert(is_string_asan_correct(s1a)); + assert(is_string_asan_correct(s1b)); + + S s10a(ptr1), s10b(ptr2), s10c(ptr3), s10d(ptr4); + s2a.assign(3, Val); + s2b.assign(300, Val); + s2c.assign(300, Val); + s2d.assign(3, Val); + + assert(is_string_asan_correct(s10a)); + assert(is_string_asan_correct(s10b)); + assert(is_string_asan_correct(s10c)); + assert(is_string_asan_correct(s10d)); +} + +template +void test_view() { + typedef std::basic_string S; + typedef std::basic_string_view SV; + S empty_s; + S short_s(4, 'b'); + S long_s(1100, 'a'); + + SV empty_v(empty_s); + SV short_v(short_s); + SV long_v(long_s); + + assert(is_string_asan_correct(empty_s)); + assert(is_string_asan_correct(short_s)); + assert(is_string_asan_correct(long_s)); + + SV partial_short_v(short_s.data() + 1, 1); + SV partial_long_v(long_s.data() + 10, 200); + + assert(is_string_asan_correct(short_s)); + assert(is_string_asan_correct(long_s)); + + // assign + + S empty_s2a(empty_s), short_s2a(short_s), long_s2a(long_s); + empty_s2a.assign(empty_v); + short_s2a.assign(short_v); + long_s2a.assign(long_v); + + assert(is_string_asan_correct(empty_s2a)); + assert(is_string_asan_correct(short_s2a)); + assert(is_string_asan_correct(long_s2a)); + + S partial_short_s2, partial_long_s2; + partial_short_s2.assign(partial_short_v); + partial_long_s2.assign(partial_long_v); + + assert(is_string_asan_correct(partial_short_s2)); + assert(is_string_asan_correct(partial_long_s2)); + + S empty_s2b(empty_s), short_s2b(short_s), long_s2b(long_s); + empty_s2b.assign(empty_v, 0, 0); + short_s2b.assign(short_v, 1, 2); + long_s2b.assign(long_v, 4, 280); + + assert(is_string_asan_correct(empty_s2b)); + assert(is_string_asan_correct(short_s2b)); + assert(is_string_asan_correct(long_s2b)); + + // operator= + + S empty_s3, short_s3, long_s3; + empty_s3 = empty_v; + short_s3 = short_v; + long_s3 = long_v; + + assert(is_string_asan_correct(empty_s3)); + assert(is_string_asan_correct(short_s3)); + assert(is_string_asan_correct(long_s3)); + + empty_s3 = short_v; + short_s3 = long_v; + long_s3 = empty_v; + + assert(is_string_asan_correct(empty_s3)); + assert(is_string_asan_correct(short_s3)); + assert(is_string_asan_correct(long_s3)); + + S partial_short_s3, partial_long_s3; + partial_short_s3 = partial_short_v; + partial_long_s3 = partial_long_v; + + assert(is_string_asan_correct(partial_short_s3)); + assert(is_string_asan_correct(partial_long_s3)); +} + +template +void test( + const CharT Val, const CharT* s_init, const CharT* s_init2, const CharT* s_long_init, const CharT* s_long_init2) { + test_eq(Val, s_init, s_init2, s_long_init, s_long_init2); + test_assign(Val, s_init, s_init2, s_long_init, s_long_init2); + test_view(); +} + +char literal[] = "ab"; +wchar_t wliteral[] = L"ab"; +#if TEST_STD_VER >= 11 +char16_t literal16[] = u"ab"; +char32_t literal32[] = U"ab"; +#endif +#if TEST_STD_VER >= 20 +char8_t literal8[] = u8"ab"; +#endif + +char literal_2[] = "abc"; +wchar_t wliteral_2[] = L"abc"; +#if TEST_STD_VER >= 11 +char16_t literal16_2[] = u"abc"; +char32_t literal32_2[] = U"abc"; +#endif +#if TEST_STD_VER >= 20 +char8_t literal8_2[] = u8"abc"; +#endif + +char literal_long[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +wchar_t wliteral_long[] = L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +#if TEST_STD_VER >= 11 +char16_t literal16_long[] = u"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +char32_t literal32_long[] = U"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +#endif +#if TEST_STD_VER >= 20 +char8_t literal8_long[] = u8"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +#endif + +char literal_long2[] = + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +wchar_t wliteral_long2[] = + L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +#if TEST_STD_VER >= 11 +char16_t literal16_long2[] = + u"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +char32_t literal32_long2[] = + U"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +#endif +#if TEST_STD_VER >= 20 +char8_t literal8_long2[] = + u8"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +#endif + +int main(int, char**) { + { + typedef char CharT; + test('x', literal, literal_2, literal_long, literal_long2); + } + { + typedef wchar_t CharT; + test(L'x', wliteral, wliteral_2, wliteral_long, wliteral_long2); + } +#if TEST_STD_VER >= 11 + { + typedef char16_t CharT; + test(u'x', literal16, literal16_2, literal16_long, literal16_long2); + } + { + typedef char32_t CharT; + test(U'x', literal32, literal32_2, literal32_long, literal32_long2); + } +#endif +#if TEST_STD_VER >= 20 + { + typedef char8_t CharT; + test(u8'x', literal8, literal8_2, literal8_long, literal8_long2); + } +#endif + + return 0; +} diff --git a/libcxx/test/std/strings/basic.string/string.asan/capacity.pass.cpp b/libcxx/test/std/strings/basic.string/string.asan/capacity.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/strings/basic.string/string.asan/capacity.pass.cpp @@ -0,0 +1,80 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// + +#include +#include + +#include "test_macros.h" +#include "asan_testing.h" + +template +void test() { + S short_s1(3, 'a'), long_s1(100, 'c'); + short_s1.reserve(0x1337); + long_s1.reserve(0x1337); + + assert(is_string_asan_correct(short_s1)); + assert(is_string_asan_correct(long_s1)); +#if TEST_STD_VER >= 11 + short_s1.shrink_to_fit(); + long_s1.shrink_to_fit(); + + assert(is_string_asan_correct(short_s1)); + assert(is_string_asan_correct(long_s1)); +#endif + short_s1.clear(); + long_s1.clear(); + + assert(is_string_asan_correct(short_s1)); + assert(is_string_asan_correct(long_s1)); +#if TEST_STD_VER >= 11 + short_s1.shrink_to_fit(); + long_s1.shrink_to_fit(); + + assert(is_string_asan_correct(short_s1)); + 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); + + assert(is_string_asan_correct(short_s2)); + assert(is_string_asan_correct(long_s2)); +} + +int main(int, char**) { + { + typedef std::string S; + test(); + } + + { + typedef std::wstring S; + test(); + } +#if TEST_STD_VER >= 11 + { + typedef std::u16string S; + test(); + } + { + typedef std::u32string S; + test(); + } +#endif +#if TEST_STD_VER >= 20 + { + typedef std::u8string S; + test(); + } +#endif + + return 0; +} diff --git a/libcxx/test/std/strings/basic.string/string.asan/concatenate.pass.cpp b/libcxx/test/std/strings/basic.string/string.asan/concatenate.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/strings/basic.string/string.asan/concatenate.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 +// +//===----------------------------------------------------------------------===// + +// + +#include +#include + +#include "test_macros.h" +#include "asan_testing.h" + +template +void test(const CharT* s_init) { + typedef std::basic_string S; + + S empty_s; + S short_s(s_init); + S long_s(1100, 'a'); + + assert(is_string_asan_correct(empty_s + empty_s)); + assert(is_string_asan_correct(empty_s + short_s)); + assert(is_string_asan_correct(empty_s + long_s)); + + assert(is_string_asan_correct(short_s + empty_s)); + assert(is_string_asan_correct(short_s + short_s)); + assert(is_string_asan_correct(short_s + long_s)); + + assert(is_string_asan_correct(long_s + empty_s)); + assert(is_string_asan_correct(long_s + long_s)); + assert(is_string_asan_correct(long_s + long_s)); +} + +char literal[] = "ab"; +wchar_t wliteral[] = L"ab"; +#if TEST_STD_VER >= 11 +char16_t literal16[] = u"ab"; +char32_t literal32[] = U"ab"; +#endif +#if TEST_STD_VER >= 20 +char8_t literal8[] = u8"ab"; +#endif + +int main(int, char**) { + { + typedef char CharT; + test(literal); + } + { + typedef wchar_t CharT; + test(wliteral); + } +#if TEST_STD_VER >= 11 + { + typedef char16_t CharT; + test(literal16); + } + { + typedef char32_t CharT; + test(literal32); + } +#endif +#if TEST_STD_VER >= 20 + { + typedef char8_t CharT; + test(literal8); + } +#endif + + return 0; +} diff --git a/libcxx/test/std/strings/basic.string/string.asan/constructor.pass.cpp b/libcxx/test/std/strings/basic.string/string.asan/constructor.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/strings/basic.string/string.asan/constructor.pass.cpp @@ -0,0 +1,364 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// + +#include +#include +#include + +#include "test_macros.h" +#include "asan_testing.h" + +template +size_t strlen(const CharT* str); + +template <> +size_t strlen(const char* str) { + return std::strlen(str); +} + +template <> +size_t strlen(const wchar_t* str) { + return std::wcslen(str); +} +#if TEST_STD_VER >= 11 +template <> +size_t strlen(const char16_t* str) { + size_t size = 0; + while (str[size] != u'\0') { + size += 1; + } + + return size; +} + +template <> +size_t strlen(const char32_t* str) { + size_t size = 0; + while (str[size] != U'\0') { + size += 1; + } + + return size; +} +#endif +#if TEST_STD_VER >= 20 +template <> +size_t strlen(const char8_t* str) { + size_t size = 0; + while (str[size] != u8'\0') { + size += 1; + } + + return size; +} +#endif + +template +void test_basic(const CharT* s_init, + const CharT* s_init2, + const CharT* s_init3, + const CharT* s_long_init, + const CharT* s_long_init2, + const CharT* s_long_init3) { + typedef std::basic_string S; + + // Allocator + S sa((std::allocator())); + assert(is_string_asan_correct(sa)); + + // Empty string + S s1; + assert(is_string_asan_correct(s1)); + + // + S s2a(s_init), s2b = s_init2, s2c = s_init3; + assert(is_string_asan_correct(s2a)); + assert(is_string_asan_correct(s2b)); + assert(is_string_asan_correct(s2c)); + + S s3a = s_long_init2; + assert(s3a.size() >= 24); + S s3b(s_long_init); + assert(s3b.size() >= 24); + + assert(is_string_asan_correct(s3a)); + assert(is_string_asan_correct(s3b)); + + S s4(s_long_init3); + + assert(s4.size() > 8 * 4); + assert(is_string_asan_correct(s4)); + + S s5a(1, 'a'), s5b(3, 'c'), s5c(1000, 'q'); + + assert(is_string_asan_correct(s5a)); + assert(is_string_asan_correct(s5b)); + assert(is_string_asan_correct(s5c)); + + S s6a(s5c.data() + 3, s5c.data() + 5), s6b(s4.data(), s4.data() + 22), s6c(s5c.data(), s5c.data() + 999); + + assert(is_string_asan_correct(s6a)); + assert(is_string_asan_correct(s6b)); + assert(is_string_asan_correct(s6c)); + + S s7a(s6a, 1), s7b(s6b, 20), s7c(s6c, 300), s7d(s6c, 995); + + assert(is_string_asan_correct(s7a)); + assert(is_string_asan_correct(s7b)); + assert(is_string_asan_correct(s7c)); + assert(is_string_asan_correct(s7d)); + + S s8a(s6a, 1, 1), s8b(s6b, 20, 1), s8c(s6c, 300, 600), s8d(s6c, 995, 2); + + assert(is_string_asan_correct(s8a)); + assert(is_string_asan_correct(s8b)); + assert(is_string_asan_correct(s8c)); + assert(is_string_asan_correct(s8d)); + + assert(strlen(s_long_init) > 34); + S s9a(s_long_init, 0), s9b(s_long_init, 5), s9c(s_long_init, 34); + + assert(is_string_asan_correct(s9a)); + assert(is_string_asan_correct(s9b)); + assert(is_string_asan_correct(s9c)); + +#if TEST_STD_VER >= 11 + const CharT val = s_long_init[0]; + S s10a{val, val, val, val, val, val}; + S s10b{val, val, val, val, val, val, val, val, val, val, val, val, val, val, val, val, + val, val, val, val, val, val, val, val, val, val, val, val, val, val, val, val}; + S s10c({val, val, val}); + assert(is_string_asan_correct(s10a)); + assert(is_string_asan_correct(s10b)); + assert(is_string_asan_correct(s10c)); +#endif +} + +template +S get_s(size_t n, char c = 'a') { + S s; + while (s.size() < n) + s.push_back(c); + + return s; +} + +template +void test_complex() { + S short_s1a(get_s(1)), short_s1b = get_s(1, 'b'); + S long_s1a(get_s(91, 'c')), long_s1b = get_s(91); + + assert(is_string_asan_correct(short_s1a)); + assert(is_string_asan_correct(short_s1b)); + assert(is_string_asan_correct(long_s1a)); + assert(is_string_asan_correct(long_s1b)); + + S short_s2a(std::move(short_s1a)), short_s2b = std::move(short_s1b); + S long_s2a(std::move(long_s1a)), long_s2b = std::move(long_s1b); + + assert(is_string_asan_correct(short_s2a)); + assert(is_string_asan_correct(short_s2b)); + assert(is_string_asan_correct(long_s2a)); + assert(is_string_asan_correct(long_s2b)); + + assert(is_string_asan_correct(short_s1a)); + assert(is_string_asan_correct(short_s1b)); + assert(is_string_asan_correct(long_s1a)); + assert(is_string_asan_correct(long_s1b)); + + S short_s3a(short_s2a), short_s3b = short_s2b; + S long_s3a(long_s2a), long_s3b = long_s2b; + + assert(is_string_asan_correct(short_s3a)); + assert(is_string_asan_correct(short_s3b)); + assert(is_string_asan_correct(long_s3a)); + assert(is_string_asan_correct(long_s3b)); + + assert(is_string_asan_correct(short_s2a)); + assert(is_string_asan_correct(short_s2b)); + assert(is_string_asan_correct(long_s2a)); + assert(is_string_asan_correct(long_s2b)); + + typedef typename S::value_type value_type; + value_type a = value_type('a'); + + S short_s4(1, a); + S long_s4(91, a); + + assert(is_string_asan_correct(short_s4)); + assert(is_string_asan_correct(long_s4)); + + S short_s5(long_s4.begin(), long_s4.begin() + 1); + S long_s5(long_s4.rbegin(), long_s4.rbegin() + 81); + + assert(is_string_asan_correct(short_s5)); + assert(is_string_asan_correct(long_s5)); + assert(is_string_asan_correct(long_s4)); + +#if TEST_STD_VER >= 11 + S short_s6({short_s5[0]}); + S long_s6({ + long_s5[0], long_s5[1], long_s5[2], long_s5[3], long_s5[4], long_s5[5], long_s5[6], long_s5[7], + long_s5[8], long_s5[9], long_s5[0], long_s5[1], long_s5[2], long_s5[3], long_s5[4], long_s5[5], + long_s5[6], long_s5[7], long_s5[1], long_s5[2], long_s5[3], long_s5[4], long_s5[5], long_s5[6], + }); + + assert(is_string_asan_correct(short_s6)); + assert(is_string_asan_correct(long_s6)); + assert(is_string_asan_correct(short_s5)); + assert(is_string_asan_correct(long_s5)); +#endif +} + +template +void test_view() { + typedef std::basic_string S; + typedef std::basic_string_view SV; + S empty_s; + S short_s(3, 'b'); + S long_s(1100, 'a'); + + SV empty_v(empty_s); + SV short_v(short_s); + SV long_v = long_s; + + assert(is_string_asan_correct(empty_s)); + assert(is_string_asan_correct(short_s)); + assert(is_string_asan_correct(long_s)); + + SV partial_short_v(short_s.data() + 1, 1); + SV partial_long_v(long_s.data() + 10, 200); + + assert(is_string_asan_correct(short_s)); + assert(is_string_asan_correct(long_s)); + + S empty_sb(empty_v); + S short_sb(short_v); + S long_sb(long_v); + + assert(is_string_asan_correct(empty_sb)); + assert(is_string_asan_correct(short_sb)); + assert(is_string_asan_correct(long_sb)); + + S partial_short_s(partial_short_v); + S partial_long_s(partial_long_v); + S partial_s(long_v, 10, 30); + + assert(is_string_asan_correct(partial_short_s)); + assert(is_string_asan_correct(partial_long_s)); + assert(is_string_asan_correct(partial_s)); +} + +template +void test(const CharT* s_init, + const CharT* s_init2, + const CharT* s_init3, + const CharT* s_long_init, + const CharT* s_long_init2, + const CharT* s_long_init3) { + test_basic(s_init, s_init2, s_init3, s_long_init, s_long_init2, s_long_init3); + typedef std::basic_string S; + test_complex(); + test_view(); +} + +char literal[] = "a"; +wchar_t wliteral[] = L"a"; +#if TEST_STD_VER >= 11 +char16_t literal16[] = u"a"; +char32_t literal32[] = U"a"; +#endif +#if TEST_STD_VER >= 20 +char8_t literal8[] = u8"a"; +#endif + +char literal_2[] = "ab"; +wchar_t wliteral_2[] = L"ab"; +#if TEST_STD_VER >= 11 +char16_t literal16_2[] = u"ab"; +char32_t literal32_2[] = U"ab"; +#endif +#if TEST_STD_VER >= 20 +char8_t literal8_2[] = u8"ab"; +#endif + +char literal_3[] = "abc"; +wchar_t wliteral_3[] = L"abc"; +#if TEST_STD_VER >= 11 +char16_t literal16_3[] = u"abc"; +char32_t literal32_3[] = U"abc"; +#endif +#if TEST_STD_VER >= 20 +char8_t literal8_3[] = u8"abc"; +#endif + +char literal_long[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +wchar_t wliteral_long[] = L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +#if TEST_STD_VER >= 11 +char16_t literal16_long[] = u"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +char32_t literal32_long[] = U"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +#endif +#if TEST_STD_VER >= 20 +char8_t literal8_long[] = u8"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +#endif + +char literal_long2[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; +wchar_t wliteral_long2[] = L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; +#if TEST_STD_VER >= 11 +char16_t literal16_long2[] = u"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; +char32_t literal32_long2[] = U"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; +#endif +#if TEST_STD_VER >= 20 +char8_t literal8_long2[] = u8"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; +#endif + +char literal_long3[] = + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +wchar_t wliteral_long3[] = + L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +#if TEST_STD_VER >= 11 +char16_t literal16_long3[] = + u"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +char32_t literal32_long3[] = + U"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +#endif +#if TEST_STD_VER >= 20 +char8_t literal8_long3[] = + u8"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +#endif + +int main(int, char**) { + { + typedef char CharT; + test(literal, literal_2, literal_3, literal_long, literal_long2, literal_long3); + } + { + typedef wchar_t CharT; + test(wliteral, wliteral_2, wliteral_3, wliteral_long, wliteral_long2, wliteral_long3); + } +#if TEST_STD_VER >= 11 + { + typedef char16_t CharT; + test(literal16, literal16_2, literal16_3, literal16_long, literal16_long2, literal16_long3); + } + { + typedef char32_t CharT; + test(literal32, literal32_2, literal32_3, literal32_long, literal32_long2, literal32_long3); + } +#endif +#if TEST_STD_VER >= 20 + { + typedef char8_t CharT; + test(literal8, literal8_2, literal8_3, literal8_long, literal8_long2, literal8_long3); + } +#endif + + return 0; +} diff --git a/libcxx/test/std/strings/basic.string/string.asan/erase.pass.cpp b/libcxx/test/std/strings/basic.string/string.asan/erase.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/strings/basic.string/string.asan/erase.pass.cpp @@ -0,0 +1,97 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// + +#include +#include + +#include "test_macros.h" +#include "asan_testing.h" + +template +void test() { + S short_s, long_s; + + for (size_t i = 0; i < 1000; ++i) { + long_s.push_back('a' + (i % 3)); + + if (i < 6) + short_s.push_back('a' + (i % 3)); + } + + S short_s2(short_s), long_s2(long_s); + short_s2.erase(short_s2.begin()); + long_s2.erase(long_s2.begin()); + assert(is_string_asan_correct(short_s2)); + assert(is_string_asan_correct(long_s2)); + + short_s2.erase(short_s2.begin() + 1); + long_s2.erase(long_s2.begin() + 1); + assert(is_string_asan_correct(short_s2)); + assert(is_string_asan_correct(long_s2)); + + short_s2.erase(1, 2); + long_s2.erase(30, 200); + assert(is_string_asan_correct(short_s2)); + assert(is_string_asan_correct(long_s2)); + + short_s2.erase(1); + long_s2.erase(5); + assert(is_string_asan_correct(short_s2)); + assert(is_string_asan_correct(long_s2)); + + short_s2.erase(); + long_s2.erase(); + assert(is_string_asan_correct(short_s2)); + assert(is_string_asan_correct(long_s2)); + + S short_s3(short_s), long_s3(long_s); + short_s3.erase(short_s3.begin() + 1, short_s3.end() - 1); + long_s3.erase(long_s3.begin() + 100, long_s3.end() - 100); + assert(is_string_asan_correct(short_s3)); + assert(is_string_asan_correct(long_s3)); + + long_s3.erase(long_s3.begin() + 20, long_s3.end() - 20); + assert(is_string_asan_correct(long_s3)); + + short_s3.erase(); + long_s3.erase(); + assert(is_string_asan_correct(short_s3)); + assert(is_string_asan_correct(long_s3)); +} + +int main(int, char**) { + { + typedef std::string S; + test(); + } + + { + typedef std::wstring S; + test(); + } +#if TEST_STD_VER >= 11 + { + typedef std::u16string S; + test(); + } + { + typedef std::u32string S; + test(); + } +#endif +#if TEST_STD_VER >= 20 + { + typedef std::u8string S; + test(); + } +#endif + + return 0; +} diff --git a/libcxx/test/std/strings/basic.string/string.asan/insert.pass.cpp b/libcxx/test/std/strings/basic.string/string.asan/insert.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/strings/basic.string/string.asan/insert.pass.cpp @@ -0,0 +1,419 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// + +#include +#include +#include + +#include "test_macros.h" +#include "asan_testing.h" + +template +size_t strlen(const CharT* str); + +template <> +size_t strlen(const char* str) { + return std::strlen(str); +} + +template <> +size_t strlen(const wchar_t* str) { + return std::wcslen(str); +} +#if TEST_STD_VER >= 11 +template <> +size_t strlen(const char16_t* str) { + size_t size = 0; + while (str[size] != u'\0') { + size += 1; + } + + return size; +} + +template <> +size_t strlen(const char32_t* str) { + size_t size = 0; + while (str[size] != U'\0') { + size += 1; + } + + return size; +} +#endif +#if TEST_STD_VER >= 20 +template <> +size_t strlen(const char8_t* str) { + size_t size = 0; + while (str[size] != u8'\0') { + size += 1; + } + + return size; +} +#endif + +template +void test_simple_insert(const CharT* s_init, const CharT* s_long_init) { + typedef std::basic_string S; + const CharT val = s_init[0], val2 = s_long_init[1]; + + S short_s(2, val); + S long_s(1100, val2); + + short_s.insert(1, 2, val); + long_s.insert(10, 9, val2); + assert(is_string_asan_correct(short_s)); + assert(is_string_asan_correct(long_s)); + +#if TEST_STD_VER >= 11 + short_s.insert(short_s.cbegin() + 2, val); + long_s.insert(long_s.cbegin() + 22, val); +#else + short_s.insert(short_s.begin() + 2, val); + long_s.insert(long_s.begin() + 22, val); +#endif + + assert(is_string_asan_correct(short_s)); + assert(is_string_asan_correct(long_s)); + + S short_s2(short_s), long_s2(long_s); +#if TEST_STD_VER >= 11 + short_s2.insert(short_s2.cbegin() + 2, 128, val); + long_s2.insert(long_s2.cbegin() + 22, 128, val); +#else + short_s2.insert(short_s2.begin() + 2, 128, val); + long_s2.insert(long_s2.begin() + 22, 128, val); +#endif + + assert(is_string_asan_correct(short_s2)); + assert(is_string_asan_correct(long_s2)); + +#if TEST_STD_VER >= 14 + short_s.insert(2, s_init, 2, 2); + long_s.insert(2, s_long_init, 22, 2); + + assert(is_string_asan_correct(short_s)); + assert(is_string_asan_correct(long_s)); + + short_s.insert(2, 1000, val2); + + assert(is_string_asan_correct(short_s)); +#endif +} + +template +void test_insert( + const CharT Val, const CharT* s_init, const CharT* s_init2, const CharT* s_long_init, const CharT* s_long_init2) { + typedef std::basic_string S; + + const CharT* ptr1 = s_init; + const CharT* ptr2 = s_long_init; + const CharT* ptr3 = s_init2; + const CharT* ptr4 = s_long_init2; + + size_t size1 = strlen(ptr1); + size_t size2 = strlen(ptr2); + size_t size3 = strlen(ptr3); + size_t size4 = strlen(ptr4); + + S s1a(s_init2), s1b(s_long_init2), s1c(s_long_init), s1d(s_init); + s1a.insert(s1a.begin() + 1, ptr1, ptr1 + size1); + s1b.insert(s1b.begin() + 9, ptr2, ptr2 + size2); + s1c.insert(8, ptr3, size3); + s1d.insert(1, ptr4, size4); + + assert(is_string_asan_correct(s1a)); + assert(is_string_asan_correct(s1b)); + assert(is_string_asan_correct(s1c)); + assert(is_string_asan_correct(s1d)); + + S s2a(ptr4), s2b(ptr3), s2c(s1a), s2d(s1b); + s2a.insert(1, s1a); + s2b.insert(1, s1b); + s2c.insert(1, s1c, 1, 1); + s2d.insert(1, s1d, 2); + + assert(is_string_asan_correct(s2a)); + assert(is_string_asan_correct(s2b)); + assert(is_string_asan_correct(s2c)); + assert(is_string_asan_correct(s2d)); + + assert(is_string_asan_correct(s1a)); + assert(is_string_asan_correct(s1b)); + + s2c.insert(1, ptr4); + s2d.insert(1, ptr3); + + assert(is_string_asan_correct(s2c)); + assert(is_string_asan_correct(s2d)); + + s2c.insert(0, ptr2); + s2d.insert(0, ptr1); + + assert(is_string_asan_correct(s2c)); + assert(is_string_asan_correct(s2d)); + + S s3a(s_init2), s3b(s_long_init2); + s3a.insert(s3a.begin() + 1, s1a.data(), s1a.data() + s1a.size() - 1); + s3b.insert(s3b.begin() + 1, s1b.data(), s1b.data() + s1b.size() - 1); + + assert(is_string_asan_correct(s3a)); + assert(is_string_asan_correct(s3b)); + + S s4(s_long_init); + s4.insert(1, s1a + s1b); + + assert(is_string_asan_correct(s4)); + + assert(is_string_asan_correct(s1a)); + assert(is_string_asan_correct(s1b)); + + S s5a, s5b(s_init2), s5c(ptr1), s5d(ptr2); + s5a.insert(0, std::move(s1c)); + s5b.insert(s5b.size(), std::move(s1d)); + + assert(is_string_asan_correct(s5a)); + assert(is_string_asan_correct(s5b)); + + assert(is_string_asan_correct(s1c)); + assert(is_string_asan_correct(s1d)); + + s5c.insert(1, std::move(s5a)); + s5d.insert(1, std::move(s5b)); + + assert(is_string_asan_correct(s5a)); + assert(is_string_asan_correct(s5b)); + assert(is_string_asan_correct(s5c)); + assert(is_string_asan_correct(s5d)); + + S s6a, s6b, s6c; + s6a.insert(0, ptr1); + s6b.insert(0, ptr2); + s6c.insert(s6c.size(), s_init2); + + assert(is_string_asan_correct(s6a)); + assert(is_string_asan_correct(s6b)); + assert(is_string_asan_correct(s6c)); + + s6a.insert(1, ptr2, 26); + s6b.insert(1, ptr1, 1); + s6c.insert(1, s_init2, 3); + + assert(is_string_asan_correct(s6a)); + assert(is_string_asan_correct(s6b)); + assert(is_string_asan_correct(s6c)); +#if TEST_STD_VER >= 11 + S s7a(s_init), s7b(s_long_init), s7c(s_init), s7d(s_long_init2); + s7a.insert(s7a.begin() + 1, {Val}); + s7b.insert(s7b.begin() + 1, {Val}); + s7c.insert(s7c.begin() + 1, + {Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, + Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val}); + s7d.insert(s7d.begin() + 1, + {Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, + Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val, Val}); + + assert(s7c.size() > sizeof(void*) * 3); + + assert(is_string_asan_correct(s7a)); + assert(is_string_asan_correct(s7b)); + assert(is_string_asan_correct(s7c)); + assert(is_string_asan_correct(s7d)); +#endif + + S s8a(ptr1), s8b(ptr2), s8c(ptr4), s8d(ptr3); +#if TEST_STD_VER >= 11 + s8a.insert(s8a.begin() + 1, s1a.cbegin(), s1a.cend()); + s8b.insert(s8b.begin() + 1, s1b.cbegin(), s1b.cend()); + s8c.insert(s8c.begin() + 1, s1a.crbegin(), s1a.crend()); + s8d.insert(s8d.begin() + 1, s1b.crbegin(), s1b.crend()); +#else + s8a.insert(s8a.begin() + 1, s1a.begin(), s1a.end()); + s8b.insert(s8b.begin() + 1, s1b.begin(), s1b.end()); + s8c.insert(s8c.begin() + 1, s1a.rbegin(), s1a.rend()); + s8d.insert(s8d.begin() + 1, s1b.rbegin(), s1b.rend()); +#endif + + assert(is_string_asan_correct(s8a)); + assert(is_string_asan_correct(s8b)); + assert(is_string_asan_correct(s8c)); + assert(is_string_asan_correct(s8d)); + + assert(is_string_asan_correct(s1a)); + assert(is_string_asan_correct(s1b)); + + S &s9a = s1a, s9b = s1b; +#if TEST_STD_VER >= 11 + s9a.insert(s9a.end() - 1, s1a.cbegin() + 1, s1a.cend()); + s9b.insert(s9b.end() - 1, s1b.cbegin() + 4, s1b.cbegin() + 10); +#else + s9a.insert(s9a.end() - 1, s1a.begin() + 1, s1a.end()); + s9b.insert(s9b.end() - 1, s1b.begin() + 4, s1b.begin() + 10); +#endif + + assert(is_string_asan_correct(s9a)); + assert(is_string_asan_correct(s9b)); + + assert(is_string_asan_correct(s1a)); + assert(is_string_asan_correct(s1b)); + + S s10a(ptr1), s10b(ptr2), s10c(ptr3), s10d(ptr4); + s2a.insert(1, 3, Val); + s2b.insert(10, 300, Val); + s2c.insert(10, 300, Val); + s2d.insert(1, 3, Val); + + assert(is_string_asan_correct(s10a)); + assert(is_string_asan_correct(s10b)); + assert(is_string_asan_correct(s10c)); + assert(is_string_asan_correct(s10d)); +} + +template +void test_view() { + typedef std::basic_string S; + typedef std::basic_string_view SV; + S empty_s; + S short_s(4, 'b'); + S long_s(1100, 'a'); + + SV empty_v(empty_s); + SV short_v(short_s); + SV long_v(long_s); + + assert(is_string_asan_correct(empty_s)); + assert(is_string_asan_correct(short_s)); + assert(is_string_asan_correct(long_s)); + + SV partial_short_v(short_s.data() + 1, 1); + SV partial_long_v(long_s.data() + 10, 200); + + assert(is_string_asan_correct(short_s)); + assert(is_string_asan_correct(long_s)); + + // insert + + S empty_s2a(empty_v), short_s2a(short_v), long_s2a(long_v); + empty_s2a.insert(0, empty_v); + short_s2a.insert(1, short_v); + long_s2a.insert(99, long_v); + + assert(is_string_asan_correct(empty_s2a)); + assert(is_string_asan_correct(short_s2a)); + assert(is_string_asan_correct(long_s2a)); + + S partial_short_s2(short_s), partial_long_s2(long_s); + partial_short_s2.insert(1, partial_short_v); + partial_long_s2.insert(99, partial_long_v); + + assert(is_string_asan_correct(partial_short_s2)); + assert(is_string_asan_correct(partial_long_s2)); + + S empty_s2b(long_s), short_s2b(short_s), long_s2b(long_s); + empty_s2b.insert(0, empty_v, 0, 0); + short_s2b.insert(1, short_v, 1, 2); + long_s2b.insert(99, long_v, 4, 280); + + assert(is_string_asan_correct(empty_s2b)); + assert(is_string_asan_correct(short_s2b)); + assert(is_string_asan_correct(long_s2b)); + + empty_s2b.insert(0, empty_v, 0); + short_s2b.insert(1, short_v, 1); + long_s2b.insert(99, long_v, 4); + + assert(is_string_asan_correct(empty_s2b)); + assert(is_string_asan_correct(short_s2b)); + assert(is_string_asan_correct(long_s2b)); +} + +template +void test( + const CharT Val, const CharT* s_init, const CharT* s_init2, const CharT* s_long_init, const CharT* s_long_init2) { + test_simple_insert(s_init, s_long_init2); + test_insert(Val, s_init, s_init2, s_long_init, s_long_init2); + test_view(); +} + +char literal[] = "ab"; +wchar_t wliteral[] = L"ab"; +#if TEST_STD_VER >= 11 +char16_t literal16[] = u"ab"; +char32_t literal32[] = U"ab"; +#endif +#if TEST_STD_VER >= 20 +char8_t literal8[] = u8"ab"; +#endif + +char literal_2[] = "abc"; +wchar_t wliteral_2[] = L"abc"; +#if TEST_STD_VER >= 11 +char16_t literal16_2[] = u"abc"; +char32_t literal32_2[] = U"abc"; +#endif +#if TEST_STD_VER >= 20 +char8_t literal8_2[] = u8"abc"; +#endif + +char literal_long[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +wchar_t wliteral_long[] = L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +#if TEST_STD_VER >= 11 +char16_t literal16_long[] = u"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +char32_t literal32_long[] = U"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +#endif +#if TEST_STD_VER >= 20 +char8_t literal8_long[] = u8"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +#endif + +char literal_long2[] = + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +wchar_t wliteral_long2[] = + L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +#if TEST_STD_VER >= 11 +char16_t literal16_long2[] = + u"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +char32_t literal32_long2[] = + U"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +#endif +#if TEST_STD_VER >= 20 +char8_t literal8_long2[] = + u8"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +#endif + +int main(int, char**) { + { + typedef char CharT; + test('x', literal, literal_2, literal_long, literal_long2); + } + { + typedef wchar_t CharT; + test(L'x', wliteral, wliteral_2, wliteral_long, wliteral_long2); + } +#if TEST_STD_VER >= 11 + { + typedef char16_t CharT; + test(u'x', literal16, literal16_2, literal16_long, literal16_long2); + } + { + typedef char32_t CharT; + test(U'x', literal32, literal32_2, literal32_long, literal32_long2); + } +#endif +#if TEST_STD_VER >= 20 + { + typedef char8_t CharT; + test(u8'x', literal8, literal8_2, literal8_long, literal8_long2); + } +#endif + + return 0; +} \ No newline at end of file diff --git a/libcxx/test/std/strings/basic.string/string.asan/nostd_allocator.pass.cpp b/libcxx/test/std/strings/basic.string/string.asan/nostd_allocator.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/strings/basic.string/string.asan/nostd_allocator.pass.cpp @@ -0,0 +1,32 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// + +#include +#include + +#include "test_macros.h" +#include "min_allocator.h" +#include "asan_testing.h" + +int main(int, char**) { +#if TEST_STD_VER >= 11 + { + // ASan is turned off for non standard allocator. + // It is sanitycheck. + typedef std::basic_string, min_allocator> S; + S s; + // is_string_asan_correct should always return true for non-standard + // allocator. + assert(is_string_asan_correct(s)); + } +#endif + + return 0; +} diff --git a/libcxx/test/std/strings/basic.string/string.asan/packed.pass.cpp b/libcxx/test/std/strings/basic.string/string.asan/packed.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/strings/basic.string/string.asan/packed.pass.cpp @@ -0,0 +1,42 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// + +#include +#include +#include + +#include "test_macros.h" +#include "asan_testing.h" + +#if TEST_HAS_FEATURE(address_sanitizer) +extern "C" bool __sanitizer_is_annotable(const void* address, const unsigned long size); +#endif + +class __attribute__((packed)) X { +public: + char x[4]; + std::string s; +}; + +int main(int, char**) { +#if TEST_HAS_FEATURE(address_sanitizer) + { + X x; + // Because we use attribute packed, memory should + // not be aligned, and therefore x.s should not + // be annotated when Short String Optimization is used. + if (!__sanitizer_is_annotable(&x.s, sizeof(x.s))) + for (size_t i = 0; i < x.s.capacity(); ++i) + x.s[i] = 'a' + i; // Memory should not be poisoned + } +#endif + + return 0; +} diff --git a/libcxx/test/std/strings/basic.string/string.asan/push_pop_back.pass.cpp b/libcxx/test/std/strings/basic.string/string.asan/push_pop_back.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/strings/basic.string/string.asan/push_pop_back.pass.cpp @@ -0,0 +1,63 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// + +#include +#include + +#include "test_macros.h" +#include "asan_testing.h" + +template +void test(const CharT val) { + typedef std::basic_string S; + + S s; + while (s.size() < 1000000) { + s.push_back(val); + + assert(is_string_asan_correct(s)); + } +#if TEST_STD_VER >= 11 + while (s.size() > 0) { + s.pop_back(); + + assert(is_string_asan_correct(s)); + } +#endif +} + +int main(int, char**) { + { + typedef char CharT; + test('x'); + } + { + typedef wchar_t CharT; + test(L'x'); + } +#if TEST_STD_VER >= 11 + { + typedef char16_t CharT; + test(u'x'); + } + { + typedef char32_t CharT; + test(U'x'); + } +#endif +#if TEST_STD_VER >= 20 + { + typedef char8_t CharT; + test(u8'x'); + } +#endif + + return 0; +} diff --git a/libcxx/test/std/strings/basic.string/string.asan/replace.pass.cpp b/libcxx/test/std/strings/basic.string/string.asan/replace.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/strings/basic.string/string.asan/replace.pass.cpp @@ -0,0 +1,317 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// + +#include +#include + +#include "test_macros.h" +#include "asan_testing.h" + +template +void test_replace_basic(const CharT val) { + typedef std::basic_string S; + + S short_s, long_s, x, one; + + one.push_back('1'); + for (size_t i = 0; i < 1000; ++i) { + long_s.push_back('a' + (i % 3)); + x.push_back('x'); + if (i < 5) + short_s.push_back('a' + (i % 3)); + } + + short_s.replace(1, 2, one.data()); + long_s.replace(20, 20, one.data()); + assert(is_string_asan_correct(short_s)); + assert(is_string_asan_correct(long_s)); + + short_s.replace(short_s.begin(), short_s.begin() + 1, 2, val); + long_s.replace(long_s.begin(), long_s.begin() + 1, 2, val); + + assert(is_string_asan_correct(short_s)); + assert(is_string_asan_correct(long_s)); + + short_s.replace(1, 2, x.data()); + long_s.replace(20, 2, x.data()); + + assert(is_string_asan_correct(short_s)); + assert(is_string_asan_correct(long_s)); + + S short_s2(3, val), long_s2(300, val); + + short_s2.replace(short_s2.begin() + 1, short_s2.begin() + 3, {val}); + long_s2.replace(long_s2.begin() + 1, long_s2.begin() + 3, {val}); + + assert(is_string_asan_correct(short_s2)); + assert(is_string_asan_correct(long_s2)); + + short_s2.push_back(val); + + assert(is_string_asan_correct(short_s2)); + + short_s2.replace(short_s2.begin() + 1, short_s2.begin() + 3, {val, val, val, val}); + long_s2.replace(long_s2.begin() + 1, long_s2.begin() + 3, {val, val, val, val}); + + assert(is_string_asan_correct(short_s2)); + assert(is_string_asan_correct(long_s2)); +} + +template +void test_replace(const CharT* s_init, const CharT* s_long_init) { + typedef std::basic_string S; + + S long_buffer; + while (long_buffer.size() < 190) { + long_buffer.push_back(s_init[0]); + } + + S short_s1a(s_init), long_s1a(s_long_init); + short_s1a.replace(1, 1, long_buffer); + long_s1a.replace(10, 10, long_buffer); + + assert(is_string_asan_correct(short_s1a)); + assert(is_string_asan_correct(long_s1a)); + + S short_s1b(s_init), long_s1b(s_long_init); + short_s1b.replace(short_s1b.begin() + 1, short_s1b.begin() + 2, long_buffer); + long_s1b.replace(long_s1b.begin() + 10, long_s1b.begin() + 20, long_buffer); + + assert(is_string_asan_correct(short_s1b)); + assert(is_string_asan_correct(long_s1b)); + + S short_s2a(s_init), long_s2a(s_long_init); + short_s2a.replace(1, 1, long_buffer, 5, 7); + long_s2a.replace(1, 1, long_buffer, 5, 7); + assert(is_string_asan_correct(short_s2a)); + assert(is_string_asan_correct(long_s2a)); + + short_s2a.replace(1, 1, long_buffer, 5, 77); + long_s2a.replace(1, 10, long_buffer, 5, 7); + assert(is_string_asan_correct(short_s2a)); + assert(is_string_asan_correct(long_s2a)); + + short_s2a.replace(1, 1, long_buffer, 5); + long_s2a.replace(1, 10, long_buffer, 5); + assert(is_string_asan_correct(short_s2a)); + assert(is_string_asan_correct(long_s2a)); + + long_s2a.replace(1, 1, short_s2a, 1, 7000); + assert(is_string_asan_correct(short_s2a)); + assert(is_string_asan_correct(long_s2a)); + + S short_s3a(s_init), long_s3a(s_long_init); + short_s3a.replace(short_s3a.begin() + 1, short_s3a.end(), long_s2a.begin(), long_s2a.begin() + 25); + long_s3a.replace(long_s3a.begin() + 1, long_s3a.begin() + 4, long_s2a.begin(), long_s2a.begin() + 25); + assert(is_string_asan_correct(short_s3a)); + assert(is_string_asan_correct(long_s3a)); + + S short_s4a(s_init), long_s4a(s_long_init); + short_s4a.replace(1, 1, s_long_init, 26); + long_s4a.replace(2, 4, s_long_init, 26); + assert(is_string_asan_correct(short_s4a)); + assert(is_string_asan_correct(long_s4a)); + + S short_s5a(s_init), long_s5a(s_long_init); + short_s5a.replace(1, 1, s_long_init); + long_s5a.replace(2, 4, s_long_init); + assert(is_string_asan_correct(short_s5a)); + assert(is_string_asan_correct(long_s5a)); + + S short_s5b(s_init), long_s5b(s_long_init); + short_s5b.replace(short_s5b.end() - 1, short_s5b.end(), s_long_init); + long_s5b.replace(long_s5b.end() - 4, long_s5b.end() - 2, s_long_init); + assert(is_string_asan_correct(short_s5b)); + assert(is_string_asan_correct(long_s5b)); + + const CharT val = s_init[1]; + S short_s6a(s_init), long_s6a(s_long_init); + short_s6a.replace(1, 1, 128, val); + long_s6a.replace(2, 4, 128, val); + assert(is_string_asan_correct(short_s6a)); + assert(is_string_asan_correct(long_s6a)); + + S short_s6b(s_init), long_s6b(s_long_init); + short_s6b.replace(short_s6b.end() - 1, short_s6b.end(), 1, val); + long_s6b.replace(long_s6b.end() - 4, long_s6b.end() - 2, 1, val); + assert(is_string_asan_correct(short_s6b)); + assert(is_string_asan_correct(long_s6b)); + + short_s6b.replace(short_s6b.end() - 1, short_s6b.end(), 128, val); + long_s6b.replace(long_s6b.end() - 4, long_s6b.end() - 2, 128, val); + assert(is_string_asan_correct(short_s6b)); + assert(is_string_asan_correct(long_s6b)); + + short_s6b.replace(short_s6b.begin() + 1, short_s6b.end() - 1, 1, val); + long_s6b.replace(long_s6b.begin() + 1, long_s6b.end() - 1, 1, val); + assert(is_string_asan_correct(short_s6b)); + assert(is_string_asan_correct(long_s6b)); + + S short_s7a(s_init), long_s7a(s_long_init); + short_s7a.replace(short_s7a.end() - 1, short_s7a.end(), {val, val}); + long_s7a.replace(long_s7a.end() - 4, long_s7a.end() - 2, {val, val}); + assert(is_string_asan_correct(short_s7a)); + assert(is_string_asan_correct(long_s7a)); + + short_s7a.replace(short_s7a.begin() + 1, short_s7a.end() - 1, {val}); + long_s7a.replace(long_s7a.begin() + 1, long_s7a.end() - 1, {val}); + assert(is_string_asan_correct(short_s7a)); + assert(is_string_asan_correct(long_s7a)); + + short_s7a.replace(short_s7a.begin(), short_s7a.end(), {}); + long_s7a.replace(long_s7a.begin(), long_s7a.end(), {}); + assert(is_string_asan_correct(short_s7a)); + assert(is_string_asan_correct(long_s7a)); +} + +template +void test_view() { + typedef std::basic_string S; + typedef std::basic_string_view SV; + S empty_s; + S short_s(4, 'b'); + S long_s(1100, 'a'); + + SV empty_v(empty_s); + SV short_v(short_s); + SV long_v(long_s); + + assert(is_string_asan_correct(empty_s)); + assert(is_string_asan_correct(short_s)); + assert(is_string_asan_correct(long_s)); + + SV partial_short_v(short_s.data() + 1, 1); + SV partial_long_v(long_s.data() + 10, 200); + + assert(is_string_asan_correct(short_s)); + assert(is_string_asan_correct(long_s)); + + // append + + S empty_s2a(empty_s), short_s2a(short_s), long_s2a(long_s); + empty_s2a.replace(0, 0, empty_v); + short_s2a.replace(1, 1, short_v); + long_s2a.replace(11, 5, long_v); + + assert(is_string_asan_correct(empty_s2a)); + assert(is_string_asan_correct(short_s2a)); + assert(is_string_asan_correct(long_s2a)); + + S partial_short_s2(short_s), partial_long_s2(long_s); + partial_short_s2.replace(1, 1, partial_short_v); + partial_long_s2.replace(1, 1, partial_long_v); + + assert(is_string_asan_correct(partial_short_s2)); + assert(is_string_asan_correct(partial_long_s2)); + + S empty_s2b(empty_s), short_s2b(short_s), long_s2b(long_s); + empty_s2b.replace(empty_s2b.begin(), empty_s2b.end(), empty_v); + short_s2b.replace(short_s2b.begin() + 1, short_s2b.end(), short_v); + long_s2b.replace(long_s2b.begin() + 1, long_s2b.end() - 1, long_v); + + assert(is_string_asan_correct(empty_s2b)); + assert(is_string_asan_correct(short_s2b)); + assert(is_string_asan_correct(long_s2b)); + + S empty_s3a(empty_s), short_s3a(short_s), long_s3a(long_s); + empty_s3a.replace(0, 0, long_v, 2, 26); + short_s3a.replace(1, 1, long_v, 2, 25); + long_s3a.replace(11, 5, long_v, 2, 26); + + assert(is_string_asan_correct(empty_s3a)); + assert(is_string_asan_correct(short_s3a)); + assert(is_string_asan_correct(long_s3a)); +} + +template +void test( + const CharT val, const CharT* s_init, const CharT* s_init2, const CharT* s_long_init, const CharT* s_long_init2) { + test_replace_basic(val); + test_replace(s_init, s_long_init); + test_replace(s_init2, s_long_init); + test_replace(s_init, s_long_init2); + test_replace(s_init2, s_long_init2); + test_view(); +} + +char literal[] = "ab"; +wchar_t wliteral[] = L"ab"; +#if TEST_STD_VER >= 11 +char16_t literal16[] = u"ab"; +char32_t literal32[] = U"ab"; +#endif +#if TEST_STD_VER >= 20 +char8_t literal8[] = u8"ab"; +#endif + +char literal_2[] = "abc"; +wchar_t wliteral_2[] = L"abc"; +#if TEST_STD_VER >= 11 +char16_t literal16_2[] = u"abc"; +char32_t literal32_2[] = U"abc"; +#endif +#if TEST_STD_VER >= 20 +char8_t literal8_2[] = u8"abc"; +#endif + +char literal_long[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +wchar_t wliteral_long[] = L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +#if TEST_STD_VER >= 11 +char16_t literal16_long[] = u"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +char32_t literal32_long[] = U"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +#endif +#if TEST_STD_VER >= 20 +char8_t literal8_long[] = u8"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +#endif + +char literal_long2[] = + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +wchar_t wliteral_long2[] = + L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +#if TEST_STD_VER >= 11 +char16_t literal16_long2[] = + u"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +char32_t literal32_long2[] = + U"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +#endif +#if TEST_STD_VER >= 20 +char8_t literal8_long2[] = + u8"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +#endif + +int main(int, char**) { + { + typedef char CharT; + test('x', literal, literal_2, literal_long, literal_long2); + } + { + typedef wchar_t CharT; + test(L'x', wliteral, wliteral_2, wliteral_long, wliteral_long2); + } +#if TEST_STD_VER >= 11 + { + typedef char16_t CharT; + test(u'x', literal16, literal16_2, literal16_long, literal16_long2); + } + { + typedef char32_t CharT; + test(U'x', literal32, literal32_2, literal32_long, literal32_long2); + } +#endif +#if TEST_STD_VER >= 20 + { + typedef char8_t CharT; + test(u8'x', literal8, literal8_2, literal8_long, literal8_long2); + } +#endif + + return 0; +} \ No newline at end of file diff --git a/libcxx/test/std/strings/basic.string/string.asan/substr.pass.cpp b/libcxx/test/std/strings/basic.string/string.asan/substr.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/strings/basic.string/string.asan/substr.pass.cpp @@ -0,0 +1,125 @@ +#include +#include +#include + +#include "test_macros.h" +#include "asan_testing.h" + +template +void test_substr(const CharT* s_init, const CharT* s_long_init) { + typedef std::basic_string S; + S short_s(s_init), long_s(s_long_init); + + S short_substr = short_s.substr(1, 1); + + assert(is_string_asan_correct(short_substr)); + + S long_substr1a = long_s.substr(10, 2); + S long_substr1b = long_s.substr(2, 34); + + assert(is_string_asan_correct(long_substr1a)); + assert(is_string_asan_correct(long_substr1b)); + + S short_empty = short_s.substr(1, 0); + S long_empty = long_s.substr(2, 0); + + assert(is_string_asan_correct(short_empty)); + assert(is_string_asan_correct(long_empty)); + + S short_substr2 = short_s.substr(1); + S long_substr2 = long_s.substr(2); + + assert(is_string_asan_correct(short_substr2)); + assert(is_string_asan_correct(long_substr2)); + + S short_substr3 = short_s.substr(); + S long_substr3 = long_s.substr(); + + assert(is_string_asan_correct(short_substr3)); + assert(is_string_asan_correct(long_substr3)); + + assert(is_string_asan_correct(short_s.substr(0, 1))); + assert(is_string_asan_correct(long_s.substr(2, 32))); + assert(is_string_asan_correct(short_s.substr())); + assert(is_string_asan_correct(long_s.substr())); +} + +template +void test(const CharT* s_init, const CharT* s_init2, const CharT* s_long_init, const CharT* s_long_init2) { + test_substr(s_init, s_long_init); + test_substr(s_init2, s_long_init2); +} + +char literal[] = "ab"; +wchar_t wliteral[] = L"ab"; +#if TEST_STD_VER >= 11 +char16_t literal16[] = u"ab"; +char32_t literal32[] = U"ab"; +#endif +#if TEST_STD_VER >= 20 +char8_t literal8[] = u8"ab"; +#endif + +char literal_2[] = "abc"; +wchar_t wliteral_2[] = L"abc"; +#if TEST_STD_VER >= 11 +char16_t literal16_2[] = u"abc"; +char32_t literal32_2[] = U"abc"; +#endif +#if TEST_STD_VER >= 20 +char8_t literal8_2[] = u8"abc"; +#endif + +char literal_long[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +wchar_t wliteral_long[] = L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +#if TEST_STD_VER >= 11 +char16_t literal16_long[] = u"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +char32_t literal32_long[] = U"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +#endif +#if TEST_STD_VER >= 20 +char8_t literal8_long[] = u8"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +#endif + +char literal_long2[] = + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +wchar_t wliteral_long2[] = + L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +#if TEST_STD_VER >= 11 +char16_t literal16_long2[] = + u"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +char32_t literal32_long2[] = + U"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +#endif +#if TEST_STD_VER >= 20 +char8_t literal8_long2[] = + u8"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +#endif + +int main(int, char**) { + { + typedef char CharT; + test(literal, literal_2, literal_long, literal_long2); + } + { + typedef wchar_t CharT; + test(wliteral, wliteral_2, wliteral_long, wliteral_long2); + } +#if TEST_STD_VER >= 11 + { + typedef char16_t CharT; + test(literal16, literal16_2, literal16_long, literal16_long2); + } + { + typedef char32_t CharT; + test(literal32, literal32_2, literal32_long, literal32_long2); + } +#endif +#if TEST_STD_VER >= 20 + { + typedef char8_t CharT; + test(literal8, literal8_2, literal8_long, literal8_long2); + } +#endif + + return 0; +} \ No newline at end of file diff --git a/libcxx/test/std/strings/basic.string/string.asan/swap.pass.cpp b/libcxx/test/std/strings/basic.string/string.asan/swap.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/strings/basic.string/string.asan/swap.pass.cpp @@ -0,0 +1,100 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// + +#include +#include + +#include "test_macros.h" +#include "asan_testing.h" + +template +void test(const CharT val) { + typedef std::basic_string S; + + 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); + assert(is_string_asan_correct(empty_s)); + assert(is_string_asan_correct(short_s)); + assert(is_string_asan_correct(long_s)); + + std::swap(empty_s, short_s); + assert(is_string_asan_correct(empty_s)); + assert(is_string_asan_correct(short_s)); + + std::swap(empty_s, short_s); + assert(is_string_asan_correct(empty_s)); + assert(is_string_asan_correct(short_s)); + + std::swap(empty_s, long_s); + assert(is_string_asan_correct(empty_s)); + assert(is_string_asan_correct(long_s)); + + std::swap(empty_s, long_s); + assert(is_string_asan_correct(empty_s)); + assert(is_string_asan_correct(long_s)); + + std::swap(short_s, long_s); + assert(is_string_asan_correct(short_s)); + assert(is_string_asan_correct(long_s)); + + std::swap(short_s, long_s); + assert(is_string_asan_correct(short_s)); + assert(is_string_asan_correct(long_s)); + + S long_s2(11100, val); + + std::swap(long_s, long_s2); + assert(is_string_asan_correct(long_s)); + assert(is_string_asan_correct(long_s2)); + + std::swap(long_s, long_s2); + assert(is_string_asan_correct(long_s)); + assert(is_string_asan_correct(long_s2)); + + S long_s3(111, val); + + std::swap(long_s, long_s3); + assert(is_string_asan_correct(long_s)); + assert(is_string_asan_correct(long_s2)); +} + +int main(int, char**) { + { + typedef char CharT; + test('x'); + } + { + typedef wchar_t CharT; + test(L'x'); + } +#if TEST_STD_VER >= 11 + { + typedef char16_t CharT; + test(u'x'); + } + { + typedef char32_t CharT; + test(U'x'); + } +#endif +#if TEST_STD_VER >= 20 + { + typedef char8_t CharT; + test(u8'x'); + } +#endif + + return 0; +} diff --git a/libcxx/test/support/asan_testing.h b/libcxx/test/support/asan_testing.h --- a/libcxx/test/support/asan_testing.h +++ b/libcxx/test/support/asan_testing.h @@ -25,7 +25,6 @@ c.data(), c.data() + c.size(), c.data() + c.capacity()) != 0; return true; } - #else template TEST_CONSTEXPR bool is_contiguous_container_asan_correct ( const std::vector &) @@ -34,5 +33,40 @@ } #endif +#if TEST_HAS_FEATURE(address_sanitizer) && _LIBCPP_CLANG_VER >= 16000 +# include +extern "C" bool __sanitizer_is_annotable(const void* address, const unsigned long size); +template +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*)&s <= (void*)&s[0] && (void*)&s[0] < (void*)(&s + 1); +} +template +bool is_string_asan_correct(const std::basic_string& c) { + if (std::is_same>::value && c.data() != NULL) { + if (__is_string_short(c)) { + if (__sanitizer_is_annotable(&c, sizeof(c))) + return __sanitizer_verify_contiguous_container(&c, c.data() + c.size() + 1, &c + 1) != 0; + else + return true; + } else + return __sanitizer_verify_contiguous_container(c.data(), c.data() + c.size() + 1, c.data() + c.capacity() + 1) != + 0; + } + return true; +} +#else +# include +template +bool is_string_asan_correct(const std::basic_string&) { + return true; +} +#endif // TEST_HAS_FEATURE(address_sanitizer) && _LIBCPP_CLANG_VER >= 16000 #endif // ASAN_TESTING_H