diff options
Diffstat (limited to 'lib/asan/tests/asan_interface_test.cc')
-rw-r--r-- | lib/asan/tests/asan_interface_test.cc | 334 |
1 files changed, 334 insertions, 0 deletions
diff --git a/lib/asan/tests/asan_interface_test.cc b/lib/asan/tests/asan_interface_test.cc new file mode 100644 index 000000000000..c26ed92468b3 --- /dev/null +++ b/lib/asan/tests/asan_interface_test.cc @@ -0,0 +1,334 @@ +//===-- asan_interface_test.cc ------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +//===----------------------------------------------------------------------===// +#include <pthread.h> +#include <stdio.h> +#include <string.h> + +#include "asan_test_config.h" +#include "asan_test_utils.h" +#include "asan_interface.h" + +TEST(AddressSanitizerInterface, GetEstimatedAllocatedSize) { + EXPECT_EQ(1, __asan_get_estimated_allocated_size(0)); + const size_t sizes[] = { 1, 30, 1<<30 }; + for (size_t i = 0; i < 3; i++) { + EXPECT_EQ(sizes[i], __asan_get_estimated_allocated_size(sizes[i])); + } +} + +static const char* kGetAllocatedSizeErrorMsg = + "__asan_get_allocated_size failed"; + +TEST(AddressSanitizerInterface, GetAllocatedSizeAndOwnershipTest) { + const size_t kArraySize = 100; + char *array = Ident((char*)malloc(kArraySize)); + int *int_ptr = Ident(new int); + + // Allocated memory is owned by allocator. Allocated size should be + // equal to requested size. + EXPECT_EQ(true, __asan_get_ownership(array)); + EXPECT_EQ(kArraySize, __asan_get_allocated_size(array)); + EXPECT_EQ(true, __asan_get_ownership(int_ptr)); + EXPECT_EQ(sizeof(int), __asan_get_allocated_size(int_ptr)); + + // We cannot call GetAllocatedSize from the memory we didn't map, + // and from the interior pointers (not returned by previous malloc). + void *wild_addr = (void*)0x1; + EXPECT_EQ(false, __asan_get_ownership(wild_addr)); + EXPECT_DEATH(__asan_get_allocated_size(wild_addr), kGetAllocatedSizeErrorMsg); + EXPECT_EQ(false, __asan_get_ownership(array + kArraySize / 2)); + EXPECT_DEATH(__asan_get_allocated_size(array + kArraySize / 2), + kGetAllocatedSizeErrorMsg); + + // NULL is a valid argument and is owned. + EXPECT_EQ(true, __asan_get_ownership(NULL)); + EXPECT_EQ(0, __asan_get_allocated_size(NULL)); + + // When memory is freed, it's not owned, and call to GetAllocatedSize + // is forbidden. + free(array); + EXPECT_EQ(false, __asan_get_ownership(array)); + EXPECT_DEATH(__asan_get_allocated_size(array), kGetAllocatedSizeErrorMsg); + + delete int_ptr; +} + +TEST(AddressSanitizerInterface, GetCurrentAllocatedBytesTest) { + size_t before_malloc, after_malloc, after_free; + char *array; + const size_t kMallocSize = 100; + before_malloc = __asan_get_current_allocated_bytes(); + + array = Ident((char*)malloc(kMallocSize)); + after_malloc = __asan_get_current_allocated_bytes(); + EXPECT_EQ(before_malloc + kMallocSize, after_malloc); + + free(array); + after_free = __asan_get_current_allocated_bytes(); + EXPECT_EQ(before_malloc, after_free); +} + +static void DoDoubleFree() { + int *x = Ident(new int); + delete Ident(x); + delete Ident(x); +} + +// This test is run in a separate process, so that large malloced +// chunk won't remain in the free lists after the test. +// Note: use ASSERT_* instead of EXPECT_* here. +static void RunGetHeapSizeTestAndDie() { + size_t old_heap_size, new_heap_size, heap_growth; + // We unlikely have have chunk of this size in free list. + static const size_t kLargeMallocSize = 1 << 29; // 512M + old_heap_size = __asan_get_heap_size(); + fprintf(stderr, "allocating %zu bytes:\n", kLargeMallocSize); + free(Ident(malloc(kLargeMallocSize))); + new_heap_size = __asan_get_heap_size(); + heap_growth = new_heap_size - old_heap_size; + fprintf(stderr, "heap growth after first malloc: %zu\n", heap_growth); + ASSERT_GE(heap_growth, kLargeMallocSize); + ASSERT_LE(heap_growth, 2 * kLargeMallocSize); + + // Now large chunk should fall into free list, and can be + // allocated without increasing heap size. + old_heap_size = new_heap_size; + free(Ident(malloc(kLargeMallocSize))); + heap_growth = __asan_get_heap_size() - old_heap_size; + fprintf(stderr, "heap growth after second malloc: %zu\n", heap_growth); + ASSERT_LT(heap_growth, kLargeMallocSize); + + // Test passed. Now die with expected double-free. + DoDoubleFree(); +} + +TEST(AddressSanitizerInterface, GetHeapSizeTest) { + EXPECT_DEATH(RunGetHeapSizeTestAndDie(), "double-free"); +} + +// Note: use ASSERT_* instead of EXPECT_* here. +static void DoLargeMallocForGetFreeBytesTestAndDie() { + size_t old_free_bytes, new_free_bytes; + static const size_t kLargeMallocSize = 1 << 29; // 512M + // If we malloc and free a large memory chunk, it will not fall + // into quarantine and will be available for future requests. + old_free_bytes = __asan_get_free_bytes(); + fprintf(stderr, "allocating %zu bytes:\n", kLargeMallocSize); + fprintf(stderr, "free bytes before malloc: %zu\n", old_free_bytes); + free(Ident(malloc(kLargeMallocSize))); + new_free_bytes = __asan_get_free_bytes(); + fprintf(stderr, "free bytes after malloc and free: %zu\n", new_free_bytes); + ASSERT_GE(new_free_bytes, old_free_bytes + kLargeMallocSize); + // Test passed. + DoDoubleFree(); +} + +TEST(AddressSanitizerInterface, GetFreeBytesTest) { + static const size_t kNumOfChunks = 100; + static const size_t kChunkSize = 100; + char *chunks[kNumOfChunks]; + size_t i; + size_t old_free_bytes, new_free_bytes; + // Allocate a small chunk. Now allocator probably has a lot of these + // chunks to fulfill future requests. So, future requests will decrease + // the number of free bytes. + chunks[0] = Ident((char*)malloc(kChunkSize)); + old_free_bytes = __asan_get_free_bytes(); + for (i = 1; i < kNumOfChunks; i++) { + chunks[i] = Ident((char*)malloc(kChunkSize)); + new_free_bytes = __asan_get_free_bytes(); + EXPECT_LT(new_free_bytes, old_free_bytes); + old_free_bytes = new_free_bytes; + } + // Deleting these chunks will move them to quarantine, number of free + // bytes won't increase. + for (i = 0; i < kNumOfChunks; i++) { + free(chunks[i]); + EXPECT_EQ(old_free_bytes, __asan_get_free_bytes()); + } + EXPECT_DEATH(DoLargeMallocForGetFreeBytesTestAndDie(), "double-free"); +} + +static const size_t kManyThreadsMallocSizes[] = {5, 1UL<<10, 1UL<<20, 357}; +static const size_t kManyThreadsIterations = 250; +static const size_t kManyThreadsNumThreads = 200; + +void *ManyThreadsWithStatsWorker(void *arg) { + for (size_t iter = 0; iter < kManyThreadsIterations; iter++) { + for (size_t size_index = 0; size_index < 4; size_index++) { + free(Ident(malloc(kManyThreadsMallocSizes[size_index]))); + } + } + return 0; +} + +TEST(AddressSanitizerInterface, ManyThreadsWithStatsStressTest) { + size_t before_test, after_test, i; + pthread_t threads[kManyThreadsNumThreads]; + before_test = __asan_get_current_allocated_bytes(); + for (i = 0; i < kManyThreadsNumThreads; i++) { + pthread_create(&threads[i], 0, + (void* (*)(void *x))ManyThreadsWithStatsWorker, (void*)i); + } + for (i = 0; i < kManyThreadsNumThreads; i++) { + pthread_join(threads[i], 0); + } + after_test = __asan_get_current_allocated_bytes(); + // ASan stats also reflect memory usage of internal ASan RTL structs, + // so we can't check for equality here. + EXPECT_LT(after_test, before_test + (1UL<<20)); +} + +TEST(AddressSanitizerInterface, ExitCode) { + int original_exit_code = __asan_set_error_exit_code(7); + EXPECT_EXIT(DoDoubleFree(), ::testing::ExitedWithCode(7), ""); + EXPECT_EQ(7, __asan_set_error_exit_code(8)); + EXPECT_EXIT(DoDoubleFree(), ::testing::ExitedWithCode(8), ""); + EXPECT_EQ(8, __asan_set_error_exit_code(original_exit_code)); + EXPECT_EXIT(DoDoubleFree(), + ::testing::ExitedWithCode(original_exit_code), ""); +} + +static const char* kUseAfterPoisonErrorMessage = "use-after-poison"; + +#define ACCESS(ptr, offset) Ident(*(ptr + offset)) + +#define DIE_ON_ACCESS(ptr, offset) \ + EXPECT_DEATH(Ident(*(ptr + offset)), kUseAfterPoisonErrorMessage) + +TEST(AddressSanitizerInterface, SimplePoisonMemoryRegionTest) { + char *array = Ident((char*)malloc(120)); + // poison array[40..80) + ASAN_POISON_MEMORY_REGION(array + 40, 40); + ACCESS(array, 39); + ACCESS(array, 80); + DIE_ON_ACCESS(array, 40); + DIE_ON_ACCESS(array, 60); + DIE_ON_ACCESS(array, 79); + ASAN_UNPOISON_MEMORY_REGION(array + 40, 40); + // access previously poisoned memory. + ACCESS(array, 40); + ACCESS(array, 79); + free(array); +} + +TEST(AddressSanitizerInterface, OverlappingPoisonMemoryRegionTest) { + char *array = Ident((char*)malloc(120)); + // Poison [0..40) and [80..120) + ASAN_POISON_MEMORY_REGION(array, 40); + ASAN_POISON_MEMORY_REGION(array + 80, 40); + DIE_ON_ACCESS(array, 20); + ACCESS(array, 60); + DIE_ON_ACCESS(array, 100); + // Poison whole array - [0..120) + ASAN_POISON_MEMORY_REGION(array, 120); + DIE_ON_ACCESS(array, 60); + // Unpoison [24..96) + ASAN_UNPOISON_MEMORY_REGION(array + 24, 72); + DIE_ON_ACCESS(array, 23); + ACCESS(array, 24); + ACCESS(array, 60); + ACCESS(array, 95); + DIE_ON_ACCESS(array, 96); + free(array); +} + +TEST(AddressSanitizerInterface, PushAndPopWithPoisoningTest) { + // Vector of capacity 20 + char *vec = Ident((char*)malloc(20)); + ASAN_POISON_MEMORY_REGION(vec, 20); + for (size_t i = 0; i < 7; i++) { + // Simulate push_back. + ASAN_UNPOISON_MEMORY_REGION(vec + i, 1); + ACCESS(vec, i); + DIE_ON_ACCESS(vec, i + 1); + } + for (size_t i = 7; i > 0; i--) { + // Simulate pop_back. + ASAN_POISON_MEMORY_REGION(vec + i - 1, 1); + DIE_ON_ACCESS(vec, i - 1); + if (i > 1) ACCESS(vec, i - 2); + } + free(vec); +} + +// Make sure that each aligned block of size "2^granularity" doesn't have +// "true" value before "false" value. +static void MakeShadowValid(bool *shadow, int length, int granularity) { + bool can_be_poisoned = true; + for (int i = length - 1; i >= 0; i--) { + can_be_poisoned &= shadow[i]; + shadow[i] &= can_be_poisoned; + if (i % (1 << granularity) == 0) { + can_be_poisoned = true; + } + } +} + +TEST(AddressSanitizerInterface, PoisoningStressTest) { + const size_t kSize = 24; + bool expected[kSize]; + char *arr = Ident((char*)malloc(kSize)); + for (size_t l1 = 0; l1 < kSize; l1++) { + for (size_t s1 = 1; l1 + s1 <= kSize; s1++) { + for (size_t l2 = 0; l2 < kSize; l2++) { + for (size_t s2 = 1; l2 + s2 <= kSize; s2++) { + // Poison [l1, l1+s1), [l2, l2+s2) and check result. + ASAN_UNPOISON_MEMORY_REGION(arr, kSize); + ASAN_POISON_MEMORY_REGION(arr + l1, s1); + ASAN_POISON_MEMORY_REGION(arr + l2, s2); + memset(expected, false, kSize); + memset(expected + l1, true, s1); + MakeShadowValid(expected, 24, /*granularity*/ 3); + memset(expected + l2, true, s2); + MakeShadowValid(expected, 24, /*granularity*/ 3); + for (size_t i = 0; i < kSize; i++) { + ASSERT_EQ(expected[i], __asan_address_is_poisoned(arr + i)); + } + // Unpoison [l1, l1+s1) and [l2, l2+s2) and check result. + ASAN_POISON_MEMORY_REGION(arr, kSize); + ASAN_UNPOISON_MEMORY_REGION(arr + l1, s1); + ASAN_UNPOISON_MEMORY_REGION(arr + l2, s2); + memset(expected, true, kSize); + memset(expected + l1, false, s1); + MakeShadowValid(expected, 24, /*granularity*/ 3); + memset(expected + l2, false, s2); + MakeShadowValid(expected, 24, /*granularity*/ 3); + for (size_t i = 0; i < kSize; i++) { + ASSERT_EQ(expected[i], __asan_address_is_poisoned(arr + i)); + } + } + } + } + } +} + +static const char *kInvalidPoisonMessage = "invalid-poison-memory-range"; +static const char *kInvalidUnpoisonMessage = "invalid-unpoison-memory-range"; + +TEST(AddressSanitizerInterface, DISABLED_InvalidPoisonAndUnpoisonCallsTest) { + char *array = Ident((char*)malloc(120)); + ASAN_UNPOISON_MEMORY_REGION(array, 120); + // Try to unpoison not owned memory + EXPECT_DEATH(ASAN_UNPOISON_MEMORY_REGION(array, 121), + kInvalidUnpoisonMessage); + EXPECT_DEATH(ASAN_UNPOISON_MEMORY_REGION(array - 1, 120), + kInvalidUnpoisonMessage); + + ASAN_POISON_MEMORY_REGION(array, 120); + // Try to poison not owned memory. + EXPECT_DEATH(ASAN_POISON_MEMORY_REGION(array, 121), kInvalidPoisonMessage); + EXPECT_DEATH(ASAN_POISON_MEMORY_REGION(array - 1, 120), + kInvalidPoisonMessage); + free(array); +} |