1 | // RUN: %clangxx_asan -fexceptions -O %s -o %t && %env_asan_opts=detect_stack_use_after_return=0 %run %t |
2 | // |
3 | // Test __sanitizer_copy_contiguous_container_annotations. |
4 | |
5 | #include <algorithm> |
6 | #include <iostream> |
7 | #include <memory> |
8 | #include <numeric> |
9 | #include <vector> |
10 | |
11 | #include <assert.h> |
12 | #include <sanitizer/asan_interface.h> |
13 | #include <stdio.h> |
14 | #include <stdlib.h> |
15 | #include <string.h> |
16 | |
17 | static constexpr size_t kGranularity = 8; |
18 | |
19 | template <class T> static constexpr T RoundDown(T x) { |
20 | return reinterpret_cast<T>(reinterpret_cast<uintptr_t>(x) & |
21 | ~(kGranularity - 1)); |
22 | } |
23 | template <class T> static constexpr T RoundUp(T x) { |
24 | return reinterpret_cast<T>( |
25 | RoundDown(x: reinterpret_cast<uintptr_t>(x) + kGranularity - 1)); |
26 | } |
27 | |
28 | static std::vector<int> GetPoisonedState(char *begin, char *end) { |
29 | std::vector<int> result; |
30 | for (char *ptr = begin; ptr != end; ++ptr) { |
31 | result.push_back(__asan_address_is_poisoned(ptr)); |
32 | } |
33 | return result; |
34 | } |
35 | |
36 | static void RandomPoison(char *beg, char *end) { |
37 | assert(beg == RoundDown(beg)); |
38 | assert(end == RoundDown(end)); |
39 | __asan_poison_memory_region(addr: beg, size: end - beg); |
40 | for (beg = RoundUp(x: beg); beg < end; beg += kGranularity) { |
41 | __asan_unpoison_memory_region(addr: beg, size: rand() % (kGranularity + 1)); |
42 | } |
43 | } |
44 | |
45 | template <bool benchmark> |
46 | static void Test(size_t capacity, size_t off_src, size_t off_dst, |
47 | char *src_buffer_beg, char *src_buffer_end, |
48 | char *dst_buffer_beg, char *dst_buffer_end) { |
49 | size_t dst_buffer_size = dst_buffer_end - dst_buffer_beg; |
50 | char *src_beg = src_buffer_beg + off_src; |
51 | char *src_end = src_beg + capacity; |
52 | |
53 | char *dst_beg = dst_buffer_beg + off_dst; |
54 | char *dst_end = dst_beg + capacity; |
55 | if (benchmark) { |
56 | __sanitizer_copy_contiguous_container_annotations(src_beg, src_end, dst_beg, |
57 | dst_end); |
58 | return; |
59 | } |
60 | |
61 | std::vector<int> src_poison_states = |
62 | GetPoisonedState(src_buffer_beg, src_buffer_end); |
63 | std::vector<int> dst_poison_before = |
64 | GetPoisonedState(dst_buffer_beg, dst_buffer_end); |
65 | __sanitizer_copy_contiguous_container_annotations(src_beg, src_end, dst_beg, |
66 | dst_end); |
67 | std::vector<int> dst_poison_after = |
68 | GetPoisonedState(dst_buffer_beg, dst_buffer_end); |
69 | |
70 | // Create ideal copy of src over dst. |
71 | std::vector<int> dst_poison_exp = dst_poison_before; |
72 | for (size_t cur = 0; cur < capacity; ++cur) |
73 | dst_poison_exp[off_dst + cur] = src_poison_states[off_src + cur]; |
74 | |
75 | // Unpoison prefixes of Asan granules. |
76 | for (size_t cur = dst_buffer_size - 1; cur > 0; --cur) { |
77 | if (cur % kGranularity != 0 && !dst_poison_exp[cur]) |
78 | dst_poison_exp[cur - 1] = 0; |
79 | } |
80 | |
81 | if (dst_poison_after != dst_poison_exp) { |
82 | std::cerr << "[" << off_dst << ", " << off_dst + capacity << ")\n" ; |
83 | for (size_t i = 0; i < dst_poison_after.size(); ++i) { |
84 | std::cerr << i << ":\t" << dst_poison_before[i] << "\t" |
85 | << dst_poison_after[i] << "\t" << dst_poison_exp[i] << "\n" ; |
86 | } |
87 | std::cerr << "----------\n" ; |
88 | |
89 | assert(dst_poison_after == dst_poison_exp); |
90 | } |
91 | } |
92 | |
93 | template <bool benchmark> |
94 | static void TestNonOverlappingContainers(size_t capacity, size_t off_src, |
95 | size_t off_dst) { |
96 | // Test will copy [off_src, off_src + capacity) to [off_dst, off_dst + capacity). |
97 | // Allocate buffers to have additional granule before and after tested ranges. |
98 | off_src += kGranularity; |
99 | off_dst += kGranularity; |
100 | size_t src_buffer_size = RoundUp(x: off_src + capacity) + kGranularity; |
101 | size_t dst_buffer_size = RoundUp(x: off_dst + capacity) + kGranularity; |
102 | |
103 | std::unique_ptr<char[]> src_buffer = |
104 | std::make_unique<char[]>(src_buffer_size); |
105 | std::unique_ptr<char[]> dst_buffer = |
106 | std::make_unique<char[]>(dst_buffer_size); |
107 | |
108 | char *src_buffer_beg = src_buffer.get(); |
109 | char *src_buffer_end = src_buffer_beg + src_buffer_size; |
110 | assert(RoundDown(src_buffer_beg) == src_buffer_beg); |
111 | |
112 | char *dst_buffer_beg = dst_buffer.get(); |
113 | char *dst_buffer_end = dst_buffer_beg + dst_buffer_size; |
114 | assert(RoundDown(dst_buffer_beg) == dst_buffer_beg); |
115 | |
116 | for (int i = 0; i < 35; i++) { |
117 | if (!benchmark || !i) { |
118 | RandomPoison(beg: src_buffer_beg, end: src_buffer_end); |
119 | RandomPoison(beg: dst_buffer_beg, end: dst_buffer_end); |
120 | } |
121 | |
122 | Test<benchmark>(capacity, off_src, off_dst, src_buffer_beg, src_buffer_end, |
123 | dst_buffer_beg, dst_buffer_end); |
124 | } |
125 | |
126 | __asan_unpoison_memory_region(addr: src_buffer_beg, size: src_buffer_size); |
127 | __asan_unpoison_memory_region(addr: dst_buffer_beg, size: dst_buffer_size); |
128 | } |
129 | |
130 | template <bool benchmark> |
131 | static void TestOverlappingContainers(size_t capacity, size_t off_src, |
132 | size_t off_dst) { |
133 | // Test will copy [off_src, off_src + capacity) to [off_dst, off_dst + capacity). |
134 | // Allocate buffers to have additional granule before and after tested ranges. |
135 | off_src += kGranularity; |
136 | off_dst += kGranularity; |
137 | size_t buffer_size = |
138 | RoundUp(std::max(off_src, off_dst) + capacity) + kGranularity; |
139 | |
140 | // Use unique_ptr with a custom deleter to manage the buffer |
141 | std::unique_ptr<char[]> buffer = std::make_unique<char[]>(buffer_size); |
142 | |
143 | char *buffer_beg = buffer.get(); |
144 | char *buffer_end = buffer_beg + buffer_size; |
145 | assert(RoundDown(buffer_beg) == buffer_beg); |
146 | |
147 | for (int i = 0; i < 35; i++) { |
148 | if (!benchmark || !i) |
149 | RandomPoison(beg: buffer_beg, end: buffer_end); |
150 | Test<benchmark>(capacity, off_src, off_dst, buffer_beg, buffer_end, |
151 | buffer_beg, buffer_end); |
152 | } |
153 | |
154 | __asan_unpoison_memory_region(addr: buffer_beg, size: buffer_size); |
155 | } |
156 | |
157 | int main(int argc, char **argv) { |
158 | int n = argc == 1 ? 64 : atoi(nptr: argv[1]); |
159 | for (size_t off_src = 0; off_src < kGranularity; off_src++) { |
160 | for (size_t off_dst = 0; off_dst < kGranularity; off_dst++) { |
161 | for (int capacity = 0; capacity <= n; capacity++) { |
162 | if (n < 1024) { |
163 | TestNonOverlappingContainers<false>(capacity, off_src, off_dst); |
164 | TestOverlappingContainers<false>(capacity, off_src, off_dst); |
165 | } else { |
166 | TestNonOverlappingContainers<true>(capacity, off_src, off_dst); |
167 | TestOverlappingContainers<true>(capacity, off_src, off_dst); |
168 | } |
169 | } |
170 | } |
171 | } |
172 | } |