llvm-project/libcxx/test/support/asan_testing.h
Advenam Tacet 10ec9276d4 [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
2023-06-27 06:55:09 +02:00

60 lines
2.0 KiB
C++

//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
#ifndef ASAN_TESTING_H
#define ASAN_TESTING_H
#include "test_macros.h"
#include <vector>
#if TEST_HAS_FEATURE(address_sanitizer)
extern "C" int __sanitizer_verify_contiguous_container
( const void *beg, const void *mid, const void *end );
template <typename T, typename Alloc>
TEST_CONSTEXPR bool is_contiguous_container_asan_correct ( const std::vector<T, Alloc> &c )
{
if (std::__libcpp_is_constant_evaluated())
return true;
if (std::is_same<Alloc, std::allocator<T> >::value && c.data() != NULL)
return __sanitizer_verify_contiguous_container(
c.data(), c.data() + c.size(), c.data() + c.capacity()) != 0;
return true;
}
#else
template <typename T, typename Alloc>
TEST_CONSTEXPR bool is_contiguous_container_asan_correct ( const std::vector<T, Alloc> &)
{
return true;
}
#endif // TEST_HAS_FEATURE(address_sanitizer)
#if TEST_HAS_FEATURE(address_sanitizer) && _LIBCPP_CLANG_VER >= 1600
extern "C" int __sanitizer_verify_double_ended_contiguous_container(
const void* beg, const void* con_beg, const void* con_end, const void* end);
extern "C" bool __sanitizer_is_annotable(const void* address, const unsigned long size);
#include <deque>
template <class T, class Alloc>
TEST_CONSTEXPR bool is_double_ended_contiguous_container_asan_correct(const std::deque<T, Alloc>& c) {
if (TEST_IS_CONSTANT_EVALUATED)
return true;
if (std::is_same<Alloc, std::allocator<T> >::value)
return c.__verify_asan_annotations();
return true;
}
#else
# include <deque>
template <class T, class Alloc>
TEST_CONSTEXPR bool is_double_ended_contiguous_container_asan_correct(const std::deque<T, Alloc>&) {
return true;
}
#endif
#endif // ASAN_TESTING_H