[cfe-dev] annotating libc++ to catch buffer overflows in vector/string with asan

Kostya Serebryany kcc at google.com
Mon Nov 18 07:25:25 PST 2013


Hi Marshall, Howard,

[continuing our discussion with Marshall on llvm dev meeting]
Consider the following test case:

#include <vector>
#include <assert.h>
typedef long T;
int main() {
  std::vector<T> v;
  v.push_back(0);
  v.push_back(1);
  v.push_back(2);
  assert(v.capacity() >= 4);
  T *p = &v[0];
  return p[3];  // OOPS.
}

Here we have a vector with size==3 and capacity==4, and we are reading
the element with index 3 using raw pointer arithmetic.
Today we have no way to detect this bug. AddressSanitizer is silent because
we still access the heap-allocated buffer within bounds.
I propose to annotate the libc++ sources so that AddressSanitizer can catch
these bugs.
Here is a prototype of the change.
I've only annotated __push_back_slow_path which is enough to handle the
test case above.
More methods will need to be annotated for full functionality.
Similar annotations are possible for std::string.
__sanitizer_annotate_contiguous_container is declared in
compiler-rt/include/sanitizer/common_interface_defs.h

--- include/vector      (revision 195011)
+++ include/vector      (working copy)
@@ -282,6 +282,15 @@
 #   define _LIBCPP_ASSERT(x, m) ((void)0)
 #endif

+#if __has_feature(address_sanitizer)
+extern "C" void __sanitizer_annotate_contiguous_container(void *, void *,
+                                                          void *, void *);
+#   define _LIBCPP_ANNOTATE_CONTIGUOUS_CONTAINER(beg, end, old_mid,
new_mid) \
+  __sanitizer_annotate_contiguous_container(beg, end, old_mid, new_mid)
+#else
+#   define _LIBCPP_ANNOTATE_CONTIGUOUS_CONTAINER(beg, end, old_mid,
new_mid)
+#endif
+
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #pragma GCC system_header
 #endif
@@ -1525,7 +1534,12 @@
     // __v.push_back(_VSTD::forward<_Up>(__x));
     __alloc_traits::construct(__a, _VSTD::__to_raw_pointer(__v.__end_),
_VSTD::forward<_Up>(__x));
     __v.__end_++;
+    if (data())
+      _LIBCPP_ANNOTATE_CONTIGUOUS_CONTAINER(
+          data(), data() + capacity(), data() + size(), data() +
capacity());
     __swap_out_circular_buffer(__v);
+    _LIBCPP_ANNOTATE_CONTIGUOUS_CONTAINER(data(), data() + capacity(),
+                                          data() + capacity(), data() +
size());
 }

Running:
% clang++ -g  -O -fsanitize=address  -I$HOME/llvm/projects/libcxx/include
vect_oob.cc -fno-exceptions;  ./a.out
=================================================================
==20838==ERROR: AddressSanitizer: contiguous-container-buffer-overflow on
address 0x60300000eff8 at pc 0x465455 bp 0x7fffc0e9a9f0 sp 0x7fffc0e9a9e8
READ of size 8 at 0x60300000eff8 thread T0
    #0 0x465454 in main /tmp/vect_oob.cc:11

0x60300000eff8 is located 24 bytes inside of 32-byte region
[0x60300000efe0,0x60300000f000)
allocated by thread T0 here:
    #0 0x44f8fe in operator new(unsigned long)
    #1 0x46559d in std::__1::allocator<long>::allocate(unsigned long, void
const*)
    #2 0x46559d in std::__1::allocator_traits<std::__1::allocator<long>
>::allocate(std::__1::allocator<long>&, unsigned long)
    #3 0x46559d in std::__1::vector<long, std::__1::allocator<long>
>::size() const
    #4 0x46559d in void std::__1::vector<long, std::__1::allocator<long>
>::__push_back_slow_path<long const>(long const&)


Do you like the idea?
Any comments about the prototype patch?
Any suggestion how to test these annotations in libc++ (we need some kind
of DEATH tests)?

Thanks,

--kcc
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20131118/db478074/attachment.html>


More information about the cfe-dev mailing list