[libcxx-commits] [libcxx] 4905550 - [ASan][libcxx] Annotating std::vector with all allocators
Nikolas Klauser via libcxx-commits
libcxx-commits at lists.llvm.org
Wed Jan 25 10:04:26 PST 2023
Author: Advenam Tacet
Date: 2023-01-25T19:04:15+01:00
New Revision: 490555026821db47d1cf4bf08c219b3e56ec6b45
URL: https://github.com/llvm/llvm-project/commit/490555026821db47d1cf4bf08c219b3e56ec6b45
DIFF: https://github.com/llvm/llvm-project/commit/490555026821db47d1cf4bf08c219b3e56ec6b45.diff
LOG: [ASan][libcxx] Annotating std::vector with all allocators
This revision is a part of a series of patches extending
AddressSanitizer C++ container overflow detection
capabilities by adding annotations, similar to those existing
in std::vector, to std::string and std::deque collections.
These changes allow ASan to detect cases when the instrumented
program accesses memory which is internally allocated by
the collection but is still not in-use (accesses before or
after the stored elements for std::deque, or between the size and
capacity bounds for std::string).
The motivation for the research and those changes was a bug,
found by Trail of Bits, in a real code where an out-of-bounds read
could happen as two strings were compared via a std::equals function
that took iter1_begin, iter1_end, iter2_begin iterators
(with a custom comparison function).
When object iter1 was longer than iter2, read out-of-bounds on iter2
could happen. Container sanitization would detect it.
In revision D132522, support for non-aligned memory buffers (sharing
first/last granule with other objects) was added, therefore the
check for standard allocator is not necessary anymore.
This patch removes the check in std::vector annotation member
function (__annotate_contiguous_container) to support
different allocators.
If you have any questions, please email:
- advenam.tacet at trailofbits.com
- disconnect3d at trailofbits.com
Reviewed By: #libc, #sanitizers, philnik, vitalybuka
Spies: EricWF, philnik, #sanitizers, libcxx-commits
Differential Revision: https://reviews.llvm.org/D136765
Added:
Modified:
libcxx/include/vector
libcxx/test/libcxx/containers/sequences/vector/asan.pass.cpp
libcxx/test/support/min_allocator.h
Removed:
################################################################################
diff --git a/libcxx/include/vector b/libcxx/include/vector
index 4b7ae130a7bc4..e9019a8ae3033 100644
--- a/libcxx/include/vector
+++ b/libcxx/include/vector
@@ -743,8 +743,25 @@ private:
const void *__old_mid,
const void *__new_mid) const
{
-
+# if _LIBCPP_CLANG_VER >= 1600
+ if (!__libcpp_is_constant_evaluated() && __beg)
+ // Implementation of __sanitizer_annotate_contiguous_container function changed with commit
+ // rGdd1b7b797a116eed588fd752fbe61d34deeb24e4 and supports:
+ // - unaligned beginnings of buffers,
+ // - shared first/last granule (if memory granule is shared with a
diff erent object
+ // just after the end of unaligned end/before the unaligned beginning, memory of that object won't be poisoned).
+ //
+ // Therefore, check for standard allocator is not necessary.
+# else
+ // TODO LLVM18: Remove the special-casing
+ //
+ // Vectors annotations rely on __sanitizer_annotate_contiguous_container function,
+ // its implementation from LLVM15 (and earlier) requires that
+ // beginning of a containers data buffer is aligned to shadow granularity and
+ // memory just after is not shared with another object.
+ // Default allocator satisfies that.
if (!__libcpp_is_constant_evaluated() && __beg && is_same<allocator_type, __default_allocator_type>::value)
+# endif
__sanitizer_annotate_contiguous_container(__beg, __end, __old_mid, __new_mid);
}
#else
diff --git a/libcxx/test/libcxx/containers/sequences/vector/asan.pass.cpp b/libcxx/test/libcxx/containers/sequences/vector/asan.pass.cpp
index 40275f58cb32c..01da9d52e8f0d 100644
--- a/libcxx/test/libcxx/containers/sequences/vector/asan.pass.cpp
+++ b/libcxx/test/libcxx/containers/sequences/vector/asan.pass.cpp
@@ -29,40 +29,65 @@ void do_exit() {
int main(int, char**)
{
-#if TEST_STD_VER >= 11
- {
- typedef int T;
- typedef std::vector<T, min_allocator<T>> C;
- const T t[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
- C c(std::begin(t), std::end(t));
- c.reserve(2*c.size());
- volatile T foo = c[c.size()]; // bad, but not caught by ASAN
- ((void)foo);
- }
+#if TEST_STD_VER >= 11 && TEST_CLANG_VER < 1600
+ // TODO LLVM18: Remove the special-casing
+ {
+ typedef int T;
+ typedef std::vector<T, min_allocator<T>> C;
+ const T t[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+ C c(std::begin(t), std::end(t));
+ c.reserve(2 * c.size());
+ volatile T foo = c[c.size()]; // bad, but not caught by ASAN
+ ((void)foo);
+ }
#endif
- {
- typedef cpp17_input_iterator<int*> MyInputIter;
- // Sould not trigger ASan.
- std::vector<int> v;
- v.reserve(1);
- int i[] = {42};
- v.insert(v.begin(), MyInputIter(i), MyInputIter(i + 1));
- assert(v[0] == 42);
- assert(is_contiguous_container_asan_correct(v));
- }
+#if TEST_STD_VER >= 11 && TEST_CLANG_VER >= 1600
+ // TODO LLVM18: Remove the TEST_CLANG_VER check
+ {
+ typedef int T;
+ typedef cpp17_input_iterator<T*> MyInputIter;
+ std::vector<T, min_allocator<T>> v;
+ v.reserve(1);
+ int i[] = {42};
+ v.insert(v.begin(), MyInputIter(i), MyInputIter(i + 1));
+ assert(v[0] == 42);
+ assert(is_contiguous_container_asan_correct(v));
+ }
+ {
+ typedef char T;
+ typedef cpp17_input_iterator<T*> MyInputIter;
+ std::vector<T, unaligned_allocator<T>> v;
+ v.reserve(1);
+ char i[] = {'a', 'b'};
+ v.insert(v.begin(), MyInputIter(i), MyInputIter(i + 2));
+ assert(v[0] == 'a');
+ assert(v[1] == 'b');
+ assert(is_contiguous_container_asan_correct(v));
+ }
+#endif
+ {
+ typedef cpp17_input_iterator<int*> MyInputIter;
+ // Sould not trigger ASan.
+ std::vector<int> v;
+ v.reserve(1);
+ int i[] = {42};
+ v.insert(v.begin(), MyInputIter(i), MyInputIter(i + 1));
+ assert(v[0] == 42);
+ assert(is_contiguous_container_asan_correct(v));
+ }
- __sanitizer_set_death_callback(do_exit);
- {
- typedef int T;
- typedef std::vector<T> C;
- const T t[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
- C c(std::begin(t), std::end(t));
- c.reserve(2*c.size());
- assert(is_contiguous_container_asan_correct(c));
- assert(!__sanitizer_verify_contiguous_container( c.data(), c.data() + 1, c.data() + c.capacity()));
- volatile T foo = c[c.size()]; // should trigger ASAN. Use volatile to prevent being optimized away.
- assert(false); // if we got here, ASAN didn't trigger
- ((void)foo);
- }
+ __sanitizer_set_death_callback(do_exit);
+ {
+ typedef int T;
+ typedef std::vector<T> C;
+ const T t[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+ C c(std::begin(t), std::end(t));
+ c.reserve(2 * c.size());
+ assert(is_contiguous_container_asan_correct(c));
+ assert(!__sanitizer_verify_contiguous_container(c.data(), c.data() + 1, c.data() + c.capacity()));
+ volatile T foo = c[c.size()]; // should trigger ASAN. Use volatile to prevent being optimized away.
+ assert(false); // if we got here, ASAN didn't trigger
+ ((void)foo);
+ }
}
diff --git a/libcxx/test/support/min_allocator.h b/libcxx/test/support/min_allocator.h
index f27ac80c04649..7d517518f0522 100644
--- a/libcxx/test/support/min_allocator.h
+++ b/libcxx/test/support/min_allocator.h
@@ -432,4 +432,23 @@ class explicit_allocator
TEST_CONSTEXPR_CXX20 friend bool operator!=(explicit_allocator x, explicit_allocator y) {return !(x == y);}
};
+template <class T>
+class unaligned_allocator {
+public:
+ static_assert(TEST_ALIGNOF(T) == 1, "Type T cannot be created on unaligned address (UB)");
+ typedef T value_type;
+
+ TEST_CONSTEXPR_CXX20 unaligned_allocator() TEST_NOEXCEPT {}
+
+ template <class U>
+ TEST_CONSTEXPR_CXX20 explicit unaligned_allocator(unaligned_allocator<U>) TEST_NOEXCEPT {}
+
+ TEST_CONSTEXPR_CXX20 T* allocate(std::size_t n) { return static_cast<T*>(std::allocator<T>().allocate(n + 1)) + 1; }
+
+ TEST_CONSTEXPR_CXX20 void deallocate(T* p, std::size_t n) { std::allocator<T>().deallocate(p - 1, n + 1); }
+
+ TEST_CONSTEXPR_CXX20 friend bool operator==(unaligned_allocator, unaligned_allocator) { return true; }
+ TEST_CONSTEXPR_CXX20 friend bool operator!=(unaligned_allocator x, unaligned_allocator y) { return !(x == y); }
+};
+
#endif // MIN_ALLOCATOR_H
More information about the libcxx-commits
mailing list