[2a/3][ASan][libcxx] std::deque annotations
authorAdvenam Tacet <advenam.tacet@trailofbits.com>
Thu, 1 Jun 2023 05:13:24 +0000 (05:13 +0000)
committerAdvenam Tacet <advenam.tacet@trailofbits.com>
Tue, 27 Jun 2023 04:55:09 +0000 (06:55 +0200)
commit10ec9276d40024c23a481e6671dad1521151dd85
tree3f45ca2b9f90c2210ab5419458dab0d4b8bc5f80
parent0b2c0dc63faa51835b864f1277f4d5ddffc49f6c
[2a/3][ASan][libcxx] std::deque annotations

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.

This revision introduces annotations for `std::deque`. Each chunk of the container can now be annotated using the `__sanitizer_annotate_double_ended_contiguous_container` function, which was added in the rG1c5ad6d2c01294a0decde43a88e9c27d7437d157. Any attempt to access poisoned memory will trigger an ASan error. Although false negatives are rare, they are possible due to limitations in the ASan API, where a few (usually up to 7) bytes before the container may remain unpoisoned. There are no false positives in the same way as with `std::vector` annotations.

This patch only supports objects (deques) that use the standard allocator. However, it can be easily extended to support all allocators, as suggested in the D146815 revision.

Furthermore, the patch includes the addition of the `is_double_ended_contiguous_container_asan_correct` function to `libcxx/test/support/asan_testing.h`. This function can be used to verify whether a `std::deque` object has been correctly annotated.

Finally, the patch extends the unit tests to verify ASan annotations (added LIBCPP_ASSERTs).
If a program is compiled without ASan, all helper functions will be no-ops. In binaries with ASan, there is a negligible performance impact since the code from the change is only executed when the deque container changes in size and it’s proportional to the change. It is important to note that regardless of whether or not these changes are in use, every access to the container's memory is instrumented.

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

Reviewed By: #libc, philnik

Differential Revision: https://reviews.llvm.org/D132092
60 files changed:
libcxx/include/__config
libcxx/include/deque
libcxx/test/libcxx/containers/sequences/deque/asan.pass.cpp [new file with mode: 0644]
libcxx/test/libcxx/containers/sequences/deque/asan_caterpillar.pass.cpp [new file with mode: 0644]
libcxx/test/std/containers/sequences/deque/deque.capacity/access.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.capacity/empty.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.capacity/max_size.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.capacity/resize_size.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.capacity/resize_size_value.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.capacity/shrink_to_fit.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.capacity/size.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.cons/alloc.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.cons/assign_initializer_list.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.cons/assign_iter_iter.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.cons/assign_size_value.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.cons/copy.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.cons/copy_alloc.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.cons/deduct.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.cons/default.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.cons/initializer_list.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.cons/initializer_list_alloc.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.cons/iter_iter.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.cons/iter_iter_alloc.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.cons/move.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.cons/move_alloc.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.cons/move_assign.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.cons/op_equal.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.cons/op_equal_initializer_list.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.cons/size.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.cons/size_value.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.cons/size_value_alloc.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.erasure/erase.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.erasure/erase_if.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.modifiers/clear.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.modifiers/emplace.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.modifiers/emplace_back.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.modifiers/emplace_front.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.modifiers/erase_iter.invalidation.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.modifiers/erase_iter.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.modifiers/erase_iter_iter.invalidation.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.modifiers/erase_iter_iter.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.modifiers/insert_iter_initializer_list.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.modifiers/insert_iter_iter.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.modifiers/insert_rvalue.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.modifiers/insert_size_value.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.modifiers/insert_value.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.modifiers/pop_back.invalidation.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.modifiers/pop_back.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.modifiers/pop_front.invalidation.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.modifiers/pop_front.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.modifiers/push_back.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.modifiers/push_back_rvalue.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.modifiers/push_front.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.modifiers/push_front_rvalue.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.special/copy.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.special/copy_backward.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.special/move.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.special/move_backward.pass.cpp
libcxx/test/std/containers/sequences/deque/deque.special/swap.pass.cpp
libcxx/test/support/asan_testing.h