brintos

brintos / llvm-project-archived public Read only

0
0
Text · 9.8 KiB · a2adb19 Raw
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