forked from llvm/llvm-project
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[ASan][libc++] Annotating
std::basic_string
with all allocators
This commit turns on ASan annotations in `std::basic_string` for all allocators by default. Originally suggested here: https://reviews.llvm.org/D146214 This commit is part of our efforts to support container annotations with (almost) every allocator. Annotating `std::basic_string` with default allocator is implemented in llvm#72677. Support in ASan API exests since llvm@dd1b7b7. This patch removes the check in std::basic_string annotation member function (__annotate_contiguous_container) to support different allocators. You can turn off annotations for a specific allocator based on changes from llvm@2fa1bec. This PR 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` and `std::deque` collections. These enhancements empower ASan to effectively detect instances where the instrumented program attempts to access memory within a collection's internal allocation that remains unused. This includes cases where access occurs before or after the stored elements in `std::deque`, or between the `std::basic_string`'s size (including the null terminator) and capacity bounds. The introduction of these annotations was spurred by a real-world software bug discovered by Trail of Bits, involving an out-of-bounds memory access during the comparison of two strings using the `std::equals` function. This function was taking iterators (`iter1_begin`, `iter1_end`, `iter2_begin`) to perform the comparison, using a custom comparison function. When the `iter1` object exceeded the length of `iter2`, an out-of-bounds read could occur on the `iter2` object. Container sanitization, upon enabling these annotations, would effectively identify and flag this potential vulnerability. If you have any questions, please email: - advenam.tacet@trailofbits.com - disconnect3d@trailofbits.com
- Loading branch information
Advenam Tacet
committed
Jan 11, 2024
1 parent
31fd6d1
commit 7af271d
Showing
4 changed files
with
134 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
54 changes: 54 additions & 0 deletions
54
libcxx/test/libcxx/containers/strings/basic.string/asan.pass.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// 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 | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
// REQUIRES: asan | ||
// UNSUPPORTED: c++03 | ||
|
||
// Basic test if ASan annotations work for basic_string. | ||
|
||
#include <string> | ||
#include <cassert> | ||
#include <cstdlib> | ||
|
||
#include "asan_testing.h" | ||
#include "min_allocator.h" | ||
#include "test_iterators.h" | ||
#include "test_macros.h" | ||
|
||
extern "C" void __sanitizer_set_death_callback(void (*callback)(void)); | ||
|
||
void do_exit() { exit(0); } | ||
|
||
int main(int, char**) { | ||
{ | ||
typedef cpp17_input_iterator<char*> MyInputIter; | ||
// Should not trigger ASan. | ||
std::basic_string<char, std::char_traits<char>, safe_allocator<char>> v; | ||
char i[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'a', 'b', 'c', 'd', 'e', | ||
'f', 'g', 'h', 'i', 'j', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'}; | ||
|
||
v.insert(v.begin(), MyInputIter(i), MyInputIter(i + 29)); | ||
assert(v[0] == 'a'); | ||
assert(is_string_asan_correct(v)); | ||
} | ||
|
||
__sanitizer_set_death_callback(do_exit); | ||
{ | ||
using T = char; | ||
using C = std::basic_string<T, std::char_traits<T>, safe_allocator<T>>; | ||
const T t[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'a', 'b', 'c', 'd', 'e', | ||
'f', 'g', 'h', 'i', 'j', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'}; | ||
C c(std::begin(t), std::end(t)); | ||
assert(is_string_asan_correct(c)); | ||
assert(__sanitizer_verify_contiguous_container(c.data(), c.data() + c.size() + 1, c.data() + c.capacity() + 1) != | ||
0); | ||
volatile T foo = c[c.size() + 1]; // should trigger ASAN. Use volatile to prevent being optimized away. | ||
assert(false); // if we got here, ASAN didn't trigger | ||
((void)foo); | ||
} | ||
} |
78 changes: 78 additions & 0 deletions
78
libcxx/test/libcxx/containers/strings/basic.string/asan_turning_off.pass.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// 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 | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
// REQUIRES: asan | ||
// UNSUPPORTED: c++03 | ||
|
||
// <string> | ||
|
||
// 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 <assert.h> | ||
#include <stdlib.h> | ||
#include <string> | ||
#include <new> | ||
|
||
struct reuse_allocator { | ||
static size_t const N = 100; | ||
reuse_allocator() { | ||
for (size_t i = 0; i < N; ++i) | ||
__buffers[i] = malloc(8 * 1024); | ||
} | ||
~reuse_allocator() { | ||
for (size_t i = 0; i < N; ++i) | ||
free(__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 {} | ||
}; | ||
|
||
template <class T> | ||
struct std::__asan_annotate_container_with_allocator<user_allocator<T>> { | ||
static bool const value = false; | ||
}; | ||
|
||
int main() { | ||
using S = std::basic_string<char, std::char_traits<char>, user_allocator<char>>; | ||
|
||
{ | ||
S* s = new (reuse_buffers.alloc()) S(); | ||
for (int i = 0; i < 100; i++) | ||
s->push_back('a'); | ||
} | ||
reuse_buffers.reset(); | ||
{ | ||
S s; | ||
for (int i = 0; i < 1000; i++) | ||
s.push_back('b'); | ||
} | ||
|
||
return 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters