blob: ed20dc3e80d44e263dd5e4cb4fc9c9e9ca49b7ab [file] [log] [blame]
// 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);
}
}
}
}
}