1// RUN: %clangxx_asan -fexceptions -O %s -o %t && %env_asan_opts=detect_stack_use_after_return=0 %run %t
2//
3// Test __sanitizer_annotate_contiguous_container.
4
5#include "defines.h"
6#include <algorithm>
7#include <numeric>
8#include <vector>
9
10#include <assert.h>
11#include <sanitizer/asan_interface.h>
12#include <stdio.h>
13#include <stdlib.h>
14#include <string.h>
15
16static constexpr size_t kGranularity = 8;
17
18template <class T> static constexpr T RoundDown(T x) {
19 return reinterpret_cast<T>(reinterpret_cast<uintptr_t>(x) &
20 ~(kGranularity - 1));
21}
22
23static std::vector<int> GetPoisonedState(char *begin, char *end) {
24 std::vector<int> result;
25 for (; begin != end;) {
26 int poisoned = 0;
27 for (; begin != end && __asan_address_is_poisoned(addr: begin); ++begin)
28 ++poisoned;
29 result.push_back(poisoned);
30 int unpoisoned = 0;
31 for (; begin != end && !__asan_address_is_poisoned(addr: begin); ++begin)
32 ++unpoisoned;
33 result.push_back(unpoisoned);
34 }
35 return result;
36}
37
38static int GetFirstMismatch(const std::vector<int> &a,
39 const std::vector<int> &b) {
40 auto mismatch = std::mismatch(a.begin(), a.end(), b.begin(), b.end());
41 return std::accumulate(a.begin(), mismatch.first, 0) +
42 std::min(*mismatch.first, *mismatch.second);
43}
44
45void TestContainer(size_t capacity, size_t off_begin, bool poison_buffer) {
46 size_t buffer_size = capacity + off_begin + kGranularity * 2;
47 char *buffer = new char[buffer_size];
48 if (poison_buffer)
49 __asan_poison_memory_region(addr: buffer, size: buffer_size);
50 char *st_beg = buffer + off_begin;
51 char *st_end = st_beg + capacity;
52 char *end = poison_buffer ? st_beg : st_end;
53
54 for (int i = 0; i < 1000; i++) {
55 size_t size = rand() % (capacity + 1);
56 assert(size <= capacity);
57 char *old_end = end;
58 end = st_beg + size;
59 __sanitizer_annotate_contiguous_container(beg: st_beg, end: st_end, old_mid: old_end, new_mid: end);
60
61 char *cur = buffer;
62 for (; cur < RoundDown(x: st_beg); ++cur)
63 assert(__asan_address_is_poisoned(cur) == poison_buffer);
64 // The prefix of the first incomplete granule can switch from poisoned to
65 // unpoisoned but not otherwise.
66 for (; cur < st_beg; ++cur)
67 assert(poison_buffer || !__asan_address_is_poisoned(cur));
68 for (; cur < end; ++cur)
69 assert(!__asan_address_is_poisoned(cur));
70 for (; cur < RoundDown(x: st_end); ++cur)
71 assert(__asan_address_is_poisoned(cur));
72 // The suffix of the last incomplete granule must be poisoned the same as
73 // bytes after the end.
74 for (; cur != st_end + kGranularity; ++cur)
75 assert(__asan_address_is_poisoned(cur) == poison_buffer);
76 }
77
78 // Precalculate masks.
79 std::vector<std::vector<int>> masks(capacity + 1);
80 for (int i = 0; i <= capacity; i++) {
81 char *old_end = end;
82 end = st_beg + i;
83 __sanitizer_annotate_contiguous_container(beg: st_beg, end: st_end, old_mid: old_end, new_mid: end);
84 masks[i] = GetPoisonedState(st_beg, st_end);
85 }
86 for (int i = 0; i <= capacity; i++) {
87 char *old_end = end;
88 end = st_beg + i;
89 __sanitizer_annotate_contiguous_container(beg: st_beg, end: st_end, old_mid: old_end, new_mid: end);
90
91 char *cur_first = std::max(end - 2 * kGranularity, st_beg);
92 char *cur_last = std::min(end + 2 * kGranularity, st_end);
93 for (char *cur = cur_first; cur <= cur_last; ++cur) {
94 bool is_valid =
95 __sanitizer_verify_contiguous_container(beg: st_beg, mid: cur, end: st_end);
96 const void *bad_address =
97 __sanitizer_contiguous_container_find_bad_address(beg: st_beg, mid: cur,
98 end: st_end);
99 if (cur == end ||
100 // The last unaligned granule of the storage followed by unpoisoned
101 // bytes looks the same.
102 (!poison_buffer && RoundDown(st_end) <= std::min(cur, end))) {
103 assert(is_valid);
104 assert(!bad_address);
105 continue;
106 }
107 assert(!is_valid);
108 assert(bad_address == std::min(cur, end));
109 assert(bad_address ==
110 st_beg + GetFirstMismatch(masks[i], masks[cur - st_beg]));
111 }
112 }
113
114 __asan_unpoison_memory_region(addr: buffer, size: buffer_size);
115 delete[] buffer;
116}
117
118void TestDoubleEndedContainer(size_t capacity, size_t off_begin,
119 bool poison_buffer) {
120 size_t buffer_size = capacity + off_begin + kGranularity * 2;
121 char *buffer = new char[buffer_size];
122 if (poison_buffer)
123 __asan_poison_memory_region(addr: buffer, size: buffer_size);
124 char *st_beg = buffer + off_begin;
125 char *st_end = st_beg + capacity;
126 char *beg = st_beg;
127 char *end = poison_buffer ? st_beg : st_end;
128
129 for (int i = 0; i < 1000; i++) {
130 size_t size = rand() % (capacity + 1);
131 size_t skipped = rand() % (capacity - size + 1);
132 assert(size <= capacity);
133 char *old_beg = beg;
134 char *old_end = end;
135 beg = st_beg + skipped;
136 end = beg + size;
137
138 __sanitizer_annotate_double_ended_contiguous_container(
139 storage_beg: st_beg, storage_end: st_end, old_container_beg: old_beg, old_container_end: old_end, new_container_beg: beg, new_container_end: end);
140
141 char *cur = buffer;
142 for (; cur < RoundDown(x: st_beg); ++cur)
143 assert(__asan_address_is_poisoned(cur) == poison_buffer);
144 // The prefix of the first incomplete granule can switch from poisoned to
145 // unpoisoned but not otherwise.
146 for (; cur < st_beg; ++cur)
147 assert(poison_buffer || !__asan_address_is_poisoned(cur));
148 if (beg != end) {
149 for (; cur < RoundDown(x: beg); ++cur)
150 assert(__asan_address_is_poisoned(cur));
151
152 for (; cur < end; ++cur)
153 assert(!__asan_address_is_poisoned(cur));
154 }
155 for (; cur < RoundDown(x: st_end); ++cur)
156 assert(__asan_address_is_poisoned(cur));
157 // The suffix of the last incomplete granule must be poisoned the same as
158 // bytes after the end.
159 for (; cur != st_end + kGranularity; ++cur)
160 assert(__asan_address_is_poisoned(cur) == poison_buffer);
161 }
162
163 if (capacity < 32) {
164
165 // Precalculate masks.
166 std::vector<std::vector<std::vector<int>>> masks(
167 capacity + 1, std::vector<std::vector<int>>(capacity + 1));
168 for (int i = 0; i <= capacity; i++) {
169 for (int j = i; j <= capacity; j++) {
170 char *old_beg = beg;
171 char *old_end = end;
172 beg = st_beg + i;
173 end = st_beg + j;
174 __sanitizer_annotate_double_ended_contiguous_container(
175 storage_beg: st_beg, storage_end: st_end, old_container_beg: old_beg, old_container_end: old_end, new_container_beg: beg, new_container_end: end);
176 masks[i][j] = GetPoisonedState(st_beg, st_end);
177 }
178 }
179
180 for (int i = 0; i <= capacity; i++) {
181 for (int j = i; j <= capacity; j++) {
182 char *old_beg = beg;
183 char *old_end = end;
184 beg = st_beg + i;
185 end = st_beg + j;
186 __sanitizer_annotate_double_ended_contiguous_container(
187 storage_beg: st_beg, storage_end: st_end, old_container_beg: old_beg, old_container_end: old_end, new_container_beg: beg, new_container_end: end);
188
189 // Try to mismatch the end of the container.
190 char *cur_first = std::max(end - 2 * kGranularity, beg);
191 char *cur_last = std::min(end + 2 * kGranularity, st_end);
192 for (char *cur = cur_first; cur <= cur_last; ++cur) {
193 bool is_valid = __sanitizer_verify_double_ended_contiguous_container(
194 storage_beg: st_beg, container_beg: beg, container_end: cur, storage_end: st_end);
195 const void *bad_address =
196 __sanitizer_double_ended_contiguous_container_find_bad_address(
197 storage_beg: st_beg, container_beg: beg, container_end: cur, storage_end: st_end);
198
199 if (cur == end ||
200 // The last unaligned granule of the storage followed by unpoisoned
201 // bytes looks the same.
202 (!poison_buffer && RoundDown(st_end) <= std::min(cur, end))) {
203 assert(is_valid);
204 assert(!bad_address);
205 continue;
206 }
207
208 assert(!is_valid);
209 assert(bad_address);
210 assert(bad_address ==
211 st_beg +
212 GetFirstMismatch(masks[i][j], masks[i][cur - st_beg]));
213 }
214
215 // Try to mismatch the begin of the container.
216 cur_first = std::max(beg - 2 * kGranularity, st_beg);
217 cur_last = std::min(beg + 2 * kGranularity, end);
218 for (char *cur = cur_first; cur <= cur_last; ++cur) {
219 bool is_valid = __sanitizer_verify_double_ended_contiguous_container(
220 storage_beg: st_beg, container_beg: cur, container_end: end, storage_end: st_end);
221 const void *bad_address =
222 __sanitizer_double_ended_contiguous_container_find_bad_address(
223 storage_beg: st_beg, container_beg: cur, container_end: end, storage_end: st_end);
224
225 if (cur == beg ||
226 // The last unaligned granule of the storage followed by unpoisoned
227 // bytes looks the same.
228 (!poison_buffer && RoundDown(st_end) <= std::min(cur, beg) ||
229 // The first unaligned granule of non-empty container looks the
230 // same.
231 (std::max(beg, cur) < end &&
232 RoundDown(beg) == RoundDown(cur)))) {
233 assert(is_valid);
234 assert(!bad_address);
235 continue;
236 }
237 assert(!is_valid);
238 assert(bad_address);
239 assert(bad_address ==
240 st_beg +
241 GetFirstMismatch(masks[i][j], masks[cur - st_beg][j]));
242 }
243 }
244 }
245 }
246
247 __asan_unpoison_memory_region(addr: buffer, size: buffer_size);
248 delete[] buffer;
249}
250
251ATTRIBUTE_NOINLINE void Throw() { throw 1; }
252
253ATTRIBUTE_NOINLINE void ThrowAndCatch() {
254 try {
255 Throw();
256 } catch (...) {
257 }
258}
259
260void TestThrow() {
261 char x[32];
262 __sanitizer_annotate_contiguous_container(beg: x, end: x + 32, old_mid: x + 32, new_mid: x + 14);
263 assert(!__asan_address_is_poisoned(x + 13));
264 assert(__asan_address_is_poisoned(x + 14));
265 ThrowAndCatch();
266 assert(!__asan_address_is_poisoned(x + 13));
267 assert(!__asan_address_is_poisoned(x + 14));
268 __sanitizer_annotate_contiguous_container(beg: x, end: x + 32, old_mid: x + 14, new_mid: x + 32);
269 assert(!__asan_address_is_poisoned(x + 13));
270 assert(!__asan_address_is_poisoned(x + 14));
271}
272
273int main(int argc, char **argv) {
274 int n = argc == 1 ? 64 : atoi(nptr: argv[1]);
275 for (int i = 0; i <= n; i++) {
276 for (int j = 0; j < kGranularity * 2; j++) {
277 for (int poison = 0; poison < 2; ++poison) {
278 TestContainer(capacity: i, off_begin: j, poison_buffer: poison);
279 TestDoubleEndedContainer(capacity: i, off_begin: j, poison_buffer: poison);
280 }
281 }
282 }
283 TestThrow();
284}
285

Provided by KDAB

Privacy Policy
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more

source code of compiler-rt/test/asan/TestCases/contiguous_container.cpp