16
16
// In those situations, one may want to deactivate annotations for a specific allocator.
17
17
// It's possible with __asan_annotate_container_with_allocator template class.
18
18
// This test confirms that those allocators work after turning off annotations.
19
+ //
20
+ // A context to this test is a situations when memory is repurposed and destructors are not called.
21
+ // Related issue: https://github.com/llvm/llvm-project/issues/60384
22
+ //
23
+ // That issue appeared in the past and was addressed here: https://reviews.llvm.org/D145628
24
+ //
25
+ // There is also a question, if it's UB. And it's not clear.
26
+ // Related discussion: https://reviews.llvm.org/D136765#4155262
27
+ // Related notes: https://eel.is/c++draft/basic.life#6
28
+ //
29
+ // Even if it is UB, we want to make sure that it works that way, because people rely on this behavior.
30
+ // In similar situations. a user explicitly turns off annotations for a specific allocator.
19
31
20
32
#include < assert.h>
21
33
#include < stdlib.h>
22
34
#include < string>
23
35
#include < new>
24
36
37
+ // Allocator with pre-allocated (with malloc in constructor) buffers.
38
+ // Memory may be freed without calling destructors.
25
39
struct reuse_allocator {
26
40
static size_t const N = 100 ;
27
41
reuse_allocator () {
@@ -50,10 +64,15 @@ struct user_allocator {
50
64
friend bool operator ==(user_allocator, user_allocator) { return true ; }
51
65
friend bool operator !=(user_allocator x, user_allocator y) { return !(x == y); }
52
66
53
- T* allocate (size_t ) { return (T*)reuse_buffers.alloc (); }
67
+ T* allocate (size_t n) {
68
+ if (n * sizeof (T) > 8 * 1024 )
69
+ throw std::bad_array_new_length ();
70
+ return (T*)reuse_buffers.alloc ();
71
+ }
54
72
void deallocate (T*, size_t ) noexcept {}
55
73
};
56
74
75
+ // Turn off annotations for user_allocator:
57
76
template <class T >
58
77
struct std ::__asan_annotate_container_with_allocator<user_allocator<T>> {
59
78
static bool const value = false ;
@@ -63,15 +82,20 @@ int main() {
63
82
using S = std::basic_string<char , std::char_traits<char >, user_allocator<char >>;
64
83
65
84
{
85
+ // Create a string with a buffer from reuse allocator object:
66
86
S* s = new (reuse_buffers.alloc ()) S ();
67
- for (int i = 0 ; i < 100 ; i++)
87
+ // Use string, so it's poisoned, if container annotations for that allocator are not turned off:
88
+ for (int i = 0 ; i < 40 ; i++)
68
89
s->push_back (' a' );
69
90
}
91
+ // Reset the state of the allocator, don't call destructors, allow memory to be reused:
70
92
reuse_buffers.reset ();
71
93
{
94
+ // Create a next string with the same allocator, so the same buffer due to the reset:
72
95
S s;
73
- for (int i = 0 ; i < 1000 ; i++)
74
- s.push_back (' b' );
96
+ // Use memory inside the string again, if it's poisoned, an error will be raised:
97
+ for (int i = 0 ; i < 60 ; i++)
98
+ s.push_back (' a' );
75
99
}
76
100
77
101
return 0 ;
0 commit comments