Skip to content

Commit 0ba0371

Browse files
author
Advenam Tacet
committed
[libc++][ASan] Stop optimizations in std::basic_string
This commit stops some optimizations when compiling with ASan. - Short strings make std::basic_string to not be trivially relocatable, because memory has to be unpoisoned. It changes value of `__trivially_relocatable` when compiling with ASan. - It truns off compiler stack optimizations with `__asan_volatile_wrapper`, the function is not used when compiling without ASan. - It turns off instrumentation in a few functions.
1 parent c9f38d2 commit 0ba0371

File tree

2 files changed

+42
-7
lines changed

2 files changed

+42
-7
lines changed

libcxx/include/string

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -735,10 +735,44 @@ public:
735735
//
736736
// This string implementation doesn't contain any references into itself. It only contains a bit that says whether
737737
// it is in small or large string mode, so the entire structure is trivially relocatable if its members are.
738+
#if !defined(_LIBCPP_HAS_NO_ASAN) && defined(_LIBCPP_INSTRUMENTED_WITH_ASAN)
739+
// When compiling with AddressSanitizer (ASan), basic_string cannot be trivially
740+
// relocatable. Because the object's memory might be poisoned when its content
741+
// is kept inside objects memory (short string optimization), instead of in allocated
742+
// external memory. In such cases, the destructor is responsible for unpoisoning
743+
// the memory to avoid triggering false positives.
744+
// Therefore it's crucial to ensure the destructor is called
745+
using __trivially_relocatable = false_type;
746+
#else
738747
using __trivially_relocatable = __conditional_t<
739748
__libcpp_is_trivially_relocatable<allocator_type>::value && __libcpp_is_trivially_relocatable<pointer>::value,
740749
basic_string,
741750
void>;
751+
#endif
752+
#if !defined(_LIBCPP_HAS_NO_ASAN) && defined(_LIBCPP_INSTRUMENTED_WITH_ASAN)
753+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
754+
pointer __asan_volatile_wrapper(pointer const &__ptr) const {
755+
if (__libcpp_is_constant_evaluated())
756+
return __ptr;
757+
758+
pointer volatile __copy_ptr = __ptr;
759+
760+
return const_cast<pointer &>(__copy_ptr);
761+
}
762+
763+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
764+
const_pointer __asan_volatile_wrapper(const_pointer const &__ptr) const {
765+
if (__libcpp_is_constant_evaluated())
766+
return __ptr;
767+
768+
const_pointer volatile __copy_ptr = __ptr;
769+
770+
return const_cast<const_pointer &>(__copy_ptr);
771+
}
772+
#define _LIBCPP_ASAN_VOLATILE_WRAPPER(PTR) __asan_volatile_wrapper(PTR)
773+
#else
774+
#define _LIBCPP_ASAN_VOLATILE_WRAPPER(PTR) PTR
775+
#endif
742776

743777
static_assert((!is_array<value_type>::value), "Character type of basic_string must not be an array");
744778
static_assert((is_standard_layout<value_type>::value), "Character type of basic_string must be standard-layout");
@@ -1885,16 +1919,16 @@ private:
18851919
__r_.first().__l.__data_ = __p;
18861920
}
18871921
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 pointer __get_long_pointer() _NOEXCEPT {
1888-
return __r_.first().__l.__data_;
1922+
return _LIBCPP_ASAN_VOLATILE_WRAPPER(__r_.first().__l.__data_);
18891923
}
18901924
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 const_pointer __get_long_pointer() const _NOEXCEPT {
1891-
return __r_.first().__l.__data_;
1925+
return _LIBCPP_ASAN_VOLATILE_WRAPPER(__r_.first().__l.__data_);
18921926
}
1893-
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 pointer __get_short_pointer() _NOEXCEPT {
1894-
return pointer_traits<pointer>::pointer_to(__r_.first().__s.__data_[0]);
1927+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_STRING_INTERNAL_MEMORY_ACCESS pointer __get_short_pointer() _NOEXCEPT {
1928+
return _LIBCPP_ASAN_VOLATILE_WRAPPER(pointer_traits<pointer>::pointer_to(__r_.first().__s.__data_[0]));
18951929
}
1896-
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 const_pointer __get_short_pointer() const _NOEXCEPT {
1897-
return pointer_traits<const_pointer>::pointer_to(__r_.first().__s.__data_[0]);
1930+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_STRING_INTERNAL_MEMORY_ACCESS const_pointer __get_short_pointer() const _NOEXCEPT {
1931+
return _LIBCPP_ASAN_VOLATILE_WRAPPER(pointer_traits<const_pointer>::pointer_to(__r_.first().__s.__data_[0]));
18981932
}
18991933
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 pointer __get_pointer() _NOEXCEPT {
19001934
return __is_long() ? __get_long_pointer() : __get_short_pointer();

libcxx/test/libcxx/type_traits/is_trivially_relocatable.compile.pass.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ static_assert(!std::__libcpp_is_trivially_relocatable<MoveOnlyTriviallyCopyable>
4848
// ----------------------
4949

5050
// basic_string
51+
#if defined(_LIBCPP_HAS_NO_ASAN) || !defined(_LIBCPP_INSTRUMENTED_WITH_ASAN)
5152
struct MyChar {
5253
char c;
5354
};
@@ -78,7 +79,7 @@ static_assert(
7879
!std::__libcpp_is_trivially_relocatable<
7980
std::basic_string<MyChar, NotTriviallyRelocatableCharTraits<MyChar>, test_allocator<MyChar> > >::value,
8081
"");
81-
82+
#endif
8283
// unique_ptr
8384
struct NotTriviallyRelocatableDeleter {
8485
NotTriviallyRelocatableDeleter(const NotTriviallyRelocatableDeleter&);

0 commit comments

Comments
 (0)