168d75effSDimitry Andric //=-- lsan_common_mac.cpp -------------------------------------------------===// 268d75effSDimitry Andric // 368d75effSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 468d75effSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 568d75effSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 668d75effSDimitry Andric // 768d75effSDimitry Andric //===----------------------------------------------------------------------===// 868d75effSDimitry Andric // 968d75effSDimitry Andric // This file is a part of LeakSanitizer. 1068d75effSDimitry Andric // Implementation of common leak checking functionality. Darwin-specific code. 1168d75effSDimitry Andric // 1268d75effSDimitry Andric //===----------------------------------------------------------------------===// 1368d75effSDimitry Andric 1468d75effSDimitry Andric #include "sanitizer_common/sanitizer_platform.h" 1568d75effSDimitry Andric #include "sanitizer_common/sanitizer_libc.h" 1668d75effSDimitry Andric #include "lsan_common.h" 1768d75effSDimitry Andric 1881ad6265SDimitry Andric #if CAN_SANITIZE_LEAKS && SANITIZER_APPLE 1968d75effSDimitry Andric 20*bdd1243dSDimitry Andric # include <mach/mach.h> 21*bdd1243dSDimitry Andric # include <mach/vm_statistics.h> 2268d75effSDimitry Andric # include <pthread.h> 2368d75effSDimitry Andric 24*bdd1243dSDimitry Andric # include "lsan_allocator.h" 25*bdd1243dSDimitry Andric # include "sanitizer_common/sanitizer_allocator_internal.h" 2668d75effSDimitry Andric namespace __lsan { 2768d75effSDimitry Andric 28*bdd1243dSDimitry Andric enum class SeenRegion { 29*bdd1243dSDimitry Andric None = 0, 30*bdd1243dSDimitry Andric AllocOnce = 1 << 0, 31*bdd1243dSDimitry Andric LibDispatch = 1 << 1, 32*bdd1243dSDimitry Andric Foundation = 1 << 2, 33*bdd1243dSDimitry Andric All = AllocOnce | LibDispatch | Foundation 34*bdd1243dSDimitry Andric }; 35*bdd1243dSDimitry Andric 36*bdd1243dSDimitry Andric inline SeenRegion operator|(SeenRegion left, SeenRegion right) { 37*bdd1243dSDimitry Andric return static_cast<SeenRegion>(static_cast<int>(left) | 38*bdd1243dSDimitry Andric static_cast<int>(right)); 39*bdd1243dSDimitry Andric } 40*bdd1243dSDimitry Andric 41*bdd1243dSDimitry Andric inline SeenRegion &operator|=(SeenRegion &left, const SeenRegion &right) { 42*bdd1243dSDimitry Andric left = left | right; 43*bdd1243dSDimitry Andric return left; 44*bdd1243dSDimitry Andric } 45*bdd1243dSDimitry Andric 46*bdd1243dSDimitry Andric struct RegionScanState { 47*bdd1243dSDimitry Andric SeenRegion seen_regions = SeenRegion::None; 48*bdd1243dSDimitry Andric bool in_libdispatch = false; 49*bdd1243dSDimitry Andric }; 50*bdd1243dSDimitry Andric 5168d75effSDimitry Andric typedef struct { 5268d75effSDimitry Andric int disable_counter; 5368d75effSDimitry Andric u32 current_thread_id; 5468d75effSDimitry Andric AllocatorCache cache; 5568d75effSDimitry Andric } thread_local_data_t; 5668d75effSDimitry Andric 5768d75effSDimitry Andric static pthread_key_t key; 5868d75effSDimitry Andric static pthread_once_t key_once = PTHREAD_ONCE_INIT; 5968d75effSDimitry Andric 6068d75effSDimitry Andric // The main thread destructor requires the current thread id, 6168d75effSDimitry Andric // so we can't destroy it until it's been used and reset to invalid tid 6268d75effSDimitry Andric void restore_tid_data(void *ptr) { 6368d75effSDimitry Andric thread_local_data_t *data = (thread_local_data_t *)ptr; 6468d75effSDimitry Andric if (data->current_thread_id != kInvalidTid) 6568d75effSDimitry Andric pthread_setspecific(key, data); 6668d75effSDimitry Andric } 6768d75effSDimitry Andric 6868d75effSDimitry Andric static void make_tls_key() { 6968d75effSDimitry Andric CHECK_EQ(pthread_key_create(&key, restore_tid_data), 0); 7068d75effSDimitry Andric } 7168d75effSDimitry Andric 7268d75effSDimitry Andric static thread_local_data_t *get_tls_val(bool alloc) { 7368d75effSDimitry Andric pthread_once(&key_once, make_tls_key); 7468d75effSDimitry Andric 7568d75effSDimitry Andric thread_local_data_t *ptr = (thread_local_data_t *)pthread_getspecific(key); 7668d75effSDimitry Andric if (ptr == NULL && alloc) { 7768d75effSDimitry Andric ptr = (thread_local_data_t *)InternalAlloc(sizeof(*ptr)); 7868d75effSDimitry Andric ptr->disable_counter = 0; 7968d75effSDimitry Andric ptr->current_thread_id = kInvalidTid; 8068d75effSDimitry Andric ptr->cache = AllocatorCache(); 8168d75effSDimitry Andric pthread_setspecific(key, ptr); 8268d75effSDimitry Andric } 8368d75effSDimitry Andric 8468d75effSDimitry Andric return ptr; 8568d75effSDimitry Andric } 8668d75effSDimitry Andric 8768d75effSDimitry Andric bool DisabledInThisThread() { 8868d75effSDimitry Andric thread_local_data_t *data = get_tls_val(false); 8968d75effSDimitry Andric return data ? data->disable_counter > 0 : false; 9068d75effSDimitry Andric } 9168d75effSDimitry Andric 9268d75effSDimitry Andric void DisableInThisThread() { ++get_tls_val(true)->disable_counter; } 9368d75effSDimitry Andric 9468d75effSDimitry Andric void EnableInThisThread() { 9568d75effSDimitry Andric int *disable_counter = &get_tls_val(true)->disable_counter; 9668d75effSDimitry Andric if (*disable_counter == 0) { 9768d75effSDimitry Andric DisableCounterUnderflow(); 9868d75effSDimitry Andric } 9968d75effSDimitry Andric --*disable_counter; 10068d75effSDimitry Andric } 10168d75effSDimitry Andric 10268d75effSDimitry Andric u32 GetCurrentThread() { 10368d75effSDimitry Andric thread_local_data_t *data = get_tls_val(false); 10468d75effSDimitry Andric return data ? data->current_thread_id : kInvalidTid; 10568d75effSDimitry Andric } 10668d75effSDimitry Andric 10768d75effSDimitry Andric void SetCurrentThread(u32 tid) { get_tls_val(true)->current_thread_id = tid; } 10868d75effSDimitry Andric 10968d75effSDimitry Andric AllocatorCache *GetAllocatorCache() { return &get_tls_val(true)->cache; } 11068d75effSDimitry Andric 11168d75effSDimitry Andric LoadedModule *GetLinker() { return nullptr; } 11268d75effSDimitry Andric 11368d75effSDimitry Andric // Required on Linux for initialization of TLS behavior, but should not be 11468d75effSDimitry Andric // required on Darwin. 11568d75effSDimitry Andric void InitializePlatformSpecificModules() {} 11668d75effSDimitry Andric 11768d75effSDimitry Andric // Sections which can't contain contain global pointers. This list errs on the 11868d75effSDimitry Andric // side of caution to avoid false positives, at the expense of performance. 11968d75effSDimitry Andric // 12068d75effSDimitry Andric // Other potentially safe sections include: 12168d75effSDimitry Andric // __all_image_info, __crash_info, __const, __got, __interpose, __objc_msg_break 12268d75effSDimitry Andric // 12368d75effSDimitry Andric // Sections which definitely cannot be included here are: 12468d75effSDimitry Andric // __objc_data, __objc_const, __data, __bss, __common, __thread_data, 12568d75effSDimitry Andric // __thread_bss, __thread_vars, __objc_opt_rw, __objc_opt_ptrs 12668d75effSDimitry Andric static const char *kSkippedSecNames[] = { 12768d75effSDimitry Andric "__cfstring", "__la_symbol_ptr", "__mod_init_func", 12868d75effSDimitry Andric "__mod_term_func", "__nl_symbol_ptr", "__objc_classlist", 12968d75effSDimitry Andric "__objc_classrefs", "__objc_imageinfo", "__objc_nlclslist", 13068d75effSDimitry Andric "__objc_protolist", "__objc_selrefs", "__objc_superrefs"}; 13168d75effSDimitry Andric 13268d75effSDimitry Andric // Scans global variables for heap pointers. 13368d75effSDimitry Andric void ProcessGlobalRegions(Frontier *frontier) { 13468d75effSDimitry Andric for (auto name : kSkippedSecNames) 13568d75effSDimitry Andric CHECK(internal_strnlen(name, kMaxSegName + 1) <= kMaxSegName); 13668d75effSDimitry Andric 13768d75effSDimitry Andric MemoryMappingLayout memory_mapping(false); 13868d75effSDimitry Andric InternalMmapVector<LoadedModule> modules; 13968d75effSDimitry Andric modules.reserve(128); 14068d75effSDimitry Andric memory_mapping.DumpListOfModules(&modules); 14168d75effSDimitry Andric for (uptr i = 0; i < modules.size(); ++i) { 14268d75effSDimitry Andric // Even when global scanning is disabled, we still need to scan 14368d75effSDimitry Andric // system libraries for stashed pointers 14468d75effSDimitry Andric if (!flags()->use_globals && modules[i].instrumented()) continue; 14568d75effSDimitry Andric 14668d75effSDimitry Andric for (const __sanitizer::LoadedModule::AddressRange &range : 14768d75effSDimitry Andric modules[i].ranges()) { 14868d75effSDimitry Andric // Sections storing global variables are writable and non-executable 14968d75effSDimitry Andric if (range.executable || !range.writable) continue; 15068d75effSDimitry Andric 15168d75effSDimitry Andric for (auto name : kSkippedSecNames) { 15268d75effSDimitry Andric if (!internal_strcmp(range.name, name)) continue; 15368d75effSDimitry Andric } 15468d75effSDimitry Andric 15568d75effSDimitry Andric ScanGlobalRange(range.beg, range.end, frontier); 15668d75effSDimitry Andric } 15768d75effSDimitry Andric } 15868d75effSDimitry Andric } 15968d75effSDimitry Andric 16068d75effSDimitry Andric void ProcessPlatformSpecificAllocations(Frontier *frontier) { 16168d75effSDimitry Andric vm_address_t address = 0; 16268d75effSDimitry Andric kern_return_t err = KERN_SUCCESS; 16368d75effSDimitry Andric 164349cc55cSDimitry Andric InternalMmapVectorNoCtor<RootRegion> const *root_regions = GetRootRegions(); 16568d75effSDimitry Andric 166*bdd1243dSDimitry Andric RegionScanState scan_state; 16768d75effSDimitry Andric while (err == KERN_SUCCESS) { 16804eeddc0SDimitry Andric vm_size_t size = 0; 16904eeddc0SDimitry Andric unsigned depth = 1; 17068d75effSDimitry Andric struct vm_region_submap_info_64 info; 17104eeddc0SDimitry Andric mach_msg_type_number_t count = VM_REGION_SUBMAP_INFO_COUNT_64; 17268d75effSDimitry Andric err = vm_region_recurse_64(mach_task_self(), &address, &size, &depth, 17368d75effSDimitry Andric (vm_region_info_t)&info, &count); 17468d75effSDimitry Andric 17568d75effSDimitry Andric uptr end_address = address + size; 176*bdd1243dSDimitry Andric if (info.user_tag == VM_MEMORY_OS_ALLOC_ONCE) { 17768d75effSDimitry Andric // libxpc stashes some pointers in the Kernel Alloc Once page, 17868d75effSDimitry Andric // make sure not to report those as leaks. 179*bdd1243dSDimitry Andric scan_state.seen_regions |= SeenRegion::AllocOnce; 18068d75effSDimitry Andric ScanRangeForPointers(address, end_address, frontier, "GLOBAL", 18168d75effSDimitry Andric kReachable); 182*bdd1243dSDimitry Andric } else if (info.user_tag == VM_MEMORY_FOUNDATION) { 183*bdd1243dSDimitry Andric // Objective-C block trampolines use the Foundation region. 184*bdd1243dSDimitry Andric scan_state.seen_regions |= SeenRegion::Foundation; 185*bdd1243dSDimitry Andric ScanRangeForPointers(address, end_address, frontier, "GLOBAL", 186*bdd1243dSDimitry Andric kReachable); 187*bdd1243dSDimitry Andric } else if (info.user_tag == VM_MEMORY_LIBDISPATCH) { 188*bdd1243dSDimitry Andric // Dispatch continuations use the libdispatch region. Empirically, there 189*bdd1243dSDimitry Andric // can be more than one region with this tag, so we'll optimistically 190*bdd1243dSDimitry Andric // assume that they're continguous. Otherwise, we would need to scan every 191*bdd1243dSDimitry Andric // region to ensure we find them all. 192*bdd1243dSDimitry Andric scan_state.in_libdispatch = true; 193*bdd1243dSDimitry Andric ScanRangeForPointers(address, end_address, frontier, "GLOBAL", 194*bdd1243dSDimitry Andric kReachable); 195*bdd1243dSDimitry Andric } else if (scan_state.in_libdispatch) { 196*bdd1243dSDimitry Andric scan_state.seen_regions |= SeenRegion::LibDispatch; 197*bdd1243dSDimitry Andric scan_state.in_libdispatch = false; 198*bdd1243dSDimitry Andric } 19968d75effSDimitry Andric 20068d75effSDimitry Andric // Recursing over the full memory map is very slow, break out 20168d75effSDimitry Andric // early if we don't need the full iteration. 202*bdd1243dSDimitry Andric if (scan_state.seen_regions == SeenRegion::All && 203*bdd1243dSDimitry Andric !(flags()->use_root_regions && root_regions->size() > 0)) { 20468d75effSDimitry Andric break; 20568d75effSDimitry Andric } 20668d75effSDimitry Andric 20768d75effSDimitry Andric // This additional root region scan is required on Darwin in order to 20868d75effSDimitry Andric // detect root regions contained within mmap'd memory regions, because 20968d75effSDimitry Andric // the Darwin implementation of sanitizer_procmaps traverses images 21068d75effSDimitry Andric // as loaded by dyld, and not the complete set of all memory regions. 21168d75effSDimitry Andric // 21268d75effSDimitry Andric // TODO(fjricci) - remove this once sanitizer_procmaps_mac has the same 21368d75effSDimitry Andric // behavior as sanitizer_procmaps_linux and traverses all memory regions 21468d75effSDimitry Andric if (flags()->use_root_regions) { 21568d75effSDimitry Andric for (uptr i = 0; i < root_regions->size(); i++) { 21668d75effSDimitry Andric ScanRootRegion(frontier, (*root_regions)[i], address, end_address, 21768d75effSDimitry Andric info.protection & kProtectionRead); 21868d75effSDimitry Andric } 21968d75effSDimitry Andric } 22068d75effSDimitry Andric 22168d75effSDimitry Andric address = end_address; 22268d75effSDimitry Andric } 22368d75effSDimitry Andric } 22468d75effSDimitry Andric 22568d75effSDimitry Andric // On darwin, we can intercept _exit gracefully, and return a failing exit code 22668d75effSDimitry Andric // if required at that point. Calling Die() here is undefined behavior and 22768d75effSDimitry Andric // causes rare race conditions. 22868d75effSDimitry Andric void HandleLeaks() {} 22968d75effSDimitry Andric 2305ffd83dbSDimitry Andric void LockStuffAndStopTheWorld(StopTheWorldCallback callback, 2315ffd83dbSDimitry Andric CheckForLeaksParam *argument) { 2320eae32dcSDimitry Andric ScopedStopTheWorldLock lock; 23368d75effSDimitry Andric StopTheWorld(callback, argument); 23468d75effSDimitry Andric } 23568d75effSDimitry Andric 23668d75effSDimitry Andric } // namespace __lsan 23768d75effSDimitry Andric 23881ad6265SDimitry Andric #endif // CAN_SANITIZE_LEAKS && SANITIZER_APPLE 239