[libcxx-commits] [libcxx] 0a35ac6 - [ASan][libc++] Annotating std::deque with all allocators

Advenam Tacet via libcxx-commits libcxx-commits at lists.llvm.org
Thu Jul 20 01:17:38 PDT 2023


Author: Advenam Tacet
Date: 2023-07-20T10:17:26+02:00
New Revision: 0a35ac6c2e0cb0160ca2e6cc11644c263692a46d

URL: https://github.com/llvm/llvm-project/commit/0a35ac6c2e0cb0160ca2e6cc11644c263692a46d
DIFF: https://github.com/llvm/llvm-project/commit/0a35ac6c2e0cb0160ca2e6cc11644c263692a46d.diff

LOG: [ASan][libc++] Annotating std::deque with all allocators

This patch is part of our efforts to support container annotations with (almost) every allocator.
Annotating std::deque with default allocator is implemented in D132092.

Support in ASan API exests since rG1c5ad6d2c01294a0decde43a88e9c27d7437d157.

The motivation for a 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.

If you have any questions, please email:
- advenam.tacet at trailofbits.com
- disconnect3d at trailofbits.com

Reviewed By: #libc, ldionne

Differential Revision: https://reviews.llvm.org/D146815

Added: 
    libcxx/test/libcxx/containers/sequences/deque/asan_turning_off.pass.cpp

Modified: 
    libcxx/include/deque
    libcxx/test/libcxx/containers/sequences/deque/asan.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/include/deque b/libcxx/include/deque
index 230bae75e6619e..b063680500cce4 100644
--- a/libcxx/include/deque
+++ b/libcxx/include/deque
@@ -458,9 +458,6 @@ const _DiffType __deque_iterator<_ValueType, _Pointer, _Reference, _MapPointer,
 template <class _Tp, class _Allocator /*= allocator<_Tp>*/>
 class _LIBCPP_TEMPLATE_VIS deque
 {
-private:
-  using __default_allocator_type = allocator<_Tp>;
-
 public:
     // types:
 
@@ -981,7 +978,7 @@ public:
         const void* __old_con_end,
         const void* __new_con_beg,
         const void* __new_con_end) const {
-        if (__beg && is_same<allocator_type, __default_allocator_type>::value)
+        if (__beg != nullptr && __asan_annotate_container_with_allocator<_Allocator>::value)
             __sanitizer_annotate_double_ended_contiguous_container(
                 __beg, __end, __old_con_beg, __old_con_end, __new_con_beg, __new_con_end);
     }

diff  --git a/libcxx/test/libcxx/containers/sequences/deque/asan.pass.cpp b/libcxx/test/libcxx/containers/sequences/deque/asan.pass.cpp
index 6067974f3a7e91..e8091acefc3681 100644
--- a/libcxx/test/libcxx/containers/sequences/deque/asan.pass.cpp
+++ b/libcxx/test/libcxx/containers/sequences/deque/asan.pass.cpp
@@ -31,13 +31,28 @@ int main(int, char**)
 {
     {
         typedef cpp17_input_iterator<int*> MyInputIter;
-        // Sould not trigger ASan.
+        // Should not trigger ASan.
         std::deque<int> v;
         int i[] = {42};
         v.insert(v.begin(), MyInputIter(i), MyInputIter(i + 1));
         assert(v[0] == 42);
         assert(is_double_ended_contiguous_container_asan_correct(v));
     }
+    {
+        typedef int T;
+        typedef std::deque<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));
+        assert(is_double_ended_contiguous_container_asan_correct(c));
+    }
+    {
+        typedef char T;
+        typedef std::deque<T, safe_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.pop_front();
+        assert(is_double_ended_contiguous_container_asan_correct(c));
+    }
     __sanitizer_set_death_callback(do_exit);
     {
         typedef int T;

diff  --git a/libcxx/test/libcxx/containers/sequences/deque/asan_turning_off.pass.cpp b/libcxx/test/libcxx/containers/sequences/deque/asan_turning_off.pass.cpp
new file mode 100644
index 00000000000000..e9b9cde64ee91a
--- /dev/null
+++ b/libcxx/test/libcxx/containers/sequences/deque/asan_turning_off.pass.cpp
@@ -0,0 +1,76 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03
+
+// <deque>
+
+// Test based on: https://bugs.chromium.org/p/chromium/issues/detail?id=1419798#c5
+// Some allocators during deallocation may not call destructors and just reuse memory.
+// In those situations, one may want to deactivate annotations for a specific allocator.
+// It's possible with __asan_annotate_container_with_allocator template class.
+// This test confirms that those allocators work after turning off annotations.
+
+#include <cassert>
+#include <deque>
+#include <new>
+
+struct reuse_allocator {
+  static size_t const N = 100;
+  reuse_allocator() {
+    for (size_t i = 0; i < N; ++i)
+      __buffers[i] = new char[8 * 1024];
+  }
+  ~reuse_allocator() {
+    for (size_t i = 0; i < N; ++i)
+      delete[] (char*)__buffers[i];
+  }
+  void* alloc() {
+    assert(__next_id < N);
+    return __buffers[__next_id++];
+  }
+  void reset() { __next_id = 0; }
+  void* __buffers[N];
+  size_t __next_id = 0;
+} reuse_buffers;
+
+template <typename T>
+struct user_allocator {
+  using value_type = T;
+  user_allocator() = default;
+  template <class U>
+  user_allocator(user_allocator<U>) {}
+  friend bool operator==(user_allocator, user_allocator) { return true; }
+  friend bool operator!=(user_allocator x, user_allocator y) { return !(x == y); }
+
+  T* allocate(size_t) { return (T*)reuse_buffers.alloc(); }
+  void deallocate(T*, size_t) noexcept {}
+};
+
+#ifdef _LIBCPP_HAS_ASAN_CONTAINER_ANNOTATIONS_FOR_ALL_ALLOCATORS
+template <class T>
+struct std::__asan_annotate_container_with_allocator<user_allocator<T>> : false_type {};
+#endif
+
+int main(int, char**) {
+  using D = std::deque<int, user_allocator<int>>;
+
+  {
+    D* d = new (reuse_buffers.alloc()) D();
+    for (int i = 0; i < 100; i++)
+      d->push_back(i);
+  }
+  reuse_buffers.reset();
+  {
+    D d;
+    for (int i = 0; i < 1000; i++)
+      d.push_back(i);
+  }
+
+  return 0;
+}


        


More information about the libcxx-commits mailing list