Rebased and update patch (typos, add missing annotations), add ASan teststo verify string annotation.
On 06/28/2018 11:09 AM, Mikhail Kashkarov wrote: > ^ gentle ping. > > > On 06/08/2018 05:54 PM, Mikhail Kashkarov wrote: >> Hello, >> >> I've updated patches for std::string sanitization and disabling CXX11 >> string SSO usage for correct sanitization. >> >> >> _M_destroy(_M_allocated_capacity); >> >>+ else >> >>+ __annotate_delete(); >> > >> >Do these calls definitely optimize away completely when not >> >sanitizing? Even for -O1, -Os and -Og? >> > >> >For std::vector annotation I used macros to add these annotations, so >> >there is no change to the generated code when annotations are >> >disabled. But it makes the code quite ugly. >> >> I've checked asm code for string-inst.o and it looks like this calls are >> optimized away, but there are some light changes after patch fir . >> >> > Right, I was wondering specifically about the <fstream> >> > instantiations. I could be wrong but I don't think anything in >> > <fstream> creates, destroys or modifies a std::basic_string. >> >> I was confused by reinterpret_cast's on strings in fstream.tcc, looks >> like this is not needed, you are right. >> >> >> // calling 4.0.x+ _S_create. >> >> __p->_M_set_sharable(); >> >>+#if _GLIBCXX_SANITIZER_ANNOTATE_STRING >> >>+ __p->_M_length = 0; >> >>+#endif >> > >> > Why is this needed? I think a comment explaining it would help (like >> > the one above explaining why calling _M_set_sharable() is needed). >> >> Checked current version without this change, looks like now it works, >> reverted. >> >> Short summary: >> The reason for changing strings layout under sanitization is to place string >> char buffer on heap for correct aligning in 32-bit environments, >> both pre-CXX11 and CXX11 strings ABI. >> >> | Sanitize string | string type | ABI is changed? | 32-bit | 64-bit | >> |-----------------+-------------+-----------------+--------+--------| >> | FULL | SSO-string | yes | + | + | >> | | COW-string | yes | + | + | >> |-----------------+-------------+-----------------+--------+--------| >> | PARTIAL | SSO-string | no | -+(*) | + | >> | | COW-string | no | - | + | >> *only longs strings are sanitized for 32-bit >> >> Some functions with new define looks a bit messy without changing internal >> functions(operator=), I'm also not sure if disabling string SSO usage define >> should also affects other parts that relies on basic_string class size >> (checks >> with static_assert in exceptions/shim-facets). >> >> >> Any thoughts? >> >> On 05/29/2018 06:55 PM, Jonathan Wakely wrote: >>> On 29/05/18 18:18 +0300, Kashkarov Mikhail wrote: >>>> Jonathan Wakely <jwak...@redhat.com> writes: >>>>>> --- a/libstdc++-v3/include/bits/fstream.tcc >>>>>> +++ b/libstdc++-v3/include/bits/fstream.tcc >>>>>> @@ -1081,6 +1081,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >>>>>> >>>>>> // Inhibit implicit instantiations for required instantiations, >>>>>> // which are defined via explicit instantiations elsewhere. >>>>>> +#if !_GLIBCXX_SANITIZE_STRING >>>>>> #if _GLIBCXX_EXTERN_TEMPLATE >>>>>> extern template class basic_filebuf<char>; >>>>>> extern template class basic_ifstream<char>; >>>>>> @@ -1094,6 +1095,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >>>>>> extern template class basic_fstream<wchar_t>; >>>>>> #endif >>>>>> #endif >>>>>> +#endif // !_GLIBCXX_SANITIZE_STRING >>>>> Why do we need to disable these explicit instantiation declarations? >>>>> Are they affected by the std::string layout changes? Is that just >>>>> because of the constructors taking std::string, or something else? >>>> Libstdc++ build is not sanitized, so macroses that requires >>>> AddressSanitizer support will not applied and these templates will be >>>> instantate without support for ASan annotations. >>> Right, I was wondering specifically about the <fstream> >>> instantiations. I could be wrong but I don't think anything in >>> <fstream> creates, destroys or modifies a std::basic_string. >>> >>> >>> >>> >>> -- Best regards, Kashkarov Mikhail Samsung R&D Institute Russia
From c08b3073e8d7d785425e972a0381cbbb9e3c2f58 Mon Sep 17 00:00:00 2001 From: Mikhail Kashkarov <m.kashka...@partner.samsung.com> Date: Fri, 8 Jun 2018 14:11:11 +0300 Subject: [PATCH 2/3] Add AddressSanitizer annotations to std::string. * include/bits/c++config: define (_GLIBCXX_SANITIZE_STRING_PARTIAL, _GLIBCXX_SANITIZE_STRING_FULL) (_GLIBCXX_SANTIZE_STRING, _GLIBCXX_SANITIZER_ANNOTATE_STRING) (_GLIBCXX_SANITIZER_DISABLE_LOCAL_STRING_ANNOTATION) (_GLIBCXX_SANITIZER_ALIGN_COW_STRING) * doc/xml/manual/using.xml: document GLIBCXX_SANITIZE_STRING_PARTIAL, _GLIBCXX_SANITIZE_STRING_FULL * include/bits/basic_string.h [_GLIBCXX_USE_DUAL_ABI] (_asan_traits<_CharT, _Alloc>, _asan_traits<_CharT, allocator<_CharT>) (_asan_traits::__annotate_delete, _asan_traits::__annotate_new) (_asan_traits::__annotate_grow): New traits for annotation. (basic_string::__RAII_IncreaseAnnotator::__RAII_Increaseannotator) (basic_string::__RAII_IncreaseAnnotator::done) (basic_string::__RAII_IncreaseAnnotator::~RAII_Increaseannotator): New annotation helpers in case of exceptions. (basic_string::__get_beg, basic_string::__get_mid) (basic_string::__get_end): New annotation helpers. (basis_string::_M_dispose, basic_string::_M_destroy) (basic_string::basic_string(basic_string&& __str)) (basic_string::basic_string(basic_string&& __str, const _Alloc& __a)) (basic_string::operator=(constbasic_string& __str)) (basic_string::operator=(basic_string&& __str)) (basic_string::clear, basic_string::push_back): Annotate. [!_GLIBCXX_USE_DUAL_ABI] (_asan_traits<_CharT, _Alloc>, _asan_traits<_CharT, allocator<_CharT>) (_asan_traits::__annotate_delete, _asan_traits::__annotate_new) (_asan_traits::__annotate_grow): New traits for annotation. (basic_string::__RAII_IncreaseAnnotator::__RAII_Increaseannotator) (basic_string::__RAII_IncreaseAnnotator::done) (basic_string::__RAII_IncreaseAnnotator::~RAII_Increaseannotator): New annotation helpers in case of exceptions. (basic_string::__get_beg, basic_string::__get_mid) (basic_string::__get_end): New annotation helpers. (basic_string::_Rep_base): Align by 8 when annotations are anabled. (basic_string::push_back): Annotate * include/bits/basic_string.tcc [_GLIBCXX_USE_DUAL_ABI] (basic_string::swap, basic_string::_M_construct) (basic_string::_M_assign, basic_string::reserve) (basic_string::_M_mutate, basic_string::_M_erase) (basic_string::resize, basic_string::_M_append) (basic_string::_M_replace): Annotate. [!_GLIBCXX_USE_DUAL_ABI] (basic_string::_S_construct) (basic_string::append(const _CharT* __s, size_type __n)) (basic_string::append(const basic_string& __str)) (basic_string::_M_destroy, basic_string::_M_mutate) (basic_string::_S_create, basic_string::_M_clone): Annotate. Disable template implicit instantiation declarations for annotations. * libstdc++-v3/include/bits/sstream.tcc (basic_stringbuf::overflow, basic_stringbuf::_M_sync): Annotate Disable template implicit instantiation declarations for annotations. * libstdc++-v3/include/bits/locale_facets.tcc: Disable template implicit instantiation declarations for annotations. * libstdc++-v3/include/bits/fstream.tcc: Likewise. --- libstdc++-v3/doc/xml/manual/using.xml | 41 ++++ libstdc++-v3/include/bits/basic_string.h | 326 +++++++++++++++++++++++++++- libstdc++-v3/include/bits/basic_string.tcc | 70 +++++- libstdc++-v3/include/bits/c++config | 28 +++ libstdc++-v3/include/bits/locale_facets.tcc | 2 + libstdc++-v3/include/bits/sstream.tcc | 8 + 6 files changed, 465 insertions(+), 10 deletions(-) diff --git a/libstdc++-v3/doc/xml/manual/using.xml b/libstdc++-v3/doc/xml/manual/using.xml index 67f9cf5..ba5af0d 100644 --- a/libstdc++-v3/doc/xml/manual/using.xml +++ b/libstdc++-v3/doc/xml/manual/using.xml @@ -992,6 +992,47 @@ g++ -Winvalid-pch -I. -include stdc++.h -H -g -O2 hello.cc -o test.exe </para> </listitem></varlistentry> + <varlistentry><term><code>_GLIBCXX_SANITIZE_STRING_PARTIAL</code></term> + <listitem> + <para> + Undefined by default. When defined, <classname>std::string</classname> + operations will be annotated so that AddressSanitizer can detect + invalid accesses to the unused capacity of a + <classname>std::string</classname>. These annotations are only + enabled for + <classname>std::basic_string<T, std::allocator<T>></classname> + and only when <classname>std::allocator</classname> is derived from + <xref linkend="allocator.impl"><classname>new_allocator</classname> + or <classname>malloc_allocator</classname></xref>. The annotations + must be present on all string operations or none, so this macro must be + defined to the same value for all translation units that create, destroy or + modify strings. With this partial sanitization CXX11 small strings wiil be not + annotated, 64-bit environment is required for COW-strings to align + character buffer correctly. + </para> + </listitem></varlistentry> + + <varlistentry><term><code>_GLIBCXX_SANITIZE_STRING_FULL</code></term> + <listitem> + <para> + Undefined by default. When defined, <classname>std::string</classname> + operations will be annotated so that AddressSanitizer can detect + invalid accesses to the unused capacity of a + <classname>std::string</classname>. These annotations are only + enabled for + <classname>std::basic_string<T, std::allocator<T>></classname> + and only when <classname>std::allocator</classname> is derived from + <xref linkend="allocator.impl"><classname>new_allocator</classname> + or <classname>malloc_allocator</classname></xref>. The annotations + must be present on all string operations or none, so this macro must + be defined to the same value for all translation units that create, + destroy or modify strings. Strings ABI is changed: pre-CXX11 string + character buffer is forced to be aligned by 8 with annotations, disable + small-string optimization for CXX11 stings to allow correct AddressSanitizer + poisoning. + </para> + </listitem></varlistentry> + <varlistentry><term><code>_GLIBCXX_SANITIZE_VECTOR</code></term> <listitem> <para> diff --git a/libstdc++-v3/include/bits/basic_string.h b/libstdc++-v3/include/bits/basic_string.h index 1fb3d42..ff49b4d 100644 --- a/libstdc++-v3/include/bits/basic_string.h +++ b/libstdc++-v3/include/bits/basic_string.h @@ -48,11 +48,84 @@ # include <string_view> #endif +#if _GLIBCXX_SANITIZER_ANNOTATE_STRING +extern "C" void +__sanitizer_annotate_contiguous_container(const void *, const void *, + const void *, const void *); +#endif namespace std _GLIBCXX_VISIBILITY(default) { _GLIBCXX_BEGIN_NAMESPACE_VERSION + template <typename _CharT, typename _Alloc> + struct _Asan_traits + { + typedef typename + __gnu_cxx::__alloc_traits<_Alloc>::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) { } + }; + + // AddressSanitizer is enabled only for default allocator. +#if _GLIBCXX_SANITIZER_ANNOTATE_STRING + template <typename _CharT> + struct _Asan_traits<_CharT, allocator<_CharT> > + { + typedef typename + __gnu_cxx::__alloc_traits<allocator<_CharT> >::const_pointer const_pointer; + + static void _GLIBCXX_VISIBILITY(hidden) __annotate_delete( + const_pointer __beg, + const_pointer __mid, + const_pointer __end) + { + __annotate_contiguous_container(__beg, __end, __mid, __end); + } + + static void _GLIBCXX_VISIBILITY(hidden) __annotate_new( + const_pointer __beg, + const_pointer __mid, + const_pointer __end) + { + __annotate_contiguous_container(__beg, __end, __end, __mid); + } + + static void _GLIBCXX_VISIBILITY(hidden) __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 _GLIBCXX_VISIBILITY(hidden) __annotate_contiguous_container( + const void* __beg, + const void* __end, + const void* __old_mid, + const void* __new_mid) + { + if (__beg) + { + __sanitizer_annotate_contiguous_container(__beg, __end, __old_mid, + __new_mid); + } + } + }; +#endif // _GLIBCXX_SANITIZER_ANNOTATE_STRING + #if _GLIBCXX_USE_CXX11_ABI _GLIBCXX_BEGIN_NAMESPACE_CXX11 /** @@ -96,11 +169,142 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 const_iterator; typedef std::reverse_iterator<const_iterator> const_reverse_iterator; typedef std::reverse_iterator<iterator> reverse_iterator; + typedef _Asan_traits<_CharT, _Char_alloc_type> asan_traits; /// Value returned by various member functions when they fail. static const size_type npos = static_cast<size_type>(-1); private: +#if _GLIBCXX_SANITIZER_ANNOTATE_STRING + // The annotation for size increase should happen before the actual + // increase, but if an exception is thrown after that the annotation has + // to be undone. + struct _GLIBCXX_VISIBILITY(hidden) _RAII_IncreaseAnnotator + { + _RAII_IncreaseAnnotator (const basic_string& __s, + difference_type __n = 1) + : __commit(false), __s(__s), __n(__n) + { + if (__n > 0) // grow before use + { + size_type __old_size = __s.size(); + if (__s._M_is_local() + && ((__s.size() + __n) > __s.capacity())) + { + __n = __s.capacity() - __s.size(); + } + __s.__annotate_grow(__old_size, __old_size + __n); + } + } + + void __done() + { + if (__n < 0) // shrink after use + { + size_type __old_size = __s.size(); + __s.__annotate_grow(__old_size, __old_size + __n); + } + __commit = true; + } + + ~_RAII_IncreaseAnnotator() + { + if (__commit) return; + size_type __cur_size = __s.size(); + if (__s._M_is_local()) + __s.__annotate_grow(__s.capacity(), __cur_size); + else + __s.__annotate_grow(__cur_size + __n, __cur_size); + } + + bool __commit; + difference_type __n; + const basic_string &__s; + }; +#else // _GLIBCXX_SANITIZER_ANNOTATE_STRING + struct _GLIBCXX_VISIBILITY(hidden) _RAII_IncreaseAnnotator + { + inline _RAII_IncreaseAnnotator(const basic_string &, size_type __n = 1) + {} + inline void __done() { } + ~_RAII_IncreaseAnnotator() { } + }; +#endif // _GLIBCXX_SANITIZER_ANNOTATE_STRING + + const_pointer _S_get_beg () const _GLIBCXX_VISIBILITY(hidden) + { + if (!_M_is_local()) + return _M_data(); + return _M_local_data(); + } + + const_pointer _S_get_mid(size_type __sz) const _GLIBCXX_VISIBILITY(hidden) + { + if (!_M_is_local()) + return _M_data() + __sz + 1 /* terminator */; + else + return _M_local_data() + __sz + 1; + } + + const_pointer _S_get_end() const _GLIBCXX_VISIBILITY(hidden) + { + if (!_M_is_local()) + return _M_data() + _M_allocated_capacity + 1; + + const_pointer __p = _S_get_beg() + _S_local_capacity + 1; + + return __p; + } + + void __annotate_new(size_type __sz) const _GLIBCXX_VISIBILITY(hidden) + { +#if _GLIBCXX_DISABLE_STRING_SSO_USAGE + if (!capacity()) + return; +#endif +#ifdef _GLIBCXX_SANITIZER_DISABLE_LOCAL_STRING_ANNOTATION + if (_M_is_local()) + return; +#endif + asan_traits::__annotate_new(_S_get_beg(), _S_get_mid(__sz), + _S_get_end()); + } + + void __annotate_grow(size_type __old_size, size_type __new_size) const + _GLIBCXX_VISIBILITY(hidden) + { +#if _GLIBCXX_DISABLE_STRING_SSO_USAGE + if (!capacity()) + return; +#endif +#ifdef _GLIBCXX_SANITIZER_DISABLE_LOCAL_STRING_ANNOTATION + if (_M_is_local()) + return; +#endif + asan_traits::__annotate_grow(_S_get_beg(), _S_get_mid(__old_size), + _S_get_mid(__new_size), _S_get_end()); + } + + basic_string<_CharT, _Traits, _Alloc> const *__annotate_delete() const + _GLIBCXX_VISIBILITY(hidden) + { +#if _GLIBCXX_DISABLE_STRING_SSO_USAGE + if (!capacity()) + return this; +#endif +#ifdef _GLIBCXX_SANITIZER_DISABLE_LOCAL_STRING_ANNOTATION + if (_M_is_local()) + return this; +#endif + if (_M_is_local()) + asan_traits::__annotate_delete(_S_get_beg(), _S_get_mid(0), + _S_get_end()); + else + asan_traits::__annotate_delete(_S_get_beg(), _S_get_mid(size()), + _S_get_end()); + return this; + } + // type used for positions in insert, erase etc. #if __cplusplus < 201103L typedef iterator __const_iterator; @@ -225,6 +429,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 { if (!_M_is_local()) _M_destroy(_M_allocated_capacity); + else + __annotate_delete(); } void @@ -234,6 +440,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 if (!_M_allocated_capacity) return; #endif + __annotate_delete(); _Alloc_traits::deallocate(_M_get_allocator(), _M_data(), __size + 1); } @@ -570,8 +777,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 #if !_GLIBCXX_DISABLE_STRING_SSO_USAGE if (__str._M_is_local()) { + __str.__annotate_delete(); traits_type::copy(_M_local_buf, __str._M_local_buf, _S_local_capacity + 1); + __annotate_new(__str.length()); } else #endif @@ -592,6 +801,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 __str._M_data(__str._M_local_data()); __str._M_set_length(0); #endif + __str.__annotate_new(0); } /** @@ -617,6 +827,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 traits_type::copy(_M_local_buf, __str._M_local_buf, _S_local_capacity + 1); _M_length(__str.length()); + __annotate_new(__str.length()); __str._M_set_length(0); } else @@ -635,7 +846,6 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 __str._M_data(__str._M_local_buf); __str._M_set_length(0); #endif - } else _M_construct(__str.begin(), __str.end()); @@ -739,6 +949,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 _M_data(__ptr); _M_capacity(__new_cap); _M_set_length(__len); + __annotate_new(__len); } } std::__alloc_on_copy(_M_get_allocator(), __str._M_get_allocator()); @@ -783,6 +994,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 operator=(basic_string&& __str) noexcept(_Alloc_traits::_S_nothrow_move()) { +#if _GLIBCXX_SANITIZER_ANNOTATE_STRING + size_type __str_capacity = __str.capacity(); + size_type __str_length = __str.length(); +#endif if (!_M_is_local() && _Alloc_traits::_S_propagate_on_move_assign() && !_Alloc_traits::_S_always_equal() && _M_get_allocator() != __str._M_get_allocator()) @@ -795,6 +1010,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 #else _M_data(_M_local_data()); _M_set_length(0); + __annotate_new(0); #endif } // Replace allocator if POCMA is true. @@ -806,16 +1022,18 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 { pointer __data = nullptr; size_type __capacity; -#if _GLIBCXX_DISABLE_STRING_SSO_USAGE +#if _GLIBCXX_DISABLE_STRING_SSO_USAGE || _GLIBCXX_SANITIZER_ANNOTATE_STRING size_type __length; #endif + __annotate_delete(); + __str.__annotate_delete(); if (!_M_is_local()) { if (_Alloc_traits::_S_always_equal()) { __data = _M_data(); __capacity = _M_allocated_capacity; -#if _GLIBCXX_DISABLE_STRING_SSO_USAGE +#if _GLIBCXX_DISABLE_STRING_SSO_USAGE || _GLIBCXX_SANITIZER_ANNOTATE_STRING __length = _M_string_length; #endif } @@ -835,8 +1053,9 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 { __str._M_data(__data); __str._M_capacity(__capacity); -#if _GLIBCXX_DISABLE_STRING_SSO_USAGE +#if _GLIBCXX_DISABLE_STRING_SSO_USAGE || _GLIBCXX_SANITIZER_ANNOTATE_STRING __str._M_length(__length); + __str.__annotate_new(__str.length()); #endif } else { @@ -846,8 +1065,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 __str._M_capacity(0); #else __str._M_data(__str._M_local_buf); + __str.__annotate_new(__str.length()); #endif } + __annotate_new(length()); } else assign(__str); @@ -1090,6 +1311,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 void clear() _GLIBCXX_NOEXCEPT { + __annotate_grow(_M_string_length, 0); #if _GLIBCXX_DISABLE_STRING_SSO_USAGE _M_allocated_capacity ? _M_set_length(0) : _M_length(0); #else @@ -1425,6 +1647,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 const size_type __size = this->size(); if (__size + 1 > this->capacity()) this->_M_mutate(__size, size_type(0), 0, size_type(1)); + this->__annotate_grow(__size, __size + 1); traits_type::assign(this->_M_data()[__size], __c); this->_M_set_length(__size + 1); } @@ -3223,13 +3446,57 @@ _GLIBCXX_END_NAMESPACE_CXX11 size_type _M_length; size_type _M_capacity; _Atomic_word _M_refcount; - }; + } _GLIBCXX_SANITIZER_ALIGN_COW_STRING; struct _Rep : _Rep_base { // Types: typedef typename _Alloc::template rebind<char>::other _Raw_bytes_alloc; + typedef _Asan_traits<_CharT, _CharT_alloc_type> asan_traits; + #if _GLIBCXX_SANITIZE_ANNOTATE_STRING + const_pointer _S_get_beg() const _GLIBCXX_NOEXCEPT + { + return _M_refdata(); + } + const_pointer _S_get_mid(size_type __sz) const _GLIBCXX_NOEXCEPT + { + return std::min(_M_refdata() + __sz + 1, /* terminator */ + _S_get_end()); + } + + const_pointer _S_get_end() const _GLIBCXX_NOEXCEPT + { + return _M_refdata() + this->_M_capacity + 1 /* terminator */; + } + + void __annotate_new(size_type __sz) const + { + asan_traits::__annotate_new(_S_get_beg(), _S_get_mid(__sz), + _S_get_end()); + } + + void __annotate_grow(size_type __old_size, size_type __new_size) const + { + asan_traits::__annotate_grow(_S_get_beg(), _S_get_mid(__old_size), + _S_get_mid(__new_size), _S_get_end()); + } + + _Rep const *__annotate_delete() const + { + asan_traits::__annotate_delete(_S_get_beg(), + _S_get_mid(this->_M_length), + _S_get_end()); + return this; + } +#else // _GLIBCXX_SANITIZE_ANNOTATE_STRING + const_pointer _S_get_beg() const _GLIBCXX_NOEXCEPT { return 0; } + const_pointer _S_get_mid(size_type __sz) const _GLIBCXX_NOEXCEPT { return 0; } + const_pointer _S_get_end() const _GLIBCXX_NOEXCEPT { return 0; } + void __annotate_new(size_type __sz) const { } + void __annotate_grow(size_type __old_size, size_type __new_size) const { } + _Rep const *__annotate_delete() const { return this; } +#endif // _GLIBCXX_SANITIZE_ANNOTATE_STRING // (Public) Data members: // The maximum number of individual char_type elements of an @@ -3312,6 +3579,10 @@ _GLIBCXX_END_NAMESPACE_CXX11 } } + const_pointer + _M_refdata() const throw() + { return reinterpret_cast<const_pointer>(this + 1); } + _CharT* _M_refdata() throw() { return reinterpret_cast<_CharT*>(this + 1); } @@ -3370,6 +3641,50 @@ _GLIBCXX_END_NAMESPACE_CXX11 _M_clone(const _Alloc&, size_type __res = 0); }; +#if _GLIBCXX_SANITIZER_ANNOTATE_STRING + // The annotation for size increase should happen before the actual + // increase, but if an exception is thrown after that the annotation has + // to be undone. + struct _GLIBCXX_VISIBILITY(hidden) _RAII_IncreaseAnnotator + { + _RAII_IncreaseAnnotator (const _Rep& __s, difference_type __n = 1) + : __commit(false), __s(__s), __n(__n) + { + if (__n > 0) // grow before use + { + size_type __old_size = __s._M_length; + __s.__annotate_grow(__old_size, __old_size + __n); + } + } + void __done() + { + if (__n < 0) // shrink after use + { + size_type __old_size = __s._M_length; + __s.__annotate_grow(__old_size, __old_size + __n); + } + __commit = true; + } + ~_RAII_IncreaseAnnotator() + { + if (__commit) return; + size_type __cur_size = __s._M_length; + __s.__annotate_grow(__cur_size + __n, __cur_size); + } + bool __commit; + difference_type __n; + const _Rep &__s; + }; +#else // _GLIBCXX_SANITIZER_ANNOTATE_STRING + struct _GLIBCXX_VISIBILITY(hidden) _RAII_IncreaseAnnotator + { + inline _RAII_IncreaseAnnotator(const _Rep &, size_type __n = 1) + {} + inline void __done() {} + ~_RAII_IncreaseAnnotator() {} + }; +#endif // _GLIBCXX_SANITIZER_ANNOTATE_STRING + // Use empty-base optimization: http://www.cantrip.org/emptyopt.html struct _Alloc_hider : _Alloc { @@ -4335,6 +4650,7 @@ _GLIBCXX_END_NAMESPACE_CXX11 push_back(_CharT __c) { const size_type __len = 1 + this->size(); + _M_rep()->__annotate_grow(this->size(), this->size() + 1); if (__len > this->capacity() || _M_rep()->_M_is_shared()) this->reserve(__len); traits_type::assign(_M_data()[this->size()], __c); diff --git a/libstdc++-v3/include/bits/basic_string.tcc b/libstdc++-v3/include/bits/basic_string.tcc index 259baec..52d1f67 100644 --- a/libstdc++-v3/include/bits/basic_string.tcc +++ b/libstdc++-v3/include/bits/basic_string.tcc @@ -62,6 +62,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return; _Alloc_traits::_S_on_swap(_M_get_allocator(), __s._M_get_allocator()); + __annotate_delete(); + __s.__annotate_delete(); if (_M_is_local()) if (__s._M_is_local()) @@ -124,7 +126,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION const size_type __tmp_length = length(); _M_length(__s.length()); + __annotate_new(length()); __s._M_length(__tmp_length); + __s.__annotate_new(__s.length()); } template<typename _CharT, typename _Traits, typename _Alloc> @@ -175,8 +179,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION while (__beg != __end && __len < __capacity) { + _RAII_IncreaseAnnotator __annotator(*this); _M_data()[__len++] = *__beg; ++__beg; + __annotator.__done(); } __try @@ -196,9 +202,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _M_dispose(); _M_data(__another); _M_capacity(__capacity); + __annotate_new(__len); } + _RAII_IncreaseAnnotator __annotator(*this); _M_data()[__len++] = *__beg; ++__beg; + __annotator.__done(); } } __catch(...) @@ -235,6 +244,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { _M_data(_M_create(__new_capacity, size_type(0))); _M_capacity(__new_capacity); + __annotate_new(__dnew); } // Check for out_of_range and length_error exceptions. @@ -266,6 +276,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { _M_data(_M_create(__new_capacity, size_type(0))); _M_capacity(__new_capacity); + __annotate_new(__n); } if (__n) @@ -281,8 +292,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { if (this != &__str) { + __annotate_delete(); const size_type __rsize = __str.length(); +#if _GLIBCXX_DISABLE_STRING_SSO_USAGE + const size_type __capacity = _M_allocated_capacity; +#else const size_type __capacity = capacity(); +#endif if (__rsize > __capacity) { @@ -301,6 +317,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #else _M_set_length(__rsize); #endif + __annotate_new(__rsize); } } @@ -335,6 +352,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _M_data(_M_local_data()); } #endif + __annotate_new(length()); } } @@ -360,6 +378,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _M_dispose(); _M_data(__r); _M_capacity(__new_capacity); + __annotate_new(__pos + __len2 + __how_much); } template<typename _CharT, typename _Traits, typename _Alloc> @@ -372,7 +391,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION if (__how_much && __n) this->_S_move(_M_data() + __pos, _M_data() + __pos + __n, __how_much); + const size_type __old_length = length(); _M_set_length(length() - __n); + __annotate_grow(__old_length, __old_length - __n); } template<typename _CharT, typename _Traits, typename _Alloc> @@ -384,7 +405,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION if (__size < __n) this->append(__n - __size, __c); else if (__n < __size) - this->_M_set_length(__n); + { + this->_M_set_length(__n); + this->__annotate_grow(__size, __n); + } } template<typename _CharT, typename _Traits, typename _Alloc> @@ -392,12 +416,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION basic_string<_CharT, _Traits, _Alloc>:: _M_append(const _CharT* __s, size_type __n) { - const size_type __len = __n + this->size(); + const size_type __old_len = this->size(); + const size_type __len = __n + __old_len; if (__len <= this->capacity()) { if (__n) - this->_S_copy(this->_M_data() + this->size(), __s, __n); + { + __annotate_grow(__old_len, __len); + this->_S_copy(this->_M_data() + __old_len, __s, __n); + } } else this->_M_mutate(this->size(), size_type(0), __s, __n); @@ -436,8 +464,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION pointer __p = this->_M_data() + __pos1; const size_type __how_much = __old_size - __pos1 - __n1; + _RAII_IncreaseAnnotator __annotator(*this, __n2 - __n1); if (__how_much && __n1 != __n2) this->_S_move(__p + __n2, __p + __n1, __how_much); + __annotator.__done(); } else this->_M_mutate(__pos1, __n1, 0, __n2); @@ -462,6 +492,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION if (__new_size <= this->capacity()) { + // grow before use + if (__old_size < __new_size) + __annotate_grow(__old_size, __new_size); pointer __p = this->_M_data() + __pos; const size_type __how_much = __old_size - __pos - __len1; @@ -494,6 +527,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } } } + // shrink after use + if (__old_size > __new_size) { + __annotate_grow(__old_size, __new_size); + } } else this->_M_mutate(__pos, __len1, __s, __len2); @@ -591,6 +628,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __r->_M_destroy(__a); __throw_exception_again; } + __r->__annotate_new(__len); __r->_M_set_length_and_sharable(__len); return __r->_M_refdata(); } @@ -621,6 +659,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __r->_M_destroy(__a); __throw_exception_again; } + __r->__annotate_new(__dnew); __r->_M_set_length_and_sharable(__dnew); return __r->_M_refdata(); } @@ -636,6 +675,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #endif // Check for out_of_range and length_error exceptions. _Rep* __r = _Rep::_S_create(__n, size_type(0), __a); + __r->__annotate_new(__n); if (__n) _M_assign(__r->_M_refdata(), __n, __c); @@ -771,10 +811,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION if (__n) { _M_check_length(size_type(0), __n, "basic_string::append"); - const size_type __len = __n + this->size(); + const size_type __old_len = this->size(); + const size_type __len = __n + __old_len; if (__len > this->capacity() || _M_rep()->_M_is_shared()) this->reserve(__len); + _RAII_IncreaseAnnotator __annotator(*_M_rep(), __n); _M_assign(_M_data() + this->size(), __n, __c); + __annotator.__done(); _M_rep()->_M_set_length_and_sharable(__len); } return *this; @@ -801,6 +844,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __s = _M_data() + __off; } } + _M_rep()->__annotate_grow(this->size(), __len); _M_copy(_M_data() + this->size(), __s, __n); _M_rep()->_M_set_length_and_sharable(__len); } @@ -815,9 +859,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION const size_type __size = __str.size(); if (__size) { - const size_type __len = __size + this->size(); + const size_type __old_len = this->size(); + const size_type __len = __size + __old_len; if (__len > this->capacity() || _M_rep()->_M_is_shared()) this->reserve(__len); + _M_rep()->__annotate_grow(__old_len, __len); _M_copy(_M_data() + this->size(), __str._M_data(), __size); _M_rep()->_M_set_length_and_sharable(__len); } @@ -934,6 +980,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { const size_type __size = sizeof(_Rep_base) + (this->_M_capacity + 1) * sizeof(_CharT); + __annotate_delete(); _Raw_bytes_alloc(__a).deallocate(reinterpret_cast<char*>(this), __size); } @@ -965,12 +1012,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // Must reallocate. const allocator_type __a = get_allocator(); _Rep* __r = _Rep::_S_create(__new_size, this->capacity(), __a); + __r->_M_length = 0; + __r->__annotate_new(0); + _RAII_IncreaseAnnotator __annotator(*__r, __new_size); if (__pos) _M_copy(__r->_M_refdata(), _M_data(), __pos); if (__how_much) _M_copy(__r->_M_refdata() + __pos + __len2, _M_data() + __pos + __len1, __how_much); + __annotator.__done(); _M_rep()->_M_dispose(__a); _M_data(__r->_M_refdata()); @@ -978,9 +1029,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION else if (__how_much && __len1 != __len2) { // Work in-place. + if (__new_size > __old_size) + _M_rep()->__annotate_grow(__old_size, __new_size); _M_move(_M_data() + __pos + __len2, _M_data() + __pos + __len1, __how_much); + if (__new_size < __old_size) + _M_rep()->__annotate_grow(__old_size, __new_size); } + else + _M_rep()->__annotate_grow(__old_size, __new_size); _M_rep()->_M_set_length_and_sharable(__new_size); } @@ -1116,6 +1173,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION const size_type __requested_cap = this->_M_length + __res; _Rep* __r = _Rep::_S_create(__requested_cap, this->_M_capacity, __alloc); + __r->__annotate_new(this->_M_length); if (this->_M_length) _M_copy(__r->_M_refdata(), _M_refdata(), this->_M_length); @@ -1636,6 +1694,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // Inhibit implicit instantiations for required instantiations, // which are defined via explicit instantiations elsewhere. +#if !_GLIBCXX_SANITIZE_STRING #if _GLIBCXX_EXTERN_TEMPLATE // The explicit instantiations definitions in src/c++11/string-inst.cc // are compiled as C++14, so the new C++17 members aren't instantiated. @@ -1686,6 +1745,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION getline(basic_istream<wchar_t>&, wstring&); #endif // _GLIBCXX_USE_WCHAR_T #endif // _GLIBCXX_EXTERN_TEMPLATE +#endif // !_GLIBCXX_SANITIZE_STRING _GLIBCXX_END_NAMESPACE_VERSION } // namespace std diff --git a/libstdc++-v3/include/bits/c++config b/libstdc++-v3/include/bits/c++config index c0b89f4..bd90524 100644 --- a/libstdc++-v3/include/bits/c++config +++ b/libstdc++-v3/include/bits/c++config @@ -428,6 +428,34 @@ namespace std # define _GLIBCXX_END_NAMESPACE_LDBL_OR_CXX11 _GLIBCXX_END_NAMESPACE_LDBL #endif +// Partial AddressSanitizer std::basic_string annotation: +// ABI is unchanged, short strings are not sanitized, +// works with cow-strings only under 64-bits environment. +#if _GLIBCXX_SANITIZE_STRING_PARTIAL +# define _GLIBCXX_SANITIZE_STRING 1 +# define _GLIBCXX_DISABLE_STRING_SSO_USAGE 0 +# define _GLIBCXX_SANITIZER_ALIGN_COW_STRING +# if _GLIBCXX_SANITIZE_STD_ALLOCATOR +# define _GLIBCXX_SANITIZER_ANNOTATE_STRING 1 +# define _GLIBCXX_SANITIZER_DISABLE_LOCAL_STRING_ANNOTATION +# endif +#endif + +// Full AddressSanitizer std::basic_string annotation: ABI is changed. +#if _GLIBCXX_SANITIZE_STRING_FULL +# define _GLIBCXX_SANITIZE_STRING 1 +# define _GLIBCXX_DISABLE_STRING_SSO_USAGE 1 +# define _GLIBCXX_SANITIZER_ALIGN_COW_STRING __attribute__((aligned(8))) +# if _GLIBCXX_SANITIZE_STD_ALLOCATOR +# define _GLIBCXX_SANITIZER_ANNOTATE_STRING 1 +# undef _GLIBCXX_SANITIZER_DISABLE_LOCAL_STRING_ANNOTATION +# endif +#endif + +#if !_GLIBCXX_SANITIZE_STRING +# define _GLIBCXX_SANITIZER_ALIGN_COW_STRING +#endif + // Debug Mode implies checking assertions. #if defined(_GLIBCXX_DEBUG) && !defined(_GLIBCXX_ASSERTIONS) # define _GLIBCXX_ASSERTIONS 1 diff --git a/libstdc++-v3/include/bits/locale_facets.tcc b/libstdc++-v3/include/bits/locale_facets.tcc index 39da5766..31ee6a4 100644 --- a/libstdc++-v3/include/bits/locale_facets.tcc +++ b/libstdc++-v3/include/bits/locale_facets.tcc @@ -1291,6 +1291,7 @@ _GLIBCXX_END_NAMESPACE_LDBL // Inhibit implicit instantiations for required instantiations, // which are defined via explicit instantiations elsewhere. +#if !_GLIBCXX_SANITIZE_STRING #if _GLIBCXX_EXTERN_TEMPLATE extern template class _GLIBCXX_NAMESPACE_CXX11 numpunct<char>; extern template class _GLIBCXX_NAMESPACE_CXX11 numpunct_byname<char>; @@ -1370,6 +1371,7 @@ _GLIBCXX_END_NAMESPACE_LDBL has_facet<num_get<wchar_t> >(const locale&); #endif #endif +#endif // !_GLIBCXX_SANITIZE_STRING _GLIBCXX_END_NAMESPACE_VERSION } // namespace diff --git a/libstdc++-v3/include/bits/sstream.tcc b/libstdc++-v3/include/bits/sstream.tcc index 5f90b76..ca1363b 100644 --- a/libstdc++-v3/include/bits/sstream.tcc +++ b/libstdc++-v3/include/bits/sstream.tcc @@ -92,6 +92,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #if _GLIBCXX_USE_CXX11_ABI if ((this->epptr() - this->pbase()) < __capacity) { +#if _GLIBCXX_SANITIZER_ANNOTATE_STRING + _M_string.__annotate_grow(_M_string.size(), __capacity); +#endif // There is additional capacity in _M_string that can be used. char_type* __base = const_cast<char_type*>(_M_string.data()); _M_pbump(__base, __base + __capacity, this->pptr() - this->pbase()); @@ -269,6 +272,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION if (!__testin) this->setg(__endg, __endg, __endg); } +#if _GLIBCXX_USE_CXX11_ABI && _GLIBCXX_SANITIZER_ANNOTATE_STRING + _M_string.__annotate_grow(_M_string.size(), _M_string.capacity()); +#endif } template <class _CharT, class _Traits, class _Alloc> @@ -287,6 +293,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // Inhibit implicit instantiations for required instantiations, // which are defined via explicit instantiations elsewhere. +#if !_GLIBCXX_SANITIZE_STRING #if _GLIBCXX_EXTERN_TEMPLATE extern template class basic_stringbuf<char>; extern template class basic_istringstream<char>; @@ -300,6 +307,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION extern template class basic_stringstream<wchar_t>; #endif #endif +#endif // !_GLIBCXX_SANITIZE_STRING _GLIBCXX_END_NAMESPACE_VERSION } // namespace std -- 2.7.4
From 327640848aa4868683d4f85cc962d0122d0e5e65 Mon Sep 17 00:00:00 2001 From: Mikhail Kashkarov <m.kashka...@partner.samsung.com> Date: Fri, 13 Jul 2018 21:56:33 +0300 Subject: [PATCH 3/3] Add AddressSanitizer testsuite for std::string annotations. --- libstdc++-v3/testsuite/asan/string_annotation.cc | 429 +++++++++++++++++++++++ libstdc++-v3/testsuite/libstdc++-asan/asan.exp | 72 ++++ libstdc++-v3/testsuite/util/testsuite_asan.h | 105 ++++++ 3 files changed, 606 insertions(+) create mode 100644 libstdc++-v3/testsuite/asan/string_annotation.cc create mode 100644 libstdc++-v3/testsuite/libstdc++-asan/asan.exp create mode 100644 libstdc++-v3/testsuite/util/testsuite_asan.h diff --git a/libstdc++-v3/testsuite/asan/string_annotation.cc b/libstdc++-v3/testsuite/asan/string_annotation.cc new file mode 100644 index 0000000..f9b6a63 --- /dev/null +++ b/libstdc++-v3/testsuite/asan/string_annotation.cc @@ -0,0 +1,429 @@ +// -*- C++ -*- + +// Copyright (C) 2016-2018 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +// { dg-options "-O0" } + +#include <testsuite_hooks.h> +#include "testsuite_asan.h" + +#include <string> + +struct AsanStringVerifier +{ + typedef char char_type; + typedef std::allocator<char_type> alloc_type; + typedef std::basic_string<char_type, std::char_traits<char_type>, alloc_type> + string_type; + using list = std::initializer_list<char_type>; + + // from 21_strings/basic_string/cons/char/8.cc + struct TestBaseObjCtor : string_type + { + template <typename... Args> + TestBaseObjCtor(Args &&... args) + : string_type(std::forward<Args>(args)...) + {} + }; + + template <typename... Args> + bool construct(Args &&... args) + { + TestBaseObjCtor as_base_obj(static_cast<Args>(args)...); + string_type as_complete_obj(std::forward<Args>(args)...); + return is_asan_string_correct(as_complete_obj); + } + + void test_ctors_small() + { + alloc_type alloc; + const string_type lvalue_small(6, 'x'); + + VERIFY(construct()); + VERIFY(construct(alloc)); + + // small size params to fit into SSO + VERIFY(construct(lvalue_small)); + VERIFY(construct(string_type{"rvalue"})); + VERIFY(construct(lvalue_small, 2)); + VERIFY(construct(lvalue_small, 2, alloc)); + VERIFY(construct(lvalue_small, 2, 3)); + VERIFY(construct(lvalue_small, 2, 3, alloc)); + VERIFY(construct("C string", 4)); + VERIFY(construct("C string", 4, alloc)); + VERIFY(construct("C string")); + VERIFY(construct("C string", alloc)); + VERIFY(construct(5, 'x')); + VERIFY(construct(5, 'x', alloc)); + VERIFY(construct(lvalue_small.begin(), lvalue_small.end())); + VERIFY(construct(lvalue_small.begin(), lvalue_small.end(), alloc)); + VERIFY(construct(list{'l', 'i', 's', 't'})); + VERIFY(construct(list{'l', 'i', 's', 't'}, alloc)); +#if _GLIBCXX_USE_CXX11_ABI + VERIFY(construct(lvalue_small, alloc)); + VERIFY(construct(string_type{"rvalue"}, alloc)); +#endif + } + + void test_ctors_long() + { + alloc_type alloc; + const string_type lvalue_long = "lvalue long enough for no-sso string"; + list long_list = {'l', 'o', 'n', 'g', ' ', 'i', 'n', 'i', 't', 'i', + 'a', 'l', 'i', 'z', 'e', 'r', ' ', 'l', 'i', 's', + 't', ' ', 'f', 'o', 'r', ' ', 'n', 'o', 'n', '-', + 's', 's', 'o', ' ', 's', 't', 'r', 'i', 'n', 'g'}; + + // Should not fit into SSO buffer + VERIFY(construct(lvalue_long)); + VERIFY(construct(string_type{"rvalue long enought for no-sso string"})); + VERIFY(construct(lvalue_long, 2)); + VERIFY(construct(lvalue_long, 2, alloc)); + VERIFY(construct(lvalue_long, 2, 24)); + VERIFY(construct(lvalue_long, 2, 24, alloc)); + VERIFY(construct("C string long enought for no-sso string", 24)); + VERIFY(construct("C string long enought for no-sso string", 24, alloc)); + VERIFY(construct("C string long enought for no-sso string")); + VERIFY(construct("C string long enought for no-sso string", alloc)); + VERIFY(construct(24, 'x')); + VERIFY(construct(24, 'x', alloc)); + VERIFY(construct(lvalue_long.begin(), lvalue_long.end())); + VERIFY(construct(lvalue_long.begin(), lvalue_long.end(), alloc)); + VERIFY(construct(long_list)); + VERIFY(construct(long_list, alloc)); +#if _GLIBCXX_USE_CXX11_ABI + VERIFY(construct(lvalue_long, alloc)); + VERIFY( + construct(string_type{"rvalue long enought for no-sso string"}, alloc)); +#endif + } + + const size_t max_steps = 128; + + template <class Func> + void ApplyForStrings(Func func) + { + string_type str_used; + for (size_t i = 1; i < max_steps; ++i) + { + string_type str_tmp(i, static_cast<char_type>('x')); + + func(str_used, i); + VERIFY(is_asan_string_correct(str_used)); + + func(str_tmp, i); + VERIFY(is_asan_string_correct(str_tmp)); + } + } + + void test_operators_equal() + { + ApplyForStrings([](string_type &str, size_t step) + { + string_type tmp = string_type(step, static_cast<char_type>('x')); + + // operator= (string&) + str = tmp; + VERIFY(is_asan_string_correct(str)); + + // operator= (const char*) + str = tmp.c_str(); + VERIFY(is_asan_string_correct(str)); + + // operator= (string&&) + str = string_type{str.c_str()}; + VERIFY(is_asan_string_correct(str)); + }); + + string_type str; + + // operator=(initializer_list<_CharT> __l) + list short_list = {'l', 'i', 's', 't'}; + str = short_list; + VERIFY(is_asan_string_correct(str)); + + list long_list = {'l', 'o', 'n', 'g', ' ', 'i', 'n', 'i', 't', 'i', + 'a', 'l', 'i', 'z', 'e', 'r', ' ', 'l', 'i', 's', + 't', ' ', 'f', 'o', 'r', ' ', 'n', 'o', 'n', '-', + 's', 's', 'o', ' ', 's', 't', 'r', 'i', 'n', 'g'}; + str = long_list; + VERIFY(is_asan_string_correct(str)); + + // operator=(char) + str = static_cast<char_type>('x'); + VERIFY(is_asan_string_correct(str)); + } + + void test_operators_assign() + { + fprintf(stderr, "operator+=(const basiC_string&)\n"); + string_type tmp("tmp"); + ApplyForStrings([&tmp](string_type &str, size_t step) + { + str += tmp; + }); + + fprintf(stderr, "operator+=(const _CharT*)\n"); + ApplyForStrings([](string_type &str, size_t step) + { + str += "str"; + }); + + fprintf(stderr, "operator+=(_CharT)\n"); + ApplyForStrings([](string_type &str, size_t step) + { + str += static_cast<char_type>('x'); + }); + +#if _GLIBCXX_USE_CXX11_ABI + fprintf(stderr, "operator+=(initializer_list<_CharT>\n"); + list list = {'l'}; + ApplyForStrings([&list](string_type &str, size_t step) + { + str += list; + }); +#endif + } + + void test_append() + { + { + fprintf(stderr, "append(const basic_string&)\n"); + string_type tmp(1, static_cast<char_type>('x')); + ApplyForStrings([&tmp](string_type &str, size_t step) + { + str.append(tmp); + }); + } + + { + fprintf(stderr, "append(const basic_string&, size_type, size_type)\n"); + string_type tmp(max_steps, static_cast<char_type>('x')); + ApplyForStrings([&](string_type &str, size_t step) + { + str.append(tmp, step, max_steps - step); + }); + } + + { + fprintf(stderr, "append(const _CHarT*, size_type)\n"); + string_type tmp(max_steps, static_cast<char_type>('x')); + ApplyForStrings([&](string_type &str, size_t step) + { + str.append(tmp.c_str(), step); + }); + } + + { + fprintf(stderr, "append(const _CHarT*)\n"); + string_type tmp(1, static_cast<char_type>('x')); + ApplyForStrings([&](string_type &str, size_t step) + { + str.append(tmp.c_str()); + }); + } + + { + fprintf(stderr, "append(size_type, _CHarT)\n"); + char_type ch = static_cast<char_type>('x'); + ApplyForStrings([&](string_type &str, size_t step) + { + str.append(step, ch); + }); + } + + { +#if _GLIBCXX_USE_CXX11_ABI + fprintf(stderr, "append(initialize_list)\n"); + list lst = {'l'}; + ApplyForStrings([&](string_type &str, size_t step) + { + str.append(lst); + }); +#endif + } + + { + fprintf(stderr, "append(_InputIterator, _InpurIterator)\n"); + string_type tmp(max_steps, static_cast<char_type>('x')); + ApplyForStrings([&tmp](string_type &str, size_t step) + { + str.append(tmp.begin() + step, tmp.end()); + }); + } + } + + void test_push_back() + { + fprintf(stderr, "push_back(_CharT)\n"); + ApplyForStrings([](string_type &str, size_t step) + { + str.push_back(static_cast<char_type>('x')); + }); + } + + void test_swap() + { + fprintf(stderr, "swap(basic_string&)\n"); + ApplyForStrings([](string_type &str, size_t step) + { + string_type tmp(step, static_cast<char_type>('x')); + str.swap(tmp); + }); + } + + void test_assign() + { + { + fprintf(stderr, "assign(const basic_string&)\n"); + ApplyForStrings([](string_type &str, size_t step) + { + string_type tmp(step, static_cast<char_type>('x')); + str.assign(tmp); + }); + } + +#if _GLIBCXX_USE_CXX11_ABI + { + fprintf(stderr, "assign(const basic_string&&)\n"); + ApplyForStrings([](string_type &str, size_t step) + { + str.assign(string_type(step, static_cast<char_type>('x'))); + }); + } +#endif + + { + fprintf(stderr, "assign(const basic_string&, size_type, size_type)\n"); + string_type tmp(max_steps, static_cast<char_type>('x')); + ApplyForStrings([&](string_type &str, size_t step) + { + str.assign(tmp, step, max_steps - step); + }); + } + + { + fprintf(stderr, "assign(const _CharT*, size_type)\n"); + string_type tmp(max_steps, static_cast<char_type>('x')); + ApplyForStrings([&tmp](string_type &str, size_t step) + { + str.assign(tmp.c_str(), step); + }); + } + + { + fprintf(stderr, "assign(const _CharT*)\n"); + ApplyForStrings([](string_type &str, size_t step) + { + string_type tmp(step, static_cast<char_type>('x')); + str.assign(tmp.c_str()); + }); + } + + { + fprintf(stderr, "assign(size_type, const _CharT*)\n"); + ApplyForStrings([](string_type &str, size_t step) + { + str.assign(step, static_cast<char_type>('x')); + }); + } + + { + fprintf(stderr, "assign(_InputIterator, _InputIterator)\n"); + string_type tmp(max_steps, static_cast<char_type>('x')); + ApplyForStrings([&tmp](string_type &str, size_t step) + { + str.assign(tmp.begin(), tmp.begin() + step); + }); + } + +#if _GLIBCXX_USE_CXX11_ABI + { + fprintf(stderr, "assign(initializer_list<_CharT>)\n"); + list lst = {'l', 'i', 's', 't'}; + string_type str; + str.assign(lst); + } +#endif + } + + void test_erase() + { + fprintf(stderr, "erase(size_type, size_type)\n"); + ApplyForStrings([&](string_type &str, size_t step) + { + str.erase(0, str.size()); + }); + } + + void test_resize() + { + fprintf(stderr, "resize(size_type)\n"); + ApplyForStrings([&](string_type &str, size_t step) + { + str.resize(step); + }); + + fprintf(stderr, "resize(size_type, _CharT)\n"); + char_type ch = static_cast<char_type>('x'); + ApplyForStrings([&](string_type &str, size_t step) + { + str.resize(step, ch); + }); + } + + void test_clear() + { + fprintf(stderr, "clear()\n"); + ApplyForStrings([&](string_type &str, size_t step) + { + str.clear(); + }); + } + + void test_reserve() + { + fprintf(stderr, "reserve(size_type_)\n"); + ApplyForStrings([&](string_type &str, size_t step) + { + str.reserve(step); + }); + } + + void run_tests() + { + test_ctors_small(); + test_ctors_long(); + test_append(); + test_erase(); + test_clear(); + test_operators_assign(); + test_operators_equal(); + test_reserve(); + test_resize(); + test_push_back(); + test_swap(); + test_assign(); + } +}; + +int main() +{ + AsanStringVerifier asv_char; + asv_char.run_tests(); +} diff --git a/libstdc++-v3/testsuite/libstdc++-asan/asan.exp b/libstdc++-v3/testsuite/libstdc++-asan/asan.exp new file mode 100644 index 0000000..fad79c9 --- /dev/null +++ b/libstdc++-v3/testsuite/libstdc++-asan/asan.exp @@ -0,0 +1,72 @@ +# Copyright (C) 2013-2018 Free Software Foundation, Inc. +# +# This file is part of GCC. +# +# GCC is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GCC is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GCC; see the file COPYING3. If not see +# <http://www.gnu.org/licenses/>. + +# libstdc++-v3 testsuite that uses the 'dg.exp' driver. + +# +# asan_link_flags -- compute library path and flags to find libasan. +# (originally from tsan-dg.exp) +# + +proc asan_link_flags { paths } { + global srcdir + global ld_library_path + global shlib_ext + global asan_saved_library_path + + set gccpath ${paths} + set flags "" + + set shlib_ext [get_shlib_extension] + set asan_saved_library_path $ld_library_path + + if { $gccpath != "" } { + if { [file exists "${gccpath}/libsanitizer/asan/.libs/libasan.a"] + || [file exists "${gccpath}/libsanitizer/asan/.libs/libasan.${shlib_ext}"] } { + append flags " -B${gccpath}/libsanitizer/asan/ " + append flags " -L${gccpath}/libsanitizer/asan/.libs " + append ld_library_path ":${gccpath}/libsanitizer/asan/.libs" + } + } else { + global tool_root_dir + + set libasan [lookfor_file ${tool_root_dir} libasan] + if { $libasan != "" } { + append flags "-L${libasan} " + append ld_library_path ":${libasan}" + } + } + + set_ld_library_path_env_vars + + return "$flags" +} + +dg-init + +v3-build_support + +set tests [lsort [find $srcdir/asan *.cc]] + +set link_flags "[asan_link_flags [get_multilibs]]" +set asan_flags " -fsanitize=address" + +dg-runtest $tests "$asan_flags $link_flags" "" + +# All done. +dg-finish diff --git a/libstdc++-v3/testsuite/util/testsuite_asan.h b/libstdc++-v3/testsuite/util/testsuite_asan.h new file mode 100644 index 0000000..26f6cee --- /dev/null +++ b/libstdc++-v3/testsuite/util/testsuite_asan.h @@ -0,0 +1,105 @@ +// -*- C++ -*- + +// Testing AddressSanitizer container annotaions. +// +// Copyright (C) 2003-2018 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. +// + +#ifndef _GLIBCXX_TESTSUITE_ASAN_H +#define _GLIBCXX_TESTSUITE_ASAN_H + +#include <string> + +#ifdef __SANITIZE_ADDRESS__ +# ifdef _GLIBCXX_SANITIZE_STRING +# define TESTSUITE_ASAN_VERIFY_STRINGS +# endif +#endif + +#ifdef TESTSUITE_ASAN_VERIFY_STRINGS + +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); + +extern "C" int __asan_address_is_poisoned(void const volatile *addr); + +void print_bad_asan_container(const void *beg, const void *mid, + const void *end, const void *bad_addr) +{ + fprintf(stderr, "ERROR: bad container annotation\n"); + fprintf(stderr, "size: [%p, %p)\n", beg, mid); + fprintf(stderr, "capacity: [%p, %p)\n", mid, end); + std::ptrdiff_t offset = reinterpret_cast<std::ptrdiff_t>(bad_addr) - + reinterpret_cast<std::ptrdiff_t>(beg); + fprintf(stderr, "First bad addr: %p (begin + %u: %s)\n", bad_addr, offset, + __asan_address_is_poisoned(bad_addr) ? "poisoned" : "unpoisoned"); +} + +static bool is_asan_contigious_container_correct(const void *beg, + const void *mid, + const void *end) +{ + const void *bad_addr = + __sanitizer_contiguous_container_find_bad_address(beg, mid, end); + + if (!bad_addr) + return true; + + print_bad_asan_container(beg, mid, end, bad_addr); + return false; +} + +template <class T> +bool is_asan_string_correct(const std::basic_string<T> &str) +{ + if (str.capacity() == 0) + return true; + const T *str_data = reinterpret_cast<const T *>(str.data()); + const T *beg = str_data; + const T *mid = str_data + str.size() + 1; + const T *end = str_data + str.capacity() + 1; + +#if _GLIBCXX_DISABLE_STRING_SSO_USAGE + const bool is_local = false; +#else + const T *str_ptr= reinterpret_cast<const T *>(&str); + const T *str_ptr_next = reinterpret_cast<const T *>(&str + 1); + const bool is_local = (str_data >= str_ptr) && (str_data < str_ptr_next); +#endif + + if (!is_local) + return is_asan_contigious_container_correct(beg, mid, end); + + return true; +} + +#else // TESTSUITE_ASAN_VERIFY_STRINGS + +template <class T> +bool is_asan_string_correct(const std::basic_string<T> &str) +{ + return true; +} + +#endif // TESTSUITE_ASAN_VERIFY_STRINGS + +#endif // _GLIBCXX_TESTSUITE_ASAN_H -- 2.7.4