This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Re: [2/2] Add AddressSanitizer annotations to std::string.
- From: Mikhail Kashkarov <m dot kashkarov at partner dot samsung dot com>
- To: Jonathan Wakely <jwakely at redhat dot com>
- Cc: "libstdc++ at gcc dot gnu dot org" <libstdc++ at gcc dot gnu dot org>, gcc-patches <gcc-patches at gcc dot gnu dot org>
- Date: Mon, 16 Jul 2018 19:16:37 +0300
- Subject: Re: [2/2] Add AddressSanitizer annotations to std::string.
- Cms-type: 201P
- Dkim-filter: OpenDKIM Filter v2.11.0 mailout2.w1.samsung.com 20180716161650euoutp02718e312daa52c2aa25c53a1eaeb05f0e~B5Zz4JRZu0835108351euoutp02Z
- References: <c8b18d2a-d5c7-e3a2-53be-30b39eadeb49@partner.samsung.com> <0d8f122d-1376-5f25-4496-0d957eebdd7b@partner.samsung.com> <CGME20180529065519eucas1p1d2e1f33524940cdc783e059622769951@eucas1p1.samsung.com> <20180529065519eucas1p1d2e1f33524940cdc783e059622769951~zCx1IxDMB0595505955eucas1p1h@eucas1p1.samsung.com> <20180529141742.GJ7974@redhat.com> <20180529151835eucas1p20ee8861f37ca38d89830d2f4aa0d8fa8~zJpPxM4Zy0176201762eucas1p2a@eucas1p2.samsung.com> <20180529155513.GM7974@redhat.com> <20180608145440eucas1p22a8a8ad9d2c4283e191bd35e37ca9048~2NxOSIERn1904519045eucas1p2Y@eucas1p2.samsung.com> <20180628080959eucas1p117bf28bcdcbc94390ba58145f588a226~8RJll5sh_2049920499eucas1p1f@eucas1p1.samsung.com>
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 <jwakely@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.kashkarov@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.kashkarov@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