|  | // RUN: %clangxx_asan -fexceptions -O %s -o %t && %env_asan_opts=detect_stack_use_after_return=0 %run %t | 
|  | // | 
|  | // Test __sanitizer_copy_contiguous_container_annotations. | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <iostream> | 
|  | #include <memory> | 
|  | #include <numeric> | 
|  | #include <vector> | 
|  |  | 
|  | #include <assert.h> | 
|  | #include <sanitizer/asan_interface.h> | 
|  | #include <stdio.h> | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  |  | 
|  | static constexpr size_t kGranularity = 8; | 
|  |  | 
|  | template <class T> static constexpr T RoundDown(T x) { | 
|  | return reinterpret_cast<T>(reinterpret_cast<uintptr_t>(x) & | 
|  | ~(kGranularity - 1)); | 
|  | } | 
|  | template <class T> static constexpr T RoundUp(T x) { | 
|  | return reinterpret_cast<T>( | 
|  | RoundDown(reinterpret_cast<uintptr_t>(x) + kGranularity - 1)); | 
|  | } | 
|  |  | 
|  | static std::vector<int> GetPoisonedState(char *begin, char *end) { | 
|  | std::vector<int> result; | 
|  | for (char *ptr = begin; ptr != end; ++ptr) { | 
|  | result.push_back(__asan_address_is_poisoned(ptr)); | 
|  | } | 
|  | return result; | 
|  | } | 
|  |  | 
|  | static void RandomPoison(char *beg, char *end) { | 
|  | assert(beg == RoundDown(beg)); | 
|  | assert(end == RoundDown(end)); | 
|  | __asan_poison_memory_region(beg, end - beg); | 
|  | for (beg = RoundUp(beg); beg < end; beg += kGranularity) { | 
|  | __asan_unpoison_memory_region(beg, rand() % (kGranularity + 1)); | 
|  | } | 
|  | } | 
|  |  | 
|  | template <bool benchmark> | 
|  | static void Test(size_t capacity, size_t off_src, size_t off_dst, | 
|  | char *src_buffer_beg, char *src_buffer_end, | 
|  | char *dst_buffer_beg, char *dst_buffer_end) { | 
|  | size_t dst_buffer_size = dst_buffer_end - dst_buffer_beg; | 
|  | char *src_beg = src_buffer_beg + off_src; | 
|  | char *src_end = src_beg + capacity; | 
|  |  | 
|  | char *dst_beg = dst_buffer_beg + off_dst; | 
|  | char *dst_end = dst_beg + capacity; | 
|  | if (benchmark) { | 
|  | __sanitizer_copy_contiguous_container_annotations(src_beg, src_end, dst_beg, | 
|  | dst_end); | 
|  | return; | 
|  | } | 
|  |  | 
|  | std::vector<int> src_poison_states = | 
|  | GetPoisonedState(src_buffer_beg, src_buffer_end); | 
|  | std::vector<int> dst_poison_before = | 
|  | GetPoisonedState(dst_buffer_beg, dst_buffer_end); | 
|  | __sanitizer_copy_contiguous_container_annotations(src_beg, src_end, dst_beg, | 
|  | dst_end); | 
|  | std::vector<int> dst_poison_after = | 
|  | GetPoisonedState(dst_buffer_beg, dst_buffer_end); | 
|  |  | 
|  | // Create ideal copy of src over dst. | 
|  | std::vector<int> dst_poison_exp = dst_poison_before; | 
|  | for (size_t cur = 0; cur < capacity; ++cur) | 
|  | dst_poison_exp[off_dst + cur] = src_poison_states[off_src + cur]; | 
|  |  | 
|  | // Unpoison prefixes of Asan granules. | 
|  | for (size_t cur = dst_buffer_size - 1; cur > 0; --cur) { | 
|  | if (cur % kGranularity != 0 && !dst_poison_exp[cur]) | 
|  | dst_poison_exp[cur - 1] = 0; | 
|  | } | 
|  |  | 
|  | if (dst_poison_after != dst_poison_exp) { | 
|  | std::cerr << "[" << off_dst << ", " << off_dst + capacity << ")\n"; | 
|  | for (size_t i = 0; i < dst_poison_after.size(); ++i) { | 
|  | std::cerr << i << ":\t" << dst_poison_before[i] << "\t" | 
|  | << dst_poison_after[i] << "\t" << dst_poison_exp[i] << "\n"; | 
|  | } | 
|  | std::cerr << "----------\n"; | 
|  |  | 
|  | assert(dst_poison_after == dst_poison_exp); | 
|  | } | 
|  | } | 
|  |  | 
|  | template <bool benchmark> | 
|  | static void TestNonOverlappingContainers(size_t capacity, size_t off_src, | 
|  | size_t off_dst) { | 
|  | // Test will copy [off_src, off_src + capacity) to [off_dst, off_dst + capacity). | 
|  | // Allocate buffers to have additional granule before and after tested ranges. | 
|  | off_src += kGranularity; | 
|  | off_dst += kGranularity; | 
|  | size_t src_buffer_size = RoundUp(off_src + capacity) + kGranularity; | 
|  | size_t dst_buffer_size = RoundUp(off_dst + capacity) + kGranularity; | 
|  |  | 
|  | std::unique_ptr<char[]> src_buffer = | 
|  | std::make_unique<char[]>(src_buffer_size); | 
|  | std::unique_ptr<char[]> dst_buffer = | 
|  | std::make_unique<char[]>(dst_buffer_size); | 
|  |  | 
|  | char *src_buffer_beg = src_buffer.get(); | 
|  | char *src_buffer_end = src_buffer_beg + src_buffer_size; | 
|  | assert(RoundDown(src_buffer_beg) == src_buffer_beg); | 
|  |  | 
|  | char *dst_buffer_beg = dst_buffer.get(); | 
|  | char *dst_buffer_end = dst_buffer_beg + dst_buffer_size; | 
|  | assert(RoundDown(dst_buffer_beg) == dst_buffer_beg); | 
|  |  | 
|  | for (int i = 0; i < 35; i++) { | 
|  | if (!benchmark || !i) { | 
|  | RandomPoison(src_buffer_beg, src_buffer_end); | 
|  | RandomPoison(dst_buffer_beg, dst_buffer_end); | 
|  | } | 
|  |  | 
|  | Test<benchmark>(capacity, off_src, off_dst, src_buffer_beg, src_buffer_end, | 
|  | dst_buffer_beg, dst_buffer_end); | 
|  | } | 
|  |  | 
|  | __asan_unpoison_memory_region(src_buffer_beg, src_buffer_size); | 
|  | __asan_unpoison_memory_region(dst_buffer_beg, dst_buffer_size); | 
|  | } | 
|  |  | 
|  | template <bool benchmark> | 
|  | static void TestOverlappingContainers(size_t capacity, size_t off_src, | 
|  | size_t off_dst) { | 
|  | // Test will copy [off_src, off_src + capacity) to [off_dst, off_dst + capacity). | 
|  | // Allocate buffers to have additional granule before and after tested ranges. | 
|  | off_src += kGranularity; | 
|  | off_dst += kGranularity; | 
|  | size_t buffer_size = | 
|  | RoundUp(std::max(off_src, off_dst) + capacity) + kGranularity; | 
|  |  | 
|  | // Use unique_ptr with a custom deleter to manage the buffer | 
|  | std::unique_ptr<char[]> buffer = std::make_unique<char[]>(buffer_size); | 
|  |  | 
|  | char *buffer_beg = buffer.get(); | 
|  | char *buffer_end = buffer_beg + buffer_size; | 
|  | assert(RoundDown(buffer_beg) == buffer_beg); | 
|  |  | 
|  | for (int i = 0; i < 35; i++) { | 
|  | if (!benchmark || !i) | 
|  | RandomPoison(buffer_beg, buffer_end); | 
|  | Test<benchmark>(capacity, off_src, off_dst, buffer_beg, buffer_end, | 
|  | buffer_beg, buffer_end); | 
|  | } | 
|  |  | 
|  | __asan_unpoison_memory_region(buffer_beg, buffer_size); | 
|  | } | 
|  |  | 
|  | int main(int argc, char **argv) { | 
|  | int n = argc == 1 ? 64 : atoi(argv[1]); | 
|  | for (size_t off_src = 0; off_src < kGranularity; off_src++) { | 
|  | for (size_t off_dst = 0; off_dst < kGranularity; off_dst++) { | 
|  | for (int capacity = 0; capacity <= n; capacity++) { | 
|  | if (n < 1024) { | 
|  | TestNonOverlappingContainers<false>(capacity, off_src, off_dst); | 
|  | TestOverlappingContainers<false>(capacity, off_src, off_dst); | 
|  | } else { | 
|  | TestNonOverlappingContainers<true>(capacity, off_src, off_dst); | 
|  | TestOverlappingContainers<true>(capacity, off_src, off_dst); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } |