This is the mail archive of the libstdc++@gcc.gnu.org mailing list for the libstdc++ project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [2/2] Add AddressSanitizer annotations to std::string.


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&lt;T, std::allocator&lt;T&gt;&gt;</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&lt;T, std::allocator&lt;T&gt;&gt;</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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]