Index: lib/lsan/lsan_common.h =================================================================== --- lib/lsan/lsan_common.h +++ lib/lsan/lsan_common.h @@ -118,6 +118,17 @@ void InitializePlatformSpecificModules(); void ProcessGlobalRegions(Frontier *frontier); void ProcessPlatformSpecificAllocations(Frontier *frontier); +void ProcessPlatformSpecificRootRegion(Frontier *frontier, uptr root_begin, + uptr root_end); + +struct RootRegion { + uptr begin; + uptr size; +}; + +InternalMmapVector const *GetRootRegions(); +void ScanRootRegion(Frontier *frontier, RootRegion const ®ion, + uptr region_begin, uptr region_end, uptr prot); // Run stoptheworld while holding any platform-specific locks. void DoStopTheWorld(StopTheWorldCallback callback, void* argument); Index: lib/lsan/lsan_common.cc =================================================================== --- lib/lsan/lsan_common.cc +++ lib/lsan/lsan_common.cc @@ -83,13 +83,10 @@ return suppression_ctx; } -struct RootRegion { - const void *begin; - uptr size; -}; - InternalMmapVector *root_regions; +InternalMmapVector const *GetRootRegions() { return root_regions; } + void InitializeRootRegions() { CHECK(!root_regions); ALIGNED(64) static char placeholder[sizeof(InternalMmapVector)]; @@ -291,23 +288,29 @@ } } -static void ProcessRootRegion(Frontier *frontier, uptr root_begin, - uptr root_end) { - MemoryMappingLayout proc_maps(/*cache_enabled*/true); +void ScanRootRegion(Frontier *frontier, RootRegion const &root_region, + uptr region_begin, uptr region_end, uptr prot) { + uptr intersection_begin = Max(root_region.begin, region_begin); + uptr intersection_end = Min(region_end, root_region.begin + root_region.size); + if (intersection_begin >= intersection_end) return; + bool is_readable = prot & MemoryMappingLayout::kProtectionRead; + LOG_POINTERS("Root region %p-%p intersects with mapped region %p-%p (%s)\n", + root_region.begin, root_region.begin + root_region.size, + region_begin, region_end, + is_readable ? "readable" : "unreadable"); + if (is_readable) + ScanRangeForPointers(intersection_begin, intersection_end, frontier, "ROOT", + kReachable); +} + +static void ProcessRootRegion(Frontier *frontier, + RootRegion const &root_region) { + MemoryMappingLayout proc_maps(/*cache_enabled*/ true); uptr begin, end, prot; while (proc_maps.Next(&begin, &end, /*offset*/ nullptr, /*filename*/ nullptr, /*filename_size*/ 0, &prot)) { - uptr intersection_begin = Max(root_begin, begin); - uptr intersection_end = Min(end, root_end); - if (intersection_begin >= intersection_end) continue; - bool is_readable = prot & MemoryMappingLayout::kProtectionRead; - LOG_POINTERS("Root region %p-%p intersects with mapped region %p-%p (%s)\n", - root_begin, root_end, begin, end, - is_readable ? "readable" : "unreadable"); - if (is_readable) - ScanRangeForPointers(intersection_begin, intersection_end, frontier, - "ROOT", kReachable); + ScanRootRegion(frontier, root_region, begin, end, prot); } } @@ -316,9 +319,7 @@ if (!flags()->use_root_regions) return; CHECK(root_regions); for (uptr i = 0; i < root_regions->size(); i++) { - RootRegion region = (*root_regions)[i]; - uptr begin_addr = reinterpret_cast(region.begin); - ProcessRootRegion(frontier, begin_addr, begin_addr + region.size); + ProcessRootRegion(frontier, (*root_regions)[i]); } } @@ -775,7 +776,7 @@ #if CAN_SANITIZE_LEAKS BlockingMutexLock l(&global_mutex); CHECK(root_regions); - RootRegion region = {begin, size}; + RootRegion region = {reinterpret_cast(begin), size}; root_regions->push_back(region); VReport(1, "Registered root region at %p of size %llu\n", begin, size); #endif // CAN_SANITIZE_LEAKS @@ -789,7 +790,7 @@ bool removed = false; for (uptr i = 0; i < root_regions->size(); i++) { RootRegion region = (*root_regions)[i]; - if (region.begin == begin && region.size == size) { + if (region.begin == reinterpret_cast(begin) && region.size == size) { removed = true; uptr last_index = root_regions->size() - 1; (*root_regions)[i] = (*root_regions)[last_index]; Index: lib/lsan/lsan_common_linux.cc =================================================================== --- lib/lsan/lsan_common_linux.cc +++ lib/lsan/lsan_common_linux.cc @@ -91,6 +91,9 @@ LoadedModule *GetLinker() { return linker; } +void ProcessPlatformSpecificRootRegion(Frontier *frontier, uptr root_begin, + uptr root_end) {} + void ProcessPlatformSpecificAllocations(Frontier *frontier) {} struct DoStopTheWorldParam { Index: lib/lsan/lsan_common_mac.cc =================================================================== --- lib/lsan/lsan_common_mac.cc +++ lib/lsan/lsan_common_mac.cc @@ -117,8 +117,6 @@ } } -// libxpc stashes some pointers in the Kernel Alloc Once page, -// make sure not to report those as leaks. void ProcessPlatformSpecificAllocations(Frontier *frontier) { mach_port_name_t port; if (task_for_pid(mach_task_self(), internal_getpid(), &port) @@ -132,16 +130,32 @@ kern_return_t err = KERN_SUCCESS; mach_msg_type_number_t count = VM_REGION_SUBMAP_INFO_COUNT_64; + InternalMmapVector const *root_regions = GetRootRegions(); + while (err == KERN_SUCCESS) { struct vm_region_submap_info_64 info; err = vm_region_recurse_64(port, &address, &size, &depth, (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 == VM_MEMORY_OS_ALLOC_ONCE) { - ScanRangeForPointers(address, address + size, frontier, - "GLOBAL", kReachable); - return; + ScanRangeForPointers(address, end_address, frontier, "GLOBAL", + kReachable); } - address += size; + + // This additional root region scan is required on Darwin in order to + // detect root regions contained within mmap'd memory regions + if (flags()->use_root_regions) { + for (uptr i = 0; i < root_regions->size(); i++) { + ScanRootRegion(frontier, (*root_regions)[i], address, end_address, + info.protection); + } + } + + address = end_address; } }