[libcxx-commits] [libcxx] [ASan][libc++] Update string ASan annotations to Zero-overhead (PR #76165)
via libcxx-commits
libcxx-commits at lists.llvm.org
Thu Dec 21 08:01:17 PST 2023
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-libcxx
Author: Tacet (AdvenamTacet)
<details>
<summary>Changes</summary>
This commit introduces Zero-overhead string ASan annotations by wrapping every call to ASan helper functions inside a constexpr if statement. This ensures that ASan checks are only performed at runtime when the compiler determines that the string is potentially vulnerable to memory safety issues. This approach significantly reduces the overhead of ASan while maintaining its effectiveness in detecting memory safety bugs.
While the intended execution path should not incur any overhead due to compiler optimizations, a few local variables solely accessed by ASan helper functions are not currently removed and we rely on optimizing them away. A future commit may reduce number of these variables.
---
Patch is 24.04 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/76165.diff
1 Files Affected:
- (modified) libcxx/include/string (+101-85)
``````````diff
diff --git a/libcxx/include/string b/libcxx/include/string
index fdffca5aed18be..fcfee0e6e2f9df 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -659,6 +659,13 @@ _LIBCPP_PUSH_MACROS
#else
# define _LIBCPP_STRING_INTERNAL_MEMORY_ACCESS
#endif
+#ifndef _IF_ASAN
+# ifndef _LIBCPP_HAS_NO_ASAN
+# define _IF_ASAN() if _LIBCPP_CONSTEXPR (true)
+# else
+# define _IF_ASAN() if _LIBCPP_CONSTEXPR (false)
+# endif
+#endif
#define _LIBCPP_SHORT_STRING_ANNOTATIONS_ALLOWED false
_LIBCPP_BEGIN_NAMESPACE_STD
@@ -858,7 +865,7 @@ private:
__set_long_pointer(__allocation);
__set_long_size(__size);
}
- __annotate_new(__size);
+ _IF_ASAN() __annotate_new(__size);
}
template <class _Iter, class _Sent>
@@ -880,7 +887,7 @@ public:
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string()
_NOEXCEPT_(is_nothrow_default_constructible<allocator_type>::value)
: __r_(__value_init_tag(), __default_init_tag()) {
- __annotate_new(0);
+ _IF_ASAN() __annotate_new(0);
}
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit basic_string(const allocator_type& __a)
@@ -890,14 +897,14 @@ public:
_NOEXCEPT
#endif
: __r_(__value_init_tag(), __a) {
- __annotate_new(0);
+ _IF_ASAN() __annotate_new(0);
}
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_STRING_INTERNAL_MEMORY_ACCESS basic_string(const basic_string& __str)
: __r_(__default_init_tag(), __alloc_traits::select_on_container_copy_construction(__str.__alloc())) {
if (!__str.__is_long()) {
__r_.first() = __str.__r_.first();
- __annotate_new(__get_short_size());
+ _IF_ASAN() __annotate_new(__get_short_size());
} else
__init_copy_ctor_external(std::__to_address(__str.__get_long_pointer()), __str.__get_long_size());
}
@@ -907,7 +914,7 @@ public:
: __r_(__default_init_tag(), __a) {
if (!__str.__is_long()) {
__r_.first() = __str.__r_.first();
- __annotate_new(__get_short_size());
+ _IF_ASAN() __annotate_new(__get_short_size());
} else
__init_copy_ctor_external(std::__to_address(__str.__get_long_pointer()), __str.__get_long_size());
}
@@ -919,13 +926,19 @@ public:
# else
_NOEXCEPT
# endif
+#ifndef _LIBCPP_HAS_NO_ASAN
// Turning off ASan instrumentation for variable initialization with _LIBCPP_STRING_INTERNAL_MEMORY_ACCESS
// does not work consistently during initialization of __r_, so we instead unpoison __str's memory manually first.
// __str's memory needs to be unpoisoned only in the case where it's a short string.
: __r_(((__str.__is_long() ? 0 : (__str.__annotate_delete(), 0)), std::move(__str.__r_))) {
+ // ASan TODO: ^ This line results in stack frame growth and isn't correctly optimized by the compiler.
+ // It should be refactored to improve performance.
+#else
+ : __r_(std::move(__str.__r_)) {
+#endif
__str.__r_.first() = __rep();
- __str.__annotate_new(0);
- if (!__is_long())
+ _IF_ASAN() __str.__annotate_new(0);
+ _IF_ASAN() if (!__is_long())
__annotate_new(size());
}
@@ -936,12 +949,12 @@ public:
else {
if (__libcpp_is_constant_evaluated())
__r_.first() = __rep();
- if (!__str.__is_long())
+ _IF_ASAN() if (!__str.__is_long())
__str.__annotate_delete();
__r_.first() = __str.__r_.first();
__str.__r_.first() = __rep();
- __str.__annotate_new(0);
- if (!__is_long() && this != &__str)
+ _IF_ASAN() __str.__annotate_new(0);
+ _IF_ASAN() if (!__is_long() && this != &__str)
__annotate_new(size());
}
}
@@ -1100,7 +1113,7 @@ public:
#endif // _LIBCPP_CXX03_LANG
inline _LIBCPP_CONSTEXPR_SINCE_CXX20 ~basic_string() {
- __annotate_delete();
+ _IF_ASAN() __annotate_delete();
if (__is_long())
__alloc_traits::deallocate(__alloc(), __get_long_pointer(), __get_long_cap());
}
@@ -1357,20 +1370,22 @@ public:
// Pilfer the allocation from __str.
_LIBCPP_ASSERT_INTERNAL(__alloc() == __str.__alloc(), "__move_assign called with wrong allocator");
size_type __old_sz = __str.size();
- if (!__str.__is_long())
+ _IF_ASAN() if (!__str.__is_long())
__str.__annotate_delete();
__r_.first() = __str.__r_.first();
__str.__r_.first() = __rep();
- __str.__annotate_new(0);
+ _IF_ASAN() __str.__annotate_new(0);
_Traits::move(data(), data() + __pos, __len);
__set_size(__len);
_Traits::assign(data()[__len], value_type());
- if (!__is_long()) {
- __annotate_new(__len);
- } else if (__old_sz > __len) {
- __annotate_shrink(__old_sz);
+ _IF_ASAN() {
+ if (!__is_long()) {
+ __annotate_new(__len);
+ } else if (__old_sz > __len) {
+ __annotate_shrink(__old_sz);
+ }
}
}
#endif
@@ -1801,7 +1816,7 @@ private:
size_type __cap = capacity();
value_type* __p;
if (__cap - __sz >= __n) {
- __annotate_increase(__n);
+ _IF_ASAN() __annotate_increase(__n);
__p = std::__to_address(__get_pointer());
size_type __n_move = __sz - __ip;
if (__n_move != 0)
@@ -2026,7 +2041,7 @@ private:
__clear_and_shrink();
__alloc() = __str.__alloc();
} else {
- __annotate_delete();
+ _IF_ASAN() __annotate_delete();
allocator_type __a = __str.__alloc();
auto __allocation = std::__allocate_at_least(__a, __str.__get_long_cap());
__begin_lifetime(__allocation.ptr, __allocation.count);
@@ -2036,7 +2051,7 @@ private:
__set_long_pointer(__allocation.ptr);
__set_long_cap(__allocation.count);
__set_long_size(__str.size());
- __annotate_new(__get_long_size());
+ _IF_ASAN() __annotate_new(__get_long_size());
}
}
}
@@ -2076,13 +2091,13 @@ private:
// Assigns the value in __s, guaranteed to be __n < __min_cap in length.
inline _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& __assign_short(const value_type* __s, size_type __n) {
size_type __old_size = size();
- if (__n > __old_size)
+ _IF_ASAN() if (__n > __old_size)
__annotate_increase(__n - __old_size);
pointer __p =
__is_long() ? (__set_long_size(__n), __get_long_pointer()) : (__set_short_size(__n), __get_short_pointer());
traits_type::move(std::__to_address(__p), __s, __n);
traits_type::assign(__p[__n], value_type());
- if (__old_size > __n)
+ _IF_ASAN() if (__old_size > __n)
__annotate_shrink(__old_size);
return *this;
}
@@ -2090,11 +2105,11 @@ private:
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string&
__null_terminate_at(value_type* __p, size_type __newsz) {
size_type __old_size = size();
- if (__newsz > __old_size)
+ _IF_ASAN() if (__newsz > __old_size)
__annotate_increase(__newsz - __old_size);
__set_size(__newsz);
traits_type::assign(__p[__newsz], value_type());
- if (__old_size > __newsz)
+ _IF_ASAN() if (__old_size > __newsz)
__annotate_shrink(__old_size);
return *this;
}
@@ -2189,7 +2204,7 @@ basic_string<_CharT, _Traits, _Allocator>::__init(const value_type* __s, size_ty
}
traits_type::copy(std::__to_address(__p), __s, __sz);
traits_type::assign(__p[__sz], value_type());
- __annotate_new(__sz);
+ _IF_ASAN() __annotate_new(__sz);
}
template <class _CharT, class _Traits, class _Allocator>
@@ -2213,7 +2228,7 @@ basic_string<_CharT, _Traits, _Allocator>::__init(const value_type* __s, size_ty
}
traits_type::copy(std::__to_address(__p), __s, __sz);
traits_type::assign(__p[__sz], value_type());
- __annotate_new(__sz);
+ _IF_ASAN() __annotate_new(__sz);
}
template <class _CharT, class _Traits, class _Allocator>
@@ -2237,7 +2252,7 @@ basic_string<_CharT, _Traits, _Allocator>::__init_copy_ctor_external(const value
__set_long_size(__sz);
}
traits_type::copy(std::__to_address(__p), __s, __sz + 1);
- __annotate_new(__sz);
+ _IF_ASAN() __annotate_new(__sz);
}
template <class _CharT, class _Traits, class _Allocator>
@@ -2261,7 +2276,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void basic_string<_CharT, _Traits, _Allocator>::__
}
traits_type::assign(std::__to_address(__p), __n, __c);
traits_type::assign(__p[__n], value_type());
- __annotate_new(__n);
+ _IF_ASAN() __annotate_new(__n);
}
template <class _CharT, class _Traits, class _Allocator>
@@ -2276,7 +2291,7 @@ template <class _InputIterator, class _Sentinel>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
basic_string<_CharT, _Traits, _Allocator>::__init_with_sentinel(_InputIterator __first, _Sentinel __last) {
__r_.first() = __rep();
- __annotate_new(0);
+ _IF_ASAN() __annotate_new(0);
#ifndef _LIBCPP_HAS_NO_EXCEPTIONS
try {
@@ -2285,7 +2300,7 @@ basic_string<_CharT, _Traits, _Allocator>::__init_with_sentinel(_InputIterator _
push_back(*__first);
#ifndef _LIBCPP_HAS_NO_EXCEPTIONS
} catch (...) {
- __annotate_delete();
+ _IF_ASAN() __annotate_delete();
if (__is_long())
__alloc_traits::deallocate(__alloc(), __get_long_pointer(), __get_long_cap());
throw;
@@ -2338,7 +2353,7 @@ basic_string<_CharT, _Traits, _Allocator>::__init_with_size(_InputIterator __fir
throw;
}
#endif // _LIBCPP_HAS_NO_EXCEPTIONS
- __annotate_new(__sz);
+ _IF_ASAN() __annotate_new(__sz);
}
template <class _CharT, class _Traits, class _Allocator>
@@ -2356,7 +2371,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void basic_string<_CharT, _Traits, _Allocator>::__
pointer __old_p = __get_pointer();
size_type __cap =
__old_cap < __ms / 2 - __alignment ? __recommend(std::max(__old_cap + __delta_cap, 2 * __old_cap)) : __ms - 1;
- __annotate_delete();
+ _IF_ASAN() __annotate_delete();
auto __allocation = std::__allocate_at_least(__alloc(), __cap + 1);
pointer __p = __allocation.ptr;
__begin_lifetime(__p, __allocation.count);
@@ -2375,7 +2390,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void basic_string<_CharT, _Traits, _Allocator>::__
__old_sz = __n_copy + __n_add + __sec_cp_sz;
__set_long_size(__old_sz);
traits_type::assign(__p[__old_sz], value_type());
- __annotate_new(__old_cap + __delta_cap);
+ _IF_ASAN() __annotate_new(__old_cap + __delta_cap);
}
// __grow_by is deprecated because it does not set the size. It may not update the size when the size is changed, and it
@@ -2399,7 +2414,7 @@ void _LIBCPP_CONSTEXPR_SINCE_CXX20
pointer __old_p = __get_pointer();
size_type __cap =
__old_cap < __ms / 2 - __alignment ? __recommend(std::max(__old_cap + __delta_cap, 2 * __old_cap)) : __ms - 1;
- __annotate_delete();
+ _IF_ASAN() __annotate_delete();
auto __allocation = std::__allocate_at_least(__alloc(), __cap + 1);
pointer __p = __allocation.ptr;
__begin_lifetime(__p, __allocation.count);
@@ -2428,7 +2443,7 @@ basic_string<_CharT, _Traits, _Allocator>::__grow_by_without_replace(
__grow_by(__old_cap, __delta_cap, __old_sz, __n_copy, __n_del, __n_add);
_LIBCPP_SUPPRESS_DEPRECATED_POP
__set_long_size(__old_sz - __n_del + __n_add);
- __annotate_new(__old_sz - __n_del + __n_add);
+ _IF_ASAN() __annotate_new(__old_sz - __n_del + __n_add);
}
// assign
@@ -2440,13 +2455,13 @@ basic_string<_CharT, _Traits, _Allocator>::__assign_no_alias(const value_type* _
size_type __cap = __is_short ? static_cast<size_type>(__min_cap) : __get_long_cap();
if (__n < __cap) {
size_type __old_size = __is_short ? __get_short_size() : __get_long_size();
- if (__n > __old_size)
+ _IF_ASAN() if (__n > __old_size)
__annotate_increase(__n - __old_size);
pointer __p = __is_short ? __get_short_pointer() : __get_long_pointer();
__is_short ? __set_short_size(__n) : __set_long_size(__n);
traits_type::copy(std::__to_address(__p), __s, __n);
traits_type::assign(__p[__n], value_type());
- if (__old_size > __n)
+ _IF_ASAN() if (__old_size > __n)
__annotate_shrink(__old_size);
} else {
size_type __sz = __is_short ? __get_short_size() : __get_long_size();
@@ -2461,7 +2476,7 @@ basic_string<_CharT, _Traits, _Allocator>::__assign_external(const value_type* _
size_type __cap = capacity();
if (__cap >= __n) {
size_type __old_size = size();
- if (__n > __old_size)
+ _IF_ASAN() if (__n > __old_size)
__annotate_increase(__n - __old_size);
value_type* __p = std::__to_address(__get_pointer());
traits_type::move(__p, __s, __n);
@@ -2488,8 +2503,8 @@ basic_string<_CharT, _Traits, _Allocator>::assign(size_type __n, value_type __c)
if (__cap < __n) {
size_type __sz = size();
__grow_by_without_replace(__cap, __n - __cap, __sz, 0, __sz);
- __annotate_increase(__n);
- } else if (__n > __old_size)
+ _IF_ASAN() __annotate_increase(__n);
+ } else _IF_ASAN() if (__n > __old_size)
__annotate_increase(__n - __old_size);
value_type* __p = std::__to_address(__get_pointer());
traits_type::assign(__p, __n, __c);
@@ -2501,7 +2516,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string<_CharT, _Traits, _Allocator>&
basic_string<_CharT, _Traits, _Allocator>::operator=(value_type __c) {
pointer __p;
size_type __old_size = size();
- if (__old_size == 0)
+ _IF_ASAN() if (__old_size == 0)
__annotate_increase(1);
if (__is_long()) {
__p = __get_long_pointer();
@@ -2512,7 +2527,7 @@ basic_string<_CharT, _Traits, _Allocator>::operator=(value_type __c) {
}
traits_type::assign(*__p, __c);
traits_type::assign(*++__p, value_type());
- if (__old_size > 1)
+ _IF_ASAN() if (__old_size > 1)
__annotate_shrink(__old_size);
return *this;
}
@@ -2525,10 +2540,10 @@ basic_string<_CharT, _Traits, _Allocator>::operator=(const basic_string& __str)
if (!__is_long()) {
if (!__str.__is_long()) {
size_type __old_size = __get_short_size();
- if (__get_short_size() < __str.__get_short_size())
+ _IF_ASAN() if (__get_short_size() < __str.__get_short_size())
__annotate_increase(__str.__get_short_size() - __get_short_size());
__r_.first() = __str.__r_.first();
- if (__old_size > __get_short_size())
+ _IF_ASAN() if (__old_size > __get_short_size())
__annotate_shrink(__old_size);
} else {
return __assign_no_alias<true>(__str.data(), __str.size());
@@ -2561,14 +2576,14 @@ basic_string<_CharT, _Traits, _Allocator>::__move_assign(basic_string& __str, tr
_NOEXCEPT_(is_nothrow_move_assignable<allocator_type>::value)
# endif
{
- __annotate_delete();
+ _IF_ASAN() __annotate_delete();
if (__is_long()) {
__alloc_traits::deallocate(__alloc(), __get_long_pointer(), __get_long_cap());
# if _LIBCPP_STD_VER <= 14
if (!is_nothrow_move_assignable<allocator_type>::value) {
__set_short_size(0);
traits_type::assign(__get_short_pointer()[0], value_type());
- __annotate_new(0);
+ _IF_ASAN() __annotate_new(0);
}
# endif
}
@@ -2579,24 +2594,25 @@ basic_string<_CharT, _Traits, _Allocator>::__move_assign(basic_string& __str, tr
__r_.first() = __str.__r_.first();
__str.__set_short_size(0);
traits_type::assign(__str.__get_short_pointer()[0], value_type());
+ _IF_ASAN() {
+ if (__str_was_short && this != &__str)
+ __str.__annotate_shrink(__str_old_size);
+ else
+ // ASan annotations: was long, so object memory is unpoisoned as new.
+ // Or is same as *this, and __annotate_delete() was called.
+ __str.__annotate_new(0);
- if (__str_was_short && this != &__str)
- __str.__annotate_shrink(__str_old_size);
- else
- // ASan annotations: was long, so object memory is unpoisoned as new.
- // Or is same as *this, and __annotate_delete() was called.
- __str.__annotate_new(0);
-
- // ASan annotations: Guard against `std::string s; s = std::move(s);`
- // You can find more here: https://en.cppreference.com/w/cpp/utility/move
- // Quote: "Unless otherwise specified, all standard library objects that have been moved
- // from are placed in a "valid but unspecified state", meaning the object's class
- // invariants hold (so functions without preconditions, such as the assignment operator,
- // can be safely used on the object after it was moved from):"
- // Quote: "v = std::move(v); // the value of v is unspecified"
- if (!__is_long() && &__str != this)
- // If it is long string, delete was never called on original __str's buffer.
- __annotate_new(__get_short_size());
+ // ASan annotations: Guard against `std::string s; s = std::move(s);`
+ // You can find more here: https://en.cppreference.com/w/cpp/utility/move
+ // Quote: "Unless otherwise specified, all standard library objects that have been moved
+ // from are placed in a "valid but unspecified state", meaning the object's class
+ // invariants hold (so functions without preconditions, such as the assignment operator,
+ // can be safely used on the object after it was moved from):"
+ // Quote: "v = std::move(v); // the value of v is unspecified"
+ if (!__is_long() && &__str != this)
+ // If it is long string, delete was never called on original __str's buffer.
+ __annotate_new(__get_short_size());
+ }
}
#endif
@@ -2649,15 +2665,15 @@ basic_string<_CharT, _Traits, _Allocator>::__assign_trivial(_Iterator __first, _
// object itself stays valid even if reallocation happens.
size_type __sz = size();
__grow_by_without_replace(__cap, __n - __cap, __sz, 0, __sz);
- __annotate_increase(__n);
- } else if (__n > __old_size)
+ _IF_ASAN() __annotate_increase(__n);
+ } else _IF_ASAN() if (__n > __old_size)
__annotate_increase(__n - __old_size);
pointer __p = __get_pointer();
for (; __first != __last; ++__p, (void)++__first)
traits_type::assign(*__p, *__first);
traits_type::assign(*__p, value_type());
__set_size(__n);
- if (__n < __old_size)
+ _IF_ASAN() if (__n < __old_size)
__annotate_shrink(__old_size);
}
@@ -2709,7 +2725,7 @@ basic_string<_CharT, _Traits, _Allocator>::append(const value_type* __s, size_ty
size_type __sz = size();
if (__cap - __sz >= __n) {
if (__n) {
- __annotate_increase(__n);
+ _IF_ASAN() __annotate_increase(__n);
value_type* __p = std::__to_address(__get_pointer());
traits_type::copy(__p + __sz, __s, __n);
__sz += __n;
@@ -2729,7 +2745,7 @@ basic_string<_CharT, _Traits, _Allocator>::append(size_type __n, value_type __c)
size_type __sz = size();
if (__cap - __sz < __n)
__grow_by_without_replace(__cap, __sz + __n - __cap, __sz, __sz, 0);
- __annotate_increase(__n);
+ _IF_ASAN() __annotate_increase(__n);
pointer __p = __get_pointer();
traits_type::assign(std::__to_address(__p) + __sz, __n, __c);
__sz += __n;
@@ -2747,7 +2763,7 @@ basic_string<_CharT, _Traits, _Allocator>::__append_default_init(size_type __n)
size_type __sz = size();
if (__cap - __sz < __n)
__grow_by_without_replace(__cap, __sz + __n - __cap, __sz, __sz, 0);
- __annotate_increase(__n);
+ _IF_ASAN() __annotate_increase(__n);
pointer __p = __get_pointer();
__sz += __n;
__set_size(__sz);
@@ -2769,10 +2785,10 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void basic_string<_CharT, _Traits, _Allocator>::pu
}
if (__sz == __cap) {
__grow_by_without_replace(__cap, 1, __sz, __sz, 0);
- __annotate_increase(1);
+ _IF_ASAN() __annotate_increase(1);
__is_short = false; // the string is always long after __grow_by
} else
- __annotate_increase(1);
+ _IF_ASAN() __annotate_increase(1);
pointer __p = __get_pointer();
if (__is_short) {
__p = __get_short_pointer() + __sz;
@@ -2796,7 +2812,7 @@ basic_string<_CharT, _Traits, _Allocator>::append(_ForwardIterator __first, _For
if (__string_is_trivial_iterator<_ForwardIterator>::value && !__addr_in_range(*__first)) {
if (__cap - __sz < __n)
__grow_by_without_replace(__cap, __sz + __n - __cap, __sz, __sz, 0);
- __annotate_increase(__n);
+ _IF_ASAN() __annotate_increase(__n);
pointer __p = __get_pointer() + __sz;
for (; __first != __last; ++__p, (void)++__first)
traits_type::assign(*__p, *__first);
@@ -2852,7 +2868,7 @@ basic_string<_CharT, _Traits, _Allocator>::insert(size_type __pos, const value_t
size_type __cap = capacity();
if (__cap - __sz >= __n) {
if (__n...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/76165
More information about the libcxx-commits
mailing list