Index: compiler-rt/lib/lsan/lsan_common_mac.cpp =================================================================== --- compiler-rt/lib/lsan/lsan_common_mac.cpp +++ compiler-rt/lib/lsan/lsan_common_mac.cpp @@ -23,16 +23,33 @@ #include #include +#include // Only introduced in Mac OS X 10.9. #ifdef VM_MEMORY_OS_ALLOC_ONCE +static const int kSanitizerVmMemoryFoundation = VM_MEMORY_FOUNDATION; +static const int kSanitizerVmMemoryLibdispatch = VM_MEMORY_LIBDISPATCH; static const int kSanitizerVmMemoryOsAllocOnce = VM_MEMORY_OS_ALLOC_ONCE; #else +static const int kSanitizerVmMemoryFoundation = 41; +static const int kSanitizerVmMemoryLibdispatch = 74; static const int kSanitizerVmMemoryOsAllocOnce = 73; #endif namespace __lsan { +enum SeenRegion { + kNone = 0, + kAllocOnce = 1 << 0, + kLibDispatch = 1 << 1, + kFoundation = 1 << 2, + kAll = kAllocOnce | kLibDispatch | kFoundation +}; +struct RegionScanState { + int seen_regions = SeenRegion::kNone; + bool in_libdispatch = false; +}; + typedef struct { int disable_counter; u32 current_thread_id; @@ -148,6 +165,7 @@ InternalMmapVectorNoCtor const *root_regions = GetRootRegions(); + RegionScanState scan_state; while (err == KERN_SUCCESS) { vm_size_t size = 0; unsigned depth = 1; @@ -158,16 +176,35 @@ uptr end_address = address + size; - // libxpc stashes some pointers in the Kernel Alloc Once page, - // make sure not to report those as leaks. if (info.user_tag == kSanitizerVmMemoryOsAllocOnce) { + // libxpc stashes some pointers in the Kernel Alloc Once page, + // make sure not to report those as leaks. + scan_state.seen_regions |= kAllocOnce; + ScanRangeForPointers(address, end_address, frontier, "GLOBAL", + kReachable); + } else if (info.user_tag == kSanitizerVmMemoryFoundation) { + // Objective-C block trampolines use the Foundation region. + scan_state.seen_regions |= kFoundation; ScanRangeForPointers(address, end_address, frontier, "GLOBAL", kReachable); + } else if (info.user_tag == kSanitizerVmMemoryLibdispatch) { + // Dispatch continuations use the libdispatch region. Empirically, there + // can be more than one region with this tag, so we'll optimistically + // assume that they're continguous. Otherwise, we would need to scan every + // region to ensure we find them all. + scan_state.in_libdispatch = true; + ScanRangeForPointers(address, end_address, frontier, "GLOBAL", + kReachable); + } else if (scan_state.in_libdispatch) { + scan_state.seen_regions |= kLibDispatch; + scan_state.in_libdispatch = false; + } - // Recursing over the full memory map is very slow, break out - // early if we don't need the full iteration. - if (!flags()->use_root_regions || !root_regions->size()) - break; + // Recursing over the full memory map is very slow, break out + // early if we don't need the full iteration. + if (scan_state.seen_regions == kAll + && !(flags()->use_root_regions && root_regions->size() > 0)) { + break; } // This additional root region scan is required on Darwin in order to @@ -180,7 +217,7 @@ if (flags()->use_root_regions) { for (uptr i = 0; i < root_regions->size(); i++) { ScanRootRegion(frontier, (*root_regions)[i], address, end_address, - info.protection & kProtectionRead); + info.protection & kProtectionRead); } } Index: compiler-rt/test/lsan/TestCases/Darwin/dispatch_continuations.mm =================================================================== --- /dev/null +++ compiler-rt/test/lsan/TestCases/Darwin/dispatch_continuations.mm @@ -0,0 +1,20 @@ +// Test that dispatch continuation memory region is scanned. +// RUN: %clangxx_lsan %s -o %t -framework Foundation +// RUN: %env_lsan_opts="report_objects=1" %run %t 2>&1 && echo "" | FileCheck %s + +#include +#include + +int main() { + dispatch_queue_t fake_rl_queue = dispatch_get_global_queue(2, 0); + dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, fake_rl_queue); + dispatch_source_set_event_handler(timer, ^{}); + dispatch_source_set_timer(timer, DISPATCH_TIME_FOREVER, DISPATCH_TIME_FOREVER, 321); + dispatch_resume(timer); + __lsan_do_leak_check(); + dispatch_source_cancel(timer); + dispatch_release(timer); + return 0; +} + +// CHECK-NOT: LeakSanitizer: detected memory leaks Index: compiler-rt/test/lsan/TestCases/Darwin/trampoline.mm =================================================================== --- /dev/null +++ compiler-rt/test/lsan/TestCases/Darwin/trampoline.mm @@ -0,0 +1,20 @@ +// Test that the memory region that contains Objective-C block trampolines +// is scanned. +// FIXME: Find a way to reduce this without AppKit to remove Mac requirement. +// UNSUPPORTED: ios +// RUN: %clangxx_lsan %s -o %t -framework Cocoa -fno-objc-arc +// RUN: %env_lsan_opts="report_objects=1" %run %t 2>&1 && echo "" | FileCheck %s + +#import + + +#include + + +int main() { + NSView* view = [[[NSView alloc] + initWithFrame:CGRectMake(0, 0, 20, 20)] autorelease]; + __lsan_do_leak_check(); + return 0; +} +// CHECK-NOT: LeakSanitizer: detected memory leaks