forked from OSchip/llvm-project
236 lines
7.2 KiB
C++
236 lines
7.2 KiB
C++
|
//===-- asan_test_mac.cpp -------------------------------------------------===//
|
||
|
//
|
||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||
|
//
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
//
|
||
|
// This file is a part of AddressSanitizer, an address sanity checker.
|
||
|
//
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
|
||
|
#include "asan_test_utils.h"
|
||
|
|
||
|
#include "asan_mac_test.h"
|
||
|
|
||
|
#include <malloc/malloc.h>
|
||
|
#include <AvailabilityMacros.h> // For MAC_OS_X_VERSION_*
|
||
|
#include <CoreFoundation/CFString.h>
|
||
|
|
||
|
TEST(AddressSanitizerMac, CFAllocatorDefaultDoubleFree) {
|
||
|
EXPECT_DEATH(
|
||
|
CFAllocatorDefaultDoubleFree(NULL),
|
||
|
"attempting double-free");
|
||
|
}
|
||
|
|
||
|
void CFAllocator_DoubleFreeOnPthread() {
|
||
|
pthread_t child;
|
||
|
PTHREAD_CREATE(&child, NULL, CFAllocatorDefaultDoubleFree, NULL);
|
||
|
PTHREAD_JOIN(child, NULL); // Shouldn't be reached.
|
||
|
}
|
||
|
|
||
|
TEST(AddressSanitizerMac, CFAllocatorDefaultDoubleFree_ChildPhread) {
|
||
|
EXPECT_DEATH(CFAllocator_DoubleFreeOnPthread(), "attempting double-free");
|
||
|
}
|
||
|
|
||
|
namespace {
|
||
|
|
||
|
void *GLOB;
|
||
|
|
||
|
void *CFAllocatorAllocateToGlob(void *unused) {
|
||
|
GLOB = CFAllocatorAllocate(NULL, 100, /*hint*/0);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
void *CFAllocatorDeallocateFromGlob(void *unused) {
|
||
|
char *p = (char*)GLOB;
|
||
|
p[100] = 'A'; // ASan should report an error here.
|
||
|
CFAllocatorDeallocate(NULL, GLOB);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
void CFAllocator_PassMemoryToAnotherThread() {
|
||
|
pthread_t th1, th2;
|
||
|
PTHREAD_CREATE(&th1, NULL, CFAllocatorAllocateToGlob, NULL);
|
||
|
PTHREAD_JOIN(th1, NULL);
|
||
|
PTHREAD_CREATE(&th2, NULL, CFAllocatorDeallocateFromGlob, NULL);
|
||
|
PTHREAD_JOIN(th2, NULL);
|
||
|
}
|
||
|
|
||
|
TEST(AddressSanitizerMac, CFAllocator_PassMemoryToAnotherThread) {
|
||
|
EXPECT_DEATH(CFAllocator_PassMemoryToAnotherThread(),
|
||
|
"heap-buffer-overflow");
|
||
|
}
|
||
|
|
||
|
} // namespace
|
||
|
|
||
|
// TODO(glider): figure out whether we still need these tests. Is it correct
|
||
|
// to intercept the non-default CFAllocators?
|
||
|
TEST(AddressSanitizerMac, DISABLED_CFAllocatorSystemDefaultDoubleFree) {
|
||
|
EXPECT_DEATH(
|
||
|
CFAllocatorSystemDefaultDoubleFree(),
|
||
|
"attempting double-free");
|
||
|
}
|
||
|
|
||
|
// We're intercepting malloc, so kCFAllocatorMalloc is routed to ASan.
|
||
|
TEST(AddressSanitizerMac, CFAllocatorMallocDoubleFree) {
|
||
|
EXPECT_DEATH(CFAllocatorMallocDoubleFree(), "attempting double-free");
|
||
|
}
|
||
|
|
||
|
TEST(AddressSanitizerMac, DISABLED_CFAllocatorMallocZoneDoubleFree) {
|
||
|
EXPECT_DEATH(CFAllocatorMallocZoneDoubleFree(), "attempting double-free");
|
||
|
}
|
||
|
|
||
|
// For libdispatch tests below we check that ASan got to the shadow byte
|
||
|
// legend, i.e. managed to print the thread stacks (this almost certainly
|
||
|
// means that the libdispatch task creation has been intercepted correctly).
|
||
|
TEST(AddressSanitizerMac, GCDDispatchAsync) {
|
||
|
// Make sure the whole ASan report is printed, i.e. that we don't die
|
||
|
// on a CHECK.
|
||
|
EXPECT_DEATH(TestGCDDispatchAsync(), "Shadow byte legend");
|
||
|
}
|
||
|
|
||
|
TEST(AddressSanitizerMac, GCDDispatchSync) {
|
||
|
// Make sure the whole ASan report is printed, i.e. that we don't die
|
||
|
// on a CHECK.
|
||
|
EXPECT_DEATH(TestGCDDispatchSync(), "Shadow byte legend");
|
||
|
}
|
||
|
|
||
|
|
||
|
TEST(AddressSanitizerMac, GCDReuseWqthreadsAsync) {
|
||
|
// Make sure the whole ASan report is printed, i.e. that we don't die
|
||
|
// on a CHECK.
|
||
|
EXPECT_DEATH(TestGCDReuseWqthreadsAsync(), "Shadow byte legend");
|
||
|
}
|
||
|
|
||
|
TEST(AddressSanitizerMac, GCDReuseWqthreadsSync) {
|
||
|
// Make sure the whole ASan report is printed, i.e. that we don't die
|
||
|
// on a CHECK.
|
||
|
EXPECT_DEATH(TestGCDReuseWqthreadsSync(), "Shadow byte legend");
|
||
|
}
|
||
|
|
||
|
TEST(AddressSanitizerMac, GCDDispatchAfter) {
|
||
|
// Make sure the whole ASan report is printed, i.e. that we don't die
|
||
|
// on a CHECK.
|
||
|
EXPECT_DEATH(TestGCDDispatchAfter(), "Shadow byte legend");
|
||
|
}
|
||
|
|
||
|
TEST(AddressSanitizerMac, GCDSourceEvent) {
|
||
|
// Make sure the whole ASan report is printed, i.e. that we don't die
|
||
|
// on a CHECK.
|
||
|
EXPECT_DEATH(TestGCDSourceEvent(), "Shadow byte legend");
|
||
|
}
|
||
|
|
||
|
TEST(AddressSanitizerMac, GCDSourceCancel) {
|
||
|
// Make sure the whole ASan report is printed, i.e. that we don't die
|
||
|
// on a CHECK.
|
||
|
EXPECT_DEATH(TestGCDSourceCancel(), "Shadow byte legend");
|
||
|
}
|
||
|
|
||
|
TEST(AddressSanitizerMac, GCDGroupAsync) {
|
||
|
// Make sure the whole ASan report is printed, i.e. that we don't die
|
||
|
// on a CHECK.
|
||
|
EXPECT_DEATH(TestGCDGroupAsync(), "Shadow byte legend");
|
||
|
}
|
||
|
|
||
|
void *MallocIntrospectionLockWorker(void *_) {
|
||
|
const int kNumPointers = 100;
|
||
|
int i;
|
||
|
void *pointers[kNumPointers];
|
||
|
for (i = 0; i < kNumPointers; i++) {
|
||
|
pointers[i] = malloc(i + 1);
|
||
|
}
|
||
|
for (i = 0; i < kNumPointers; i++) {
|
||
|
free(pointers[i]);
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
void *MallocIntrospectionLockForker(void *_) {
|
||
|
pid_t result = fork();
|
||
|
if (result == -1) {
|
||
|
perror("fork");
|
||
|
}
|
||
|
assert(result != -1);
|
||
|
if (result == 0) {
|
||
|
// Call malloc in the child process to make sure we won't deadlock.
|
||
|
void *ptr = malloc(42);
|
||
|
free(ptr);
|
||
|
exit(0);
|
||
|
} else {
|
||
|
// Return in the parent process.
|
||
|
return NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
TEST(AddressSanitizerMac, MallocIntrospectionLock) {
|
||
|
// Incorrect implementation of force_lock and force_unlock in our malloc zone
|
||
|
// will cause forked processes to deadlock.
|
||
|
// TODO(glider): need to detect that none of the child processes deadlocked.
|
||
|
const int kNumWorkers = 5, kNumIterations = 100;
|
||
|
int i, iter;
|
||
|
for (iter = 0; iter < kNumIterations; iter++) {
|
||
|
pthread_t workers[kNumWorkers], forker;
|
||
|
for (i = 0; i < kNumWorkers; i++) {
|
||
|
PTHREAD_CREATE(&workers[i], 0, MallocIntrospectionLockWorker, 0);
|
||
|
}
|
||
|
PTHREAD_CREATE(&forker, 0, MallocIntrospectionLockForker, 0);
|
||
|
for (i = 0; i < kNumWorkers; i++) {
|
||
|
PTHREAD_JOIN(workers[i], 0);
|
||
|
}
|
||
|
PTHREAD_JOIN(forker, 0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void *TSDAllocWorker(void *test_key) {
|
||
|
if (test_key) {
|
||
|
void *mem = malloc(10);
|
||
|
pthread_setspecific(*(pthread_key_t*)test_key, mem);
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
TEST(AddressSanitizerMac, DISABLED_TSDWorkqueueTest) {
|
||
|
pthread_t th;
|
||
|
pthread_key_t test_key;
|
||
|
pthread_key_create(&test_key, CallFreeOnWorkqueue);
|
||
|
PTHREAD_CREATE(&th, NULL, TSDAllocWorker, &test_key);
|
||
|
PTHREAD_JOIN(th, NULL);
|
||
|
pthread_key_delete(test_key);
|
||
|
}
|
||
|
|
||
|
// Test that CFStringCreateCopy does not copy constant strings.
|
||
|
TEST(AddressSanitizerMac, CFStringCreateCopy) {
|
||
|
CFStringRef str = CFSTR("Hello world!\n");
|
||
|
CFStringRef str2 = CFStringCreateCopy(0, str);
|
||
|
EXPECT_EQ(str, str2);
|
||
|
}
|
||
|
|
||
|
TEST(AddressSanitizerMac, NSObjectOOB) {
|
||
|
// Make sure that our allocators are used for NSObjects.
|
||
|
EXPECT_DEATH(TestOOBNSObjects(), "heap-buffer-overflow");
|
||
|
}
|
||
|
|
||
|
// Make sure that correct pointer is passed to free() when deallocating a
|
||
|
// NSURL object.
|
||
|
// See https://github.com/google/sanitizers/issues/70.
|
||
|
TEST(AddressSanitizerMac, NSURLDeallocation) {
|
||
|
TestNSURLDeallocation();
|
||
|
}
|
||
|
|
||
|
// See https://github.com/google/sanitizers/issues/109.
|
||
|
TEST(AddressSanitizerMac, Mstats) {
|
||
|
malloc_statistics_t stats1, stats2;
|
||
|
malloc_zone_statistics(/*all zones*/NULL, &stats1);
|
||
|
const size_t kMallocSize = 100000;
|
||
|
void *alloc = Ident(malloc(kMallocSize));
|
||
|
malloc_zone_statistics(/*all zones*/NULL, &stats2);
|
||
|
EXPECT_GT(stats2.blocks_in_use, stats1.blocks_in_use);
|
||
|
EXPECT_GE(stats2.size_in_use - stats1.size_in_use, kMallocSize);
|
||
|
free(alloc);
|
||
|
// Even the default OSX allocator may not change the stats after free().
|
||
|
}
|
||
|
|