285 lines · cpp
1// RUN: %clangxx_asan -fexceptions -O %s -o %t && %env_asan_opts=detect_stack_use_after_return=0 %run %t2//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(begin); ++begin)28 ++poisoned;29 result.push_back(poisoned);30 int unpoisoned = 0;31 for (; begin != end && !__asan_address_is_poisoned(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(buffer, 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(st_beg, st_end, old_end, end);60 61 char *cur = buffer;62 for (; cur < RoundDown(st_beg); ++cur)63 assert(__asan_address_is_poisoned(cur) == poison_buffer);64 // The prefix of the first incomplete granule can switch from poisoned to65 // 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(st_end); ++cur)71 assert(__asan_address_is_poisoned(cur));72 // The suffix of the last incomplete granule must be poisoned the same as73 // 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(st_beg, st_end, old_end, 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(st_beg, st_end, old_end, 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(st_beg, cur, st_end);96 const void *bad_address =97 __sanitizer_contiguous_container_find_bad_address(st_beg, cur,98 st_end);99 if (cur == end ||100 // The last unaligned granule of the storage followed by unpoisoned101 // 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(buffer, 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(buffer, 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 st_beg, st_end, old_beg, old_end, beg, end);140 141 char *cur = buffer;142 for (; cur < RoundDown(st_beg); ++cur)143 assert(__asan_address_is_poisoned(cur) == poison_buffer);144 // The prefix of the first incomplete granule can switch from poisoned to145 // 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(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(st_end); ++cur)156 assert(__asan_address_is_poisoned(cur));157 // The suffix of the last incomplete granule must be poisoned the same as158 // 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 st_beg, st_end, old_beg, old_end, beg, 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 st_beg, st_end, old_beg, old_end, beg, 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 st_beg, beg, cur, st_end);195 const void *bad_address =196 __sanitizer_double_ended_contiguous_container_find_bad_address(197 st_beg, beg, cur, st_end);198 199 if (cur == end ||200 // The last unaligned granule of the storage followed by unpoisoned201 // 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 st_beg, cur, end, st_end);221 const void *bad_address =222 __sanitizer_double_ended_contiguous_container_find_bad_address(223 st_beg, cur, end, st_end);224 225 if (cur == beg ||226 // The last unaligned granule of the storage followed by unpoisoned227 // bytes looks the same.228 (!poison_buffer && RoundDown(st_end) <= std::min(cur, beg) ||229 // The first unaligned granule of non-empty container looks the230 // 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(buffer, 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(x, x + 32, x + 32, 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(x, x + 32, x + 14, 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(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(i, j, poison);279 TestDoubleEndedContainer(i, j, poison);280 }281 }282 }283 TestThrow();284}285