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 @@ -17,21 +17,36 @@ #if CAN_SANITIZE_LEAKS && SANITIZER_APPLE -#include "sanitizer_common/sanitizer_allocator_internal.h" -#include "lsan_allocator.h" - +#include +#include #include -#include +#include "lsan_allocator.h" +#include "sanitizer_common/sanitizer_allocator_internal.h" +namespace __lsan { + +enum class SeenRegion { + None = 0, + AllocOnce = 1 << 0, + LibDispatch = 1 << 1, + Foundation = 1 << 2, + All = AllocOnce | LibDispatch | Foundation +}; + +inline SeenRegion operator|(SeenRegion left, SeenRegion right) { + return static_cast(static_cast(left) | + static_cast(right)); +} -// Only introduced in Mac OS X 10.9. -#ifdef VM_MEMORY_OS_ALLOC_ONCE -static const int kSanitizerVmMemoryOsAllocOnce = VM_MEMORY_OS_ALLOC_ONCE; -#else -static const int kSanitizerVmMemoryOsAllocOnce = 73; -#endif +inline SeenRegion &operator|=(SeenRegion &left, const SeenRegion &right) { + left = left | right; + return left; +} -namespace __lsan { +struct RegionScanState { + SeenRegion seen_regions = SeenRegion::None; + bool in_libdispatch = false; +}; typedef struct { int disable_counter; @@ -148,6 +163,7 @@ InternalMmapVectorNoCtor const *root_regions = GetRootRegions(); + RegionScanState scan_state; while (err == KERN_SUCCESS) { vm_size_t size = 0; unsigned depth = 1; @@ -157,17 +173,35 @@ (vm_region_info_t)&info, &count); 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) { + if (info.user_tag == VM_MEMORY_OS_ALLOC_ONCE) { + // libxpc stashes some pointers in the Kernel Alloc Once page, + // make sure not to report those as leaks. + scan_state.seen_regions |= SeenRegion::AllocOnce; ScanRangeForPointers(address, end_address, frontier, "GLOBAL", kReachable); + } else if (info.user_tag == VM_MEMORY_FOUNDATION) { + // Objective-C block trampolines use the Foundation region. + scan_state.seen_regions |= SeenRegion::Foundation; + ScanRangeForPointers(address, end_address, frontier, "GLOBAL", + kReachable); + } else if (info.user_tag == VM_MEMORY_LIBDISPATCH) { + // 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 |= SeenRegion::LibDispatch; + 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 == SeenRegion::All && + !(flags()->use_root_regions && root_regions->size() > 0)) { + break; } // This additional root region scan is required on Darwin in order to Index: compiler-rt/test/lsan/TestCases/Darwin/dispatch_continuations.mm =================================================================== --- /dev/null +++ compiler-rt/test/lsan/TestCases/Darwin/dispatch_continuations.mm @@ -0,0 +1,24 @@ +// 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() { + // Reduced from `CFRunLoopCreate` + 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,18 @@ +// 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