<div dir="ltr">Hi Marshall, Howard,<br><br>[continuing our discussion with Marshall on llvm dev meeting]<br>Consider the following test case:<div><br>#include <vector><br>#include <assert.h><br>typedef long T;<br>
int main() {<br> std::vector<T> v;<br> v.push_back(0);<br> v.push_back(1);<br> v.push_back(2);<br> assert(v.capacity() >= 4);<br> T *p = &v[0];<br> return p[3]; // OOPS.<br>}<br><br>Here we have a vector with size==3 and capacity==4, and we are reading<br>
the element with index 3 using raw pointer arithmetic.<br>Today we have no way to detect this bug. AddressSanitizer is silent because<br>we still access the heap-allocated buffer within bounds.<br>I propose to annotate the libc++ sources so that AddressSanitizer can catch these bugs.<br>
Here is a prototype of the change.<br>I've only annotated __push_back_slow_path which is enough to handle the test case above.<br>More methods will need to be annotated for full functionality. </div><div>Similar annotations are possible for std::string.<br>
__sanitizer_annotate_contiguous_container is declared in <div>compiler-rt/include/sanitizer/common_interface_defs.h<br><br>--- include/vector (revision 195011)<br>+++ include/vector (working copy)<br>@@ -282,6 +282,15 @@<br>
# define _LIBCPP_ASSERT(x, m) ((void)0)<br> #endif<br> <br>+#if __has_feature(address_sanitizer)<br>+extern "C" void __sanitizer_annotate_contiguous_container(void *, void *,<br>+ void *, void *);<br>
+# define _LIBCPP_ANNOTATE_CONTIGUOUS_CONTAINER(beg, end, old_mid, new_mid) \<br>+ __sanitizer_annotate_contiguous_container(beg, end, old_mid, new_mid)<br>+#else<br>+# define _LIBCPP_ANNOTATE_CONTIGUOUS_CONTAINER(beg, end, old_mid, new_mid)<br>
+#endif<br>+<br> #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)<br> #pragma GCC system_header<br> #endif<br>@@ -1525,7 +1534,12 @@<br> // __v.push_back(_VSTD::forward<_Up>(__x));<br> __alloc_traits::construct(__a, _VSTD::__to_raw_pointer(__v.__end_), _VSTD::forward<_Up>(__x));<br>
__v.__end_++;<br>+ if (data())<br>+ _LIBCPP_ANNOTATE_CONTIGUOUS_CONTAINER(<br>+ data(), data() + capacity(), data() + size(), data() + capacity());<br> __swap_out_circular_buffer(__v);<br>+ _LIBCPP_ANNOTATE_CONTIGUOUS_CONTAINER(data(), data() + capacity(),<br>
+ data() + capacity(), data() + size());<br> }<br><br>Running: </div><div><div>% clang++ -g -O -fsanitize=address -I$HOME/llvm/projects/libcxx/include vect_oob.cc -fno-exceptions; ./a.out</div>
<div>=================================================================</div><div>==20838==ERROR: AddressSanitizer: contiguous-container-buffer-overflow on address 0x60300000eff8 at pc 0x465455 bp 0x7fffc0e9a9f0 sp 0x7fffc0e9a9e8</div>
<div>READ of size 8 at 0x60300000eff8 thread T0</div><div> #0 0x465454 in main /tmp/vect_oob.cc:11</div><div><br></div><div>0x60300000eff8 is located 24 bytes inside of 32-byte region [0x60300000efe0,0x60300000f000)</div>
<div>allocated by thread T0 here:</div><div> #0 0x44f8fe in operator new(unsigned long) </div><div> #1 0x46559d in std::__1::allocator<long>::allocate(unsigned long, void const*) </div><div> #2 0x46559d in std::__1::allocator_traits<std::__1::allocator<long> >::allocate(std::__1::allocator<long>&, unsigned long) </div>
<div> #3 0x46559d in std::__1::vector<long, std::__1::allocator<long> >::size() const </div><div> #4 0x46559d in void std::__1::vector<long, std::__1::allocator<long> >::__push_back_slow_path<long const>(long const&) </div>
<br><br>Do you like the idea? </div><div>Any comments about the prototype patch? </div><div>Any suggestion how to test these annotations in libc++ (we need some kind of DEATH tests)? </div><div><br>Thanks, </div></div><div>
<br></div><div>--kcc </div></div>