Index: include/string =================================================================== --- include/string +++ include/string @@ -1200,6 +1200,60 @@ #endif // _LIBCPP_ABI_ALTERNATE_STRING_LAYOUT +template +struct _LIBCPP_TYPE_VIS_ONLY basic_string_asan_traits { + typedef allocator_traits<_Allocator> __alloc_traits; + typedef typename __alloc_traits::const_pointer __const_pointer; + + static void __annotate_delete(__const_pointer __beg, __const_pointer __mid, + __const_pointer __end) {} + + static void __annotate_new(__const_pointer __beg, __const_pointer __mid, + __const_pointer __end) {} + + static void __annotate_grow(__const_pointer __beg, __const_pointer __old_mid, + __const_pointer __new_mid, + __const_pointer __end) {} +}; + +// asan is enabled only for default allocator. +template +struct _LIBCPP_TYPE_VIS_ONLY + basic_string_asan_traits<_CharT, allocator<_CharT>> { + typedef allocator<_CharT> __allocator; + typedef allocator_traits<__allocator> __alloc_traits; + typedef typename __alloc_traits::const_pointer __const_pointer; + + static void __annotate_delete(__const_pointer __beg, __const_pointer __mid, + __const_pointer __end) { + __annotate_contiguous_container(__beg, __end, __mid, __end); + } + + static void __annotate_new(__const_pointer __beg, __const_pointer __mid, + __const_pointer __end) { + __annotate_contiguous_container(__beg, __end, __end, __mid); + } + + static void __annotate_grow(__const_pointer __beg, __const_pointer __old_mid, + __const_pointer __new_mid, + __const_pointer __end) { + __annotate_contiguous_container(__beg, __end, __old_mid, __new_mid); + } + +private: + static void __annotate_contiguous_container(const void *__beg, + const void *__end, + const void *__old_mid, + const void *__new_mid) { +#if !defined(_LIBCPP_HAS_NO_ASAN) && !defined(_LIBCPP_NO_ASAN_STD_STRING) + if (__beg) { + __sanitizer_annotate_contiguous_container(__beg, __end, __old_mid, + __new_mid); + } +#endif + } +}; + template class _LIBCPP_TYPE_VIS_ONLY basic_string : private __basic_string_common @@ -1216,6 +1270,7 @@ typedef const value_type& const_reference; typedef typename __alloc_traits::pointer pointer; typedef typename __alloc_traits::const_pointer const_pointer; + typedef basic_string_asan_traits<_CharT, _Allocator> __asan_traits; static_assert(is_pod::value, "Character type of basic_string must be a POD"); static_assert((is_same<_CharT, value_type>::value), @@ -1698,6 +1753,55 @@ const allocator_type& __alloc() const _NOEXCEPT {return __r_.second();} + const_pointer __get_beg() const _NOEXCEPT { + if (__is_long()) + return __get_long_pointer(); + +#ifdef _LIBCPP_ABI_ALTERNATE_STRING_LAYOUT + return pointer_traits::pointer_to( + __r_.first().__s.__data_[0]); +#else + return pointer_traits::pointer_to(__r_.first().__s.__lx); +#endif // _LIBCPP_ABI_ALTERNATE_STRING_LAYOUT + } + + const_pointer __get_mid(size_type __sz) const _NOEXCEPT { + if (__is_long()) + return __get_long_pointer() + __sz + 1 /* terminator */; + else + return min(__get_short_pointer() + __sz + 1 /* terminator */, + __get_end()); + } + + const_pointer __get_end() const _NOEXCEPT { + if (__is_long()) + return __get_long_pointer() + __get_long_cap(); + + const_pointer __p = __get_beg() + __min_cap + 1 /* size */; +#ifdef _LIBCPP_ABI_ALTERNATE_STRING_LAYOUT + // last byte is used for size. ASAN can't poison + // memory precisely in this case. Do not poison last 8 bytes. + __p -= 8 / sizeof(value_type) ; +#endif // _LIBCPP_ABI_ALTERNATE_STRING_LAYOUT + + return __p; + } + + void __annotate_new(size_type __sz) const { + __asan_traits::__annotate_new(__get_beg(), __get_mid(__sz), __get_end()); + } + + void __annotate_grow(size_type __old_size, size_type __new_size) const { + __asan_traits::__annotate_grow(__get_beg(), __get_mid(__old_size), + __get_mid(__new_size), __get_end()); + } + + basic_string<_CharT, _Traits, _Allocator> const *__annotate_delete() const { + __asan_traits::__annotate_delete(__get_beg(), __get_mid(size()), + __get_end()); + return this; + } + #ifdef _LIBCPP_ABI_ALTERNATE_STRING_LAYOUT _LIBCPP_INLINE_VISIBILITY @@ -1776,12 +1880,12 @@ {return __is_long() ? __get_long_pointer() : __get_short_pointer();} _LIBCPP_INLINE_VISIBILITY - void __zero() _NOEXCEPT - { - size_type (&__a)[__n_words] = __r_.first().__r.__words; - for (unsigned __i = 0; __i < __n_words; ++__i) - __a[__i] = 0; - } + void __zero() _NOEXCEPT { + size_type(&__a)[__n_words] = __r_.first().__r.__words; + for (unsigned __i = 0; __i < __n_words; ++__i) + __a[__i] = 0; + __annotate_new(0); + } template static _LIBCPP_INLINE_VISIBILITY @@ -1871,6 +1975,8 @@ _NOEXCEPT_(is_nothrow_move_assignable::value) { __alloc() = _VSTD::move(__c.__alloc()); + __annotate_new(size()); + __c.__annotate_delete(); } _LIBCPP_INLINE_VISIBILITY @@ -1975,6 +2081,7 @@ __set_long_cap(__cap+1); __set_long_size(__sz); } + __annotate_new(__sz); traits_type::copy(_VSTD::__to_raw_pointer(__p), __s, __sz); traits_type::assign(__p[__sz], value_type()); } @@ -1999,6 +2106,7 @@ __set_long_cap(__cap+1); __set_long_size(__sz); } + __annotate_new(__sz); traits_type::copy(_VSTD::__to_raw_pointer(__p), __s, __sz); traits_type::assign(__p[__sz], value_type()); } @@ -2053,10 +2161,13 @@ basic_string<_CharT, _Traits, _Allocator>::basic_string(const basic_string& __str) : __r_(__alloc_traits::select_on_container_copy_construction(__str.__alloc())) { - if (!__str.__is_long()) - __r_.first().__r = __str.__r_.first().__r; - else - __init(_VSTD::__to_raw_pointer(__str.__get_long_pointer()), __str.__get_long_size()); + if (!__str.__is_long()) { + __str.__annotate_delete(); + __r_.first().__r = __str.__r_.first().__r; + __str.__annotate_new(__str.__get_short_size()); + __annotate_new(__get_short_size()); + } else + __init(_VSTD::__to_raw_pointer(__str.__get_long_pointer()), __str.__get_long_size()); #if _LIBCPP_DEBUG_LEVEL >= 2 __get_db()->__insert_c(this); #endif @@ -2066,8 +2177,12 @@ basic_string<_CharT, _Traits, _Allocator>::basic_string(const basic_string& __str, const allocator_type& __a) : __r_(__a) { - if (!__str.__is_long()) + if (!__str.__is_long()) { + __str.__annotate_delete(); __r_.first().__r = __str.__r_.first().__r; + __str.__annotate_new(__str.__get_short_size()); + __annotate_new(__get_short_size()); + } else __init(_VSTD::__to_raw_pointer(__str.__get_long_pointer()), __str.__get_long_size()); #if _LIBCPP_DEBUG_LEVEL >= 2 @@ -2085,9 +2200,10 @@ #else _NOEXCEPT #endif - : __r_(_VSTD::move(__str.__r_)) + : __r_(_VSTD::move(__str.__annotate_delete()->__r_)) { __str.__zero(); + __annotate_new(size()); #if _LIBCPP_DEBUG_LEVEL >= 2 __get_db()->__insert_c(this); if (__is_long()) @@ -2104,8 +2220,10 @@ __init(_VSTD::__to_raw_pointer(__str.__get_long_pointer()), __str.__get_long_size()); else { + __str.__annotate_delete(); __r_.first().__r = __str.__r_.first().__r; __str.__zero(); + __annotate_new(__get_short_size()); } #if _LIBCPP_DEBUG_LEVEL >= 2 __get_db()->__insert_c(this); @@ -2136,6 +2254,7 @@ __set_long_cap(__cap+1); __set_long_size(__n); } + __annotate_new(__n); traits_type::assign(_VSTD::__to_raw_pointer(__p), __n, __c); traits_type::assign(__p[__n], value_type()); } @@ -2216,20 +2335,24 @@ if (__sz > max_size()) this->__throw_length_error(); pointer __p; + size_type __cap; if (__sz < __min_cap) { __set_short_size(__sz); __p = __get_short_pointer(); + __cap = __min_cap; } else { - size_type __cap = __recommend(__sz); + __cap = __recommend(__sz); __p = __alloc_traits::allocate(__alloc(), __cap+1); __set_long_pointer(__p); __set_long_cap(__cap+1); __set_long_size(__sz); } - for (; __first != __last; ++__first, (void) ++__p) + + __annotate_new(__sz); + for (; __first != __last; ++__first, (void) ++__p) traits_type::assign(*__p, *__first); traits_type::assign(*__p, value_type()); } @@ -2289,6 +2412,8 @@ #if _LIBCPP_DEBUG_LEVEL >= 2 __get_db()->__erase_c(this); #endif + + __annotate_delete(); if (__is_long()) __alloc_traits::deallocate(__alloc(), __get_long_pointer(), __get_long_cap()); } @@ -2319,10 +2444,12 @@ _VSTD::__to_raw_pointer(__old_p) + __n_copy + __n_del, __sec_cp_sz); if (__old_cap+1 != __min_cap) __alloc_traits::deallocate(__alloc(), __old_p, __old_cap+1); + __annotate_delete(); __set_long_pointer(__p); __set_long_cap(__cap+1); __old_sz = __n_copy + __n_add + __sec_cp_sz; __set_long_size(__old_sz); + __annotate_new(__old_sz); traits_type::assign(__p[__old_sz], value_type()); } @@ -2350,6 +2477,7 @@ __sec_cp_sz); if (__old_cap+1 != __min_cap) __alloc_traits::deallocate(__alloc(), __old_p, __old_cap+1); + __annotate_delete(); __set_long_pointer(__p); __set_long_cap(__cap+1); } @@ -2362,19 +2490,19 @@ { _LIBCPP_ASSERT(__n == 0 || __s != nullptr, "string::assign received nullptr"); size_type __cap = capacity(); + size_type __sz = size(); if (__cap >= __n) { value_type* __p = _VSTD::__to_raw_pointer(__get_pointer()); + __annotate_grow(__sz, __n); traits_type::move(__p, __s, __n); traits_type::assign(__p[__n], value_type()); __set_size(__n); __invalidate_iterators_past(__n); } else - { - size_type __sz = size(); __grow_by_and_replace(__cap, __n - __cap, __sz, 0, __sz, __n, __s); - } + return *this; } @@ -2383,14 +2511,16 @@ basic_string<_CharT, _Traits, _Allocator>::assign(size_type __n, value_type __c) { size_type __cap = capacity(); + size_type __sz = size(); if (__cap < __n) { - size_type __sz = size(); __grow_by(__cap, __n - __cap, __sz, 0, __sz); + __annotate_new(__sz); } else __invalidate_iterators_past(__n); value_type* __p = _VSTD::__to_raw_pointer(__get_pointer()); + __annotate_grow(__sz, __n); traits_type::assign(__p, __n, __c); traits_type::assign(__p[__n], value_type()); __set_size(__n); @@ -2402,6 +2532,7 @@ basic_string<_CharT, _Traits, _Allocator>::operator=(value_type __c) { pointer __p; + size_t __sz = size(); if (__is_long()) { __p = __get_long_pointer(); @@ -2412,6 +2543,7 @@ __p = __get_short_pointer(); __set_short_size(1); } + __annotate_grow(__sz, 1); traits_type::assign(*__p, __c); traits_type::assign(*++__p, value_type()); __invalidate_iterators_past(1); @@ -2456,6 +2588,8 @@ { clear(); shrink_to_fit(); + __str.__annotate_delete(); + __annotate_delete(); __r_.first() = __str.__r_.first(); __move_assign_alloc(__str); __str.__zero(); @@ -2501,14 +2635,16 @@ { size_type __n = static_cast(_VSTD::distance(__first, __last)); size_type __cap = capacity(); + size_type __sz = size(); if (__cap < __n) { - size_type __sz = size(); __grow_by(__cap, __n - __cap, __sz, 0, __sz); + __annotate_new(__sz); } else __invalidate_iterators_past(__n); pointer __p = __get_pointer(); + __annotate_grow(__sz, __n); for (; __first != __last; ++__first, ++__p) traits_type::assign(*__p, *__first); traits_type::assign(*__p, value_type()); @@ -2555,6 +2691,7 @@ { if (__n) { + __annotate_grow(__sz, __sz + __n); value_type* __p = _VSTD::__to_raw_pointer(__get_pointer()); traits_type::copy(__p + __sz, __s, __n); __sz += __n; @@ -2575,9 +2712,12 @@ { size_type __cap = capacity(); size_type __sz = size(); - if (__cap - __sz < __n) + if (__cap - __sz < __n) { __grow_by(__cap, __sz + __n - __cap, __sz, __sz, 0); + __annotate_new(__sz); + } pointer __p = __get_pointer(); + __annotate_grow(__sz, __sz + __n); traits_type::assign(_VSTD::__to_raw_pointer(__p) + __sz, __n, __c); __sz += __n; __set_size(__sz); @@ -2606,6 +2746,7 @@ if (__sz == __cap) { __grow_by(__cap, 1, __sz, __sz, 0); + __annotate_new(__sz); __is_short = !__is_long(); } pointer __p; @@ -2619,6 +2760,8 @@ __p = __get_long_pointer() + __sz; __set_long_size(__sz+1); } + + __annotate_grow(__sz, __sz + 1); traits_type::assign(*__p, __c); traits_type::assign(*++__p, value_type()); } @@ -2652,9 +2795,12 @@ size_type __n = static_cast(_VSTD::distance(__first, __last)); if (__n) { - if (__cap - __sz < __n) + if (__cap - __sz < __n) { __grow_by(__cap, __sz + __n - __cap, __sz, __sz, 0); + __annotate_new(__sz); + } pointer __p = __get_pointer() + __sz; + __annotate_grow(__sz, __sz + __n); for (; __first != __last; ++__p, ++__first) traits_type::assign(*__p, *__first); traits_type::assign(*__p, value_type()); @@ -2706,6 +2852,7 @@ { value_type* __p = _VSTD::__to_raw_pointer(__get_pointer()); size_type __n_move = __sz - __pos; + __annotate_grow(__sz, __sz + __n); if (__n_move != 0) { if (__p + __pos <= __s && __s < __p + __sz) @@ -2736,6 +2883,7 @@ value_type* __p; if (__cap - __sz >= __n) { + __annotate_grow(__sz, __sz + __n); __p = _VSTD::__to_raw_pointer(__get_pointer()); size_type __n_move = __sz - __pos; if (__n_move != 0) @@ -2744,6 +2892,7 @@ else { __grow_by(__cap, __sz + __n - __cap, __sz, __pos, 0, __n); + __annotate_new(__sz + __n); __p = _VSTD::__to_raw_pointer(__get_long_pointer()); } traits_type::assign(__p + __pos, __n, __c); @@ -2771,8 +2920,7 @@ #endif size_type __old_sz = size(); difference_type __ip = __pos - begin(); - for (; __first != __last; ++__first) - push_back(*__first); + append(__first, __last); pointer __p = __get_pointer(); _VSTD::rotate(__p + __ip, __p + __old_sz, __p + size()); #if _LIBCPP_DEBUG_LEVEL >= 2 @@ -2807,12 +2955,14 @@ { __p = _VSTD::__to_raw_pointer(__get_pointer()); size_type __n_move = __sz - __ip; + __annotate_grow(__sz, __sz + __n); if (__n_move != 0) traits_type::move(__p + __ip + __n, __p + __ip, __n_move); } else { __grow_by(__cap, __sz + __n - __cap, __sz, __ip, 0, __n); + __annotate_new(__sz + __n); __p = _VSTD::__to_raw_pointer(__get_long_pointer()); } __sz += __n; @@ -2862,10 +3012,12 @@ if (__cap == __sz) { __grow_by(__cap, 1, __sz, __ip, 0, 1); + __annotate_new(__sz + 1); __p = _VSTD::__to_raw_pointer(__get_long_pointer()); } else { + __annotate_grow(__sz, __sz + 1); __p = _VSTD::__to_raw_pointer(__get_pointer()); size_type __n_move = __sz - __ip; if (__n_move != 0) @@ -2903,9 +3055,12 @@ if (__pos > __sz) this->__throw_out_of_range(); __n1 = _VSTD::min(__n1, __sz - __pos); + size_type __new_sz = __sz + __n2 - __n1; size_type __cap = capacity(); if (__cap - __sz + __n1 >= __n2) { + if (__new_sz > __sz) + __annotate_grow(__sz, __new_sz); value_type* __p = _VSTD::__to_raw_pointer(__get_pointer()); if (__n1 != __n2) { @@ -2936,6 +3091,8 @@ } traits_type::move(__p + __pos, __s, __n2); __finish: + if (__new_sz < __sz) + __annotate_grow(__sz, __new_sz); __sz += __n2 - __n1; __set_size(__sz); __invalidate_iterators_past(__sz); @@ -2954,10 +3111,14 @@ if (__pos > __sz) this->__throw_out_of_range(); __n1 = _VSTD::min(__n1, __sz - __pos); + size_t __new_sz = __sz + __n2 - __n1; size_type __cap = capacity(); value_type* __p; - if (__cap - __sz + __n1 >= __n2) + if (__cap >= __new_sz) { + if (__new_sz > __sz) { + __annotate_grow(__sz, __new_sz); + } __p = _VSTD::__to_raw_pointer(__get_pointer()); if (__n1 != __n2) { @@ -2969,10 +3130,14 @@ else { __grow_by(__cap, __sz - __n1 + __n2 - __cap, __sz, __pos, __n1, __n2); + __annotate_new(__new_sz); __p = _VSTD::__to_raw_pointer(__get_long_pointer()); } + if (__new_sz < __sz) { + __annotate_grow(__sz, __new_sz); + } traits_type::assign(__p + __pos, __n2, __c); - __sz += __n2 - __n1; + __sz = __new_sz; __set_size(__sz); __invalidate_iterators_past(__sz); traits_type::assign(__p[__sz], value_type()); @@ -3083,6 +3248,7 @@ size_type __n_move = __sz - __pos - __n; if (__n_move != 0) traits_type::move(__p + __pos, __p + __pos + __n, __n_move); + __annotate_grow(__sz, __sz - __n); __sz -= __n; __set_size(__sz); __invalidate_iterators_past(__sz); @@ -3145,6 +3311,7 @@ __set_short_size(__sz); traits_type::assign(*(__get_short_pointer() + __sz), value_type()); } + __annotate_grow(__sz + 1, __sz); __invalidate_iterators_past(__sz); } @@ -3153,6 +3320,7 @@ void basic_string<_CharT, _Traits, _Allocator>::clear() _NOEXCEPT { + size_t __sz = size(); __invalidate_all_iterators(); if (__is_long()) { @@ -3164,6 +3332,7 @@ traits_type::assign(*__get_short_pointer(), value_type()); __set_short_size(0); } + __annotate_grow(__sz, 0); } template @@ -3171,6 +3340,7 @@ void basic_string<_CharT, _Traits, _Allocator>::__erase_to_end(size_type __pos) { + size_type __sz = size(); if (__is_long()) { traits_type::assign(*(__get_long_pointer() + __pos), value_type()); @@ -3181,6 +3351,7 @@ traits_type::assign(*(__get_short_pointer() + __pos), value_type()); __set_short_size(__pos); } + __annotate_grow(__sz, __pos); __invalidate_iterators_past(__pos); } @@ -3255,10 +3426,15 @@ __was_long = __is_long(); __p = __get_pointer(); } + traits_type::copy(_VSTD::__to_raw_pointer(__new_data), _VSTD::__to_raw_pointer(__p), size()+1); + if (__was_long) __alloc_traits::deallocate(__alloc(), __p, __cap+1); + + __annotate_delete(); + if (__now_long) { __set_long_cap(__res_arg+1); @@ -3267,6 +3443,7 @@ } else __set_short_size(__sz); + __annotate_new(__sz); __invalidate_all_iterators(); } } @@ -3381,8 +3558,12 @@ __get_db()->__invalidate_all(&__str); __get_db()->swap(this, &__str); #endif + __str.__annotate_delete(); + __annotate_delete(); _VSTD::swap(__r_.first(), __str.__r_.first()); __swap_allocator(__alloc(), __str.__alloc()); + __str.__annotate_new(__str.size()); + __annotate_new(size()); } // find @@ -3970,10 +4151,9 @@ const basic_string<_CharT, _Traits, _Allocator>& __rhs) { basic_string<_CharT, _Traits, _Allocator> __r(__lhs.get_allocator()); - typename basic_string<_CharT, _Traits, _Allocator>::size_type __lhs_sz = __lhs.size(); - typename basic_string<_CharT, _Traits, _Allocator>::size_type __rhs_sz = __rhs.size(); - __r.__init(__lhs.data(), __lhs_sz, __lhs_sz + __rhs_sz); - __r.append(__rhs.data(), __rhs_sz); + __r.reserve(__rhs.size() + __lhs.size()); + __r.append(__lhs); + __r.append(__rhs); return __r; } @@ -3982,10 +4162,9 @@ operator+(const _CharT* __lhs , const basic_string<_CharT,_Traits,_Allocator>& __rhs) { basic_string<_CharT, _Traits, _Allocator> __r(__rhs.get_allocator()); - typename basic_string<_CharT, _Traits, _Allocator>::size_type __lhs_sz = _Traits::length(__lhs); - typename basic_string<_CharT, _Traits, _Allocator>::size_type __rhs_sz = __rhs.size(); - __r.__init(__lhs, __lhs_sz, __lhs_sz + __rhs_sz); - __r.append(__rhs.data(), __rhs_sz); + __r.reserve(__rhs.size() + _Traits::length(__lhs)); + __r.append(__lhs); + __r.append(__rhs); return __r; } @@ -3994,9 +4173,9 @@ operator+(_CharT __lhs, const basic_string<_CharT,_Traits,_Allocator>& __rhs) { basic_string<_CharT, _Traits, _Allocator> __r(__rhs.get_allocator()); - typename basic_string<_CharT, _Traits, _Allocator>::size_type __rhs_sz = __rhs.size(); - __r.__init(&__lhs, 1, 1 + __rhs_sz); - __r.append(__rhs.data(), __rhs_sz); + __r.reserve(__rhs.size() + 1); + __r.push_back(__lhs); + __r.append(__rhs); return __r; } @@ -4005,10 +4184,9 @@ operator+(const basic_string<_CharT, _Traits, _Allocator>& __lhs, const _CharT* __rhs) { basic_string<_CharT, _Traits, _Allocator> __r(__lhs.get_allocator()); - typename basic_string<_CharT, _Traits, _Allocator>::size_type __lhs_sz = __lhs.size(); - typename basic_string<_CharT, _Traits, _Allocator>::size_type __rhs_sz = _Traits::length(__rhs); - __r.__init(__lhs.data(), __lhs_sz, __lhs_sz + __rhs_sz); - __r.append(__rhs, __rhs_sz); + __r.reserve(_Traits::length(__rhs) + __lhs.size()); + __r.append(__lhs); + __r.append(__rhs); return __r; } @@ -4017,8 +4195,8 @@ operator+(const basic_string<_CharT, _Traits, _Allocator>& __lhs, _CharT __rhs) { basic_string<_CharT, _Traits, _Allocator> __r(__lhs.get_allocator()); - typename basic_string<_CharT, _Traits, _Allocator>::size_type __lhs_sz = __lhs.size(); - __r.__init(__lhs.data(), __lhs_sz, __lhs_sz + 1); + __r.reserve(__lhs.size() + 1); + __r.append(__lhs); __r.push_back(__rhs); return __r; } @@ -4237,7 +4415,7 @@ #endif // _LIBCPP_DEBUG_LEVEL >= 2 -#if _LIBCPP_STD_VER > 11 +#if _LIBCPP_STD_VER > 11 // Literal suffixes for basic_string [basic.string.literals] inline namespace literals { Index: test/std/strings/basic.string/asan.pass.cpp =================================================================== --- /dev/null +++ test/std/strings/basic.string/asan.pass.cpp @@ -0,0 +1,362 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// Test asan string annotations. + +#include +#include +#include +#include + +#include "asan_testing.h" + +template void test_empty_string() { + fprintf(stderr, "--- test_empty_string\n"); + S s; + assert(is_asan_correct_string(s)); +} + +template void test_fill_constructor() { + fprintf(stderr, "--- test_fill_constructor\n"); + for (int i = 0; i < 100; ++i) { + fprintf(stderr, ">>>i=%d\n", i); + S s(i, static_cast('x')); + assert(s.size() == i); + assert(is_asan_correct_string(s)); + } +} + +template void test_cstr_constructor() { + fprintf(stderr, "--- test_cstr_constructor\n"); + for (int i = 0; i < 100; ++i) { + fprintf(stderr, ">>>i=%d\n", i); + S tmp(i, static_cast('x')); + assert(tmp.size() == i); + assert(is_asan_correct_string(tmp)); + + S s(tmp.c_str()); + assert(s.size() == i); + assert(is_asan_correct_string(s)); + } +} + +template void test_copy_constructor() { + fprintf(stderr, "--- test_copy_constructor\n"); + for (int i = 0; i < 100; ++i) { + S tmp(i, static_cast('x')); + assert(tmp.size() == i); + assert(is_asan_correct_string(tmp)); + + fprintf(stderr, ">>>i=%d\n", i); + S s(tmp); + assert(s.size() == i); + assert(is_asan_correct_string(s)); + } +} + +template void test_reserve() { + fprintf(stderr, "--- test_reserve\n"); + for (int i = 0; i < 100; ++i) { + S s; + fprintf(stderr, ">>>i=%d\n", i); + s.reserve(i); + assert(is_asan_correct_string(s)); + } +} + +template void test_push_back() { + fprintf(stderr, "--- test_push_back\n"); + S s; + for (int i = 0; i < 100; ++i) { + fprintf(stderr, ">>>s.size()=%ld\n", s.size()); + s.push_back(1); + assert(is_asan_correct_string(s)); + } +} + +template void test_insert_range() { + fprintf(stderr, "--- test_insert_range\n"); + + for (int i = 0; i < 100; ++i) { + fprintf(stderr, ">>>i=%d\n", i); + std::vector v( + i, static_cast('x')); + S s; + s.insert(s.end(), v.begin(), v.end()); + assert(is_asan_correct_string(s)); + } +} + +template void test_insert_fill() { + fprintf(stderr, "--- test_insert_fill\n"); + + for (int i = 0; i < 100; ++i) { + fprintf(stderr, ">>>i=%d\n", i); + S s; + s.insert(0, i, static_cast('x')); + assert(is_asan_correct_string(s)); + } +} + +template void test_insert_cstr() { + fprintf(stderr, "--- test_insert_range\n"); + + for (int i = 0; i < 100; ++i) { + fprintf(stderr, ">>>i=%d\n", i); + S tmp(i, static_cast('x')); + S s; + s.insert(0, tmp.c_str()); + assert(is_asan_correct_string(s)); + } +} + +template void test_assign() { + fprintf(stderr, "--- test_assign\n"); + + for (int i = 0; i < 100; ++i) { + fprintf(stderr, ">>>i=%d\n", i); + + S s; + s.assign(i, static_cast('x')); + assert(is_asan_correct_string(s)); + } +} + +template void test_assign_char() { + fprintf(stderr, "--- test_assign\n"); + + for (int i = 0; i < 100; ++i) { + fprintf(stderr, ">>>i=%d\n", i); + + S s(i, static_cast('x')); + s = static_cast('z'); + assert(is_asan_correct_string(s)); + } +} + +template void test_append_str() { + fprintf(stderr, "--- test_append_str\n"); + + for (int i = 0; i < 100; ++i) { + S tmp(i, static_cast('x')); + fprintf(stderr, ">>>i=%d\n", i); + + S s; + s.append(tmp); + assert(is_asan_correct_string(s)); + } +} + +template void test_append_fill() { + fprintf(stderr, "--- test_append_fill\n"); + + for (int i = 0; i < 100; ++i) { + fprintf(stderr, ">>>i=%d\n", i); + + S s; + s.append(i, static_cast('x')); + assert(is_asan_correct_string(s)); + } +} + +template void test_resize() { + fprintf(stderr, "--- test_resize\n"); + + for (int i = 0; i < 100; ++i) { + fprintf(stderr, ">>>i=%d\n", i); + + { + S s; + s.resize(i); + assert(is_asan_correct_string(s)); + } + { + S s(100, static_cast('x')); + s.resize(i); + assert(is_asan_correct_string(s)); + } + } +} + +template void test_assign_cstr() { + fprintf(stderr, "--- test_assign_cstr\n"); + for (int i = 0; i < 100; ++i) { + fprintf(stderr, ">>>i=%d\n", i); + S tmp(i, static_cast('x')); + assert(tmp.size() == i); + assert(is_asan_correct_string(tmp)); + + S s; + s.assign(tmp.c_str()); + assert(s.size() == i); + assert(is_asan_correct_string(s)); + } +} + +template void test_clear() { + fprintf(stderr, "--- test_clear\n"); + + for (int i = 0; i < 100; ++i) { + fprintf(stderr, ">>>i=%d\n", i); + S s(i, static_cast('x')); + s.clear(); + assert(is_asan_correct_string(s)); + } +} + +template void test_append_range() { + fprintf(stderr, "--- test_append_range\n"); + + for (int i = 0; i < 100; ++i) { + fprintf(stderr, ">>>i=%d\n", i); + std::vector v( + i, static_cast('x')); + S s; + s.append(v.begin(), v.end()); + assert(is_asan_correct_string(s)); + } +} + +template void test_assign_range() { + fprintf(stderr, "--- test_assign_range\n"); + + for (int i = 0; i < 100; ++i) { + fprintf(stderr, ">>>i=%d\n", i); + std::vector v( + i, static_cast('x')); + S s; + s.assign(v.begin(), v.end()); + assert(is_asan_correct_string(s)); + } +} + +template void test_replace_cstr() { + fprintf(stderr, "--- test_replace\n"); + + for (int i = 0; i < 100; ++i) { + fprintf(stderr, ">>>i=%d\n", i); + + { + S tmp(i, static_cast('x')); + S s; + s.replace(0, 0, tmp.c_str()); + assert(is_asan_correct_string(s)); + } + + { + S tmp; + S s(i, static_cast('x')); + s.replace(0, s.size() / 2, tmp.c_str()); + assert(is_asan_correct_string(s)); + } + } + + { + S s(10, static_cast('x')); + S tmp(10, static_cast('y')); + s.replace(0, 0, tmp.c_str(), 2); + assert(is_asan_correct_string(s)); + } +} + +template void test_operator_plus() { + fprintf(stderr, "--- test_operator_plus\n"); + + for (int i = 0; i < 100; ++i) { + fprintf(stderr, ">>>i=%d\n", i); + S tmp(i, static_cast('x')); + S s = static_cast('y') + tmp; + assert(is_asan_correct_string(s)); + } +} + +template void test_erase() { + fprintf(stderr, "--- test_erase\n"); + + for (int i = 0; i < 100; ++i) { + fprintf(stderr, ">>>i=%d\n", i); + + { + S s(i, static_cast('x')); + s.erase(0, s.size()); + assert(is_asan_correct_string(s)); + } + + { + S s(i, static_cast('x')); + s.erase(s.size() / 4, s.size() * 3 / 4); + assert(is_asan_correct_string(s)); + } + } +} + +template void test_pop_back() { + fprintf(stderr, "--- test_erase\n"); + + for (int i = 0; i < 100; ++i) { + fprintf(stderr, ">>>i=%d\n", i); + + S s(i, static_cast('x')); + while (!s.empty()) { + s.pop_back(); + assert(is_asan_correct_string(s)); + } + } +} + +template void test_cstr() { + fprintf(stderr, "--- test_erase\n"); + + for (int i = 0; i < 100; ++i) { + fprintf(stderr, ">>>i=%d\n", i); + + S s(i, static_cast('x')); + assert(is_asan_correct_string(s)); + s.c_str(); + } +} + +template void test_all() { + test_empty_string(); + test_fill_constructor(); + test_cstr_constructor(); + test_copy_constructor(); + test_resize(); + test_clear(); + test_reserve(); + test_push_back(); + test_insert_range(); + test_insert_cstr(); + test_insert_fill(); + test_assign(); + test_assign_char(); + test_assign_cstr(); + test_assign_range(); + test_append_str(); + test_append_fill(); + test_append_range(); + test_replace_cstr(); + test_operator_plus(); + test_erase(); + test_cstr(); +} + +int main() { + test_all(); + test_all(); + + std::string s( + "0123456789012345678901234567890123456789012345678901234567890123456789" + "0123456789012345678901234567890123456789012345678901234567890123456789" + "0123456789012345678901234567890123456789012345678901234567890123456789" + "0123456789012345678901234567890123456789012345678901234567890123456789"); + assert(s.size() == 280); + assert(is_asan_correct_string(s)); + assert(strlen(s.c_str()) == 280); +} Index: test/support/asan_testing.h =================================================================== --- test/support/asan_testing.h +++ test/support/asan_testing.h @@ -11,27 +11,80 @@ #define ASAN_TESTING_H #include <__config> +#include +#include #ifndef _LIBCPP_HAS_NO_ASAN -extern "C" int __sanitizer_verify_contiguous_container - ( const void *beg, const void *mid, const void *end ); - +extern "C" int __sanitizer_verify_contiguous_container(const void *beg, + const void *mid, + const void *end); + +extern "C" const void *__sanitizer_contiguous_container_find_bad_address( + const void *beg, const void *mid, const void *end); + template -bool is_contiguous_container_asan_correct ( const std::vector &c ) -{ - if ( std::is_same >::value && c.data() != NULL) - return __sanitizer_verify_contiguous_container ( - c.data(), c.data() + c.size(), c.data() + c.capacity()) != 0; +bool is_contiguous_container_asan_correct(const std::vector &c) { + if (std::is_same>::value && c.data() != NULL) + return __sanitizer_verify_contiguous_container( + c.data(), c.data() + c.size(), c.data() + c.capacity()) != 0; + return true; +} + +template +bool is_asan_correct_string(const std::basic_string &s) { + if (!std::is_same>::value || s.data() == NULL) { return true; + } + + const T *s_ptr = reinterpret_cast(&s); + const T *data = reinterpret_cast(s.data()); + bool is_small = (data - s_ptr) < sizeof(void *); + + const T *p1; + const T *p2; + const T *mid; + const void *bad_addr = nullptr; + + if (is_small) { + p1 = s_ptr; + mid = data + s.size() + 1 /* terminator */; + p2 = data + s.capacity() + 1 /* terminator */; +#ifdef _LIBCPP_ABI_ALTERNATE_STRING_LAYOUT + p2 = p2 + 1 /* size */ - 8 / sizeof(T); + mid = std::min(mid, p2); +#endif // _LIBCPP_ABI_ALTERNATE_STRING_LAYOUT + } else { + p1 = data; + mid = data + s.size() + 1 /* terminator */; + p2 = data + s.capacity() + 1 /* terminator */; + } + + bad_addr = __sanitizer_contiguous_container_find_bad_address(p1, mid, p2); + if (bad_addr) { + std::ptrdiff_t offset = reinterpret_cast(bad_addr) - + reinterpret_cast(s.data()); + + fprintf(stderr, + "ERROR: broken string: bad_addr=%p (data+%ld), this=%p, data=%p, " + "size=%ld, capacity=%ld [%p %p %p]\n", + bad_addr, offset, s_ptr, s.data(), s.size(), s.capacity(), p1, mid, + p2); + return false; + } + + return true; } #else template -bool is_contiguous_container_asan_correct ( const std::vector &c ) -{ - return true; +bool is_contiguous_container_asan_correct(const std::vector &c) { + return true; +} + +template +bool is_asan_correct_string(const std::basic_string &s) { + return true; } #endif - -#endif // ASAN_TESTING_H +#endif // ASAN_TESTING_H