| 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 | } |