blob: bd36089991debba79728d784c6261540924a076d [file] [log] [blame]
//===-- 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().
}