// 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 #include #include #include #include #include #include #include #include #include static constexpr size_t kGranularity = 8; template static constexpr T RoundDown(T x) { return reinterpret_cast(reinterpret_cast(x) & ~(kGranularity - 1)); } template static constexpr T RoundUp(T x) { return reinterpret_cast( RoundDown(reinterpret_cast(x) + kGranularity - 1)); } static std::vector GetPoisonedState(char *begin, char *end) { std::vector 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 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 src_poison_states = GetPoisonedState(src_buffer_beg, src_buffer_end); std::vector 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 dst_poison_after = GetPoisonedState(dst_buffer_beg, dst_buffer_end); // Create ideal copy of src over dst. std::vector 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 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 src_buffer = std::make_unique(src_buffer_size); std::unique_ptr dst_buffer = std::make_unique(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(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 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 buffer = std::make_unique(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(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(capacity, off_src, off_dst); TestOverlappingContainers(capacity, off_src, off_dst); } else { TestNonOverlappingContainers(capacity, off_src, off_dst); TestOverlappingContainers(capacity, off_src, off_dst); } } } } }