diff --git a/libsanitizer/sanitizer_common/sanitizer_mac.cpp b/libsanitizer/sanitizer_common/sanitizer_mac.cpp index a6f757173728..ea8b3c7e522d 100644 --- a/libsanitizer/sanitizer_common/sanitizer_mac.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_mac.cpp @@ -71,7 +71,15 @@ extern char ***_NSGetArgv(void); # include # include # include -# include +# if defined(__has_builtin) && __has_builtin(__builtin_os_log_format) +# include +# else + /* Without support for __builtin_os_log_format, fall back to the older + method. */ +# define OS_LOG_DEFAULT 0 +# define os_log_error(A,B,C) \ + asl_log(nullptr, nullptr, ASL_LEVEL_ERR, "%s", (C)) +# endif # include # include # include @@ -869,19 +877,23 @@ void LogFullErrorReport(const char *buffer) { // When logging with os_log_error this will make it into the crash log. if (internal_strncmp(SanitizerToolName, "AddressSanitizer", sizeof("AddressSanitizer") - 1) == 0) - SANITIZER_OS_LOG(OS_LOG_DEFAULT, "Address Sanitizer reported a failure."); + SANITIZER_OS_LOG(OS_LOG_DEFAULT, "%{public}s", + "Address Sanitizer reported a failure."); else if (internal_strncmp(SanitizerToolName, "UndefinedBehaviorSanitizer", sizeof("UndefinedBehaviorSanitizer") - 1) == 0) - SANITIZER_OS_LOG(OS_LOG_DEFAULT, + SANITIZER_OS_LOG(OS_LOG_DEFAULT, "%{public}s", "Undefined Behavior Sanitizer reported a failure."); else if (internal_strncmp(SanitizerToolName, "ThreadSanitizer", sizeof("ThreadSanitizer") - 1) == 0) - SANITIZER_OS_LOG(OS_LOG_DEFAULT, "Thread Sanitizer reported a failure."); + SANITIZER_OS_LOG(OS_LOG_DEFAULT, "%{public}s", + "Thread Sanitizer reported a failure."); else - SANITIZER_OS_LOG(OS_LOG_DEFAULT, "Sanitizer tool reported a failure."); + SANITIZER_OS_LOG(OS_LOG_DEFAULT, "%{public}s", + "Sanitizer tool reported a failure."); if (common_flags()->log_to_syslog) - SANITIZER_OS_LOG(OS_LOG_DEFAULT, "Consult syslog for more information."); + SANITIZER_OS_LOG(OS_LOG_DEFAULT, "%{public}s", + "Consult syslog for more information."); // Log to syslog. // The logging on OS X may call pthread_create so we need the threading @@ -952,6 +964,10 @@ void SignalContext::InitPcSpBp() { GetPcSpBp(context, &pc, &sp, &bp); } +#ifndef KERN_DENIED +#define KERN_DENIED 53 +#endif + // ASan/TSan use mmap in a way that creates “deallocation gaps” which triggers // EXC_GUARD exceptions on macOS 10.15+ (XNU 19.0+). static void DisableMmapExcGuardExceptions() { diff --git a/libsanitizer/sanitizer_common/sanitizer_mac.h b/libsanitizer/sanitizer_common/sanitizer_mac.h index b0e4ac7f4074..d4a36137be03 100644 --- a/libsanitizer/sanitizer_common/sanitizer_mac.h +++ b/libsanitizer/sanitizer_common/sanitizer_mac.h @@ -14,6 +14,26 @@ #include "sanitizer_common.h" #include "sanitizer_platform.h" + +/* TARGET_OS_OSX is not present in SDKs before Darwin16 (macOS 10.12) use + TARGET_OS_MAC (we have no support for iOS in any form for these versions, + so there's no ambiguity). */ +#if !defined(TARGET_OS_OSX) && TARGET_OS_MAC +# define TARGET_OS_OSX 1 +#endif + +/* Other TARGET_OS_xxx are not present on earlier versions, define them to + 0 (we have no support for them; they are not valid targets anyway). */ +#ifndef TARGET_OS_IOS +#define TARGET_OS_IOS 0 +#endif +#ifndef TARGET_OS_TV +#define TARGET_OS_TV 0 +#endif +#ifndef TARGET_OS_WATCH +#define TARGET_OS_WATCH 0 +#endif + #if SANITIZER_APPLE #include "sanitizer_posix.h" diff --git a/libsanitizer/sanitizer_common/sanitizer_procmaps_mac.cpp b/libsanitizer/sanitizer_common/sanitizer_procmaps_mac.cpp index f40fba6bf715..ca5c71ae9667 100644 --- a/libsanitizer/sanitizer_common/sanitizer_procmaps_mac.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_procmaps_mac.cpp @@ -239,19 +239,18 @@ typedef struct dyld_shared_cache_dylib_text_info extern bool _dyld_get_shared_cache_uuid(uuid_t uuid); extern const void *_dyld_get_shared_cache_range(size_t *length); extern intptr_t _dyld_get_image_slide(const struct mach_header* mh); +} // extern "C" + +#ifdef __BLOCKS__ +extern "C" { extern int dyld_shared_cache_iterate_text( const uuid_t cacheUuid, void (^callback)(const dyld_shared_cache_dylib_text_info *info)); } // extern "C" -static mach_header *GetDyldImageHeaderViaSharedCache() { - uuid_t uuid; - bool hasCache = _dyld_get_shared_cache_uuid(uuid); - if (!hasCache) - return nullptr; - +static mach_header *GetDyldImageHeaderViaSharedCache(uuid_t uuid) { size_t cacheLength; - __block uptr cacheStart = (uptr)_dyld_get_shared_cache_range(&cacheLength); + const uptr cacheStart = (uptr)_dyld_get_shared_cache_range(&cacheLength); CHECK(cacheStart && cacheLength); __block mach_header *dyldHdr = nullptr; @@ -268,12 +267,126 @@ static mach_header *GetDyldImageHeaderViaSharedCache() { return dyldHdr; } +#else + +/* Here we implement a manual emulation of the Blocks closure and callback + that is needed by dyld_shared_cache_iterate_text (). In the compiler- + generated code, the [local] names are mangled differently, but that + should not matter to the function (since all the entities are TU-local). */ + +extern "C" { +/* Some descriptions of the blocks interfaces/runtime that we need. */ +extern void *_NSConcreteStackBlock; +extern void _Block_object_assign(void *, void *, int); +extern void _Block_object_dispose(void *, int); +enum { + BLOCK_FIELD_IS_OBJECT = 3, /* id, NSObject, __attribute__((NSObject)), block, ... */ + BLOCK_FIELD_IS_BLOCK = 7, /* a block variable */ + BLOCK_FIELD_IS_BYREF = 8, /* the on stack structure holding the __block variable */ + BLOCK_FIELD_IS_WEAK = 16, /* declared __weak, only used in byref copy helpers */ + BLOCK_BYREF_CALLER = 128 /* called from __block (byref) copy/dispose support routines. */ +}; + +/* 1. __block mach_header *dyldHdr; Uses this on-stack object. */ +typedef struct __block_byref_dyldHdr { + void *__isa; + struct __block_byref_dyldHdr *forwarding; + int flags; // 1<<25 iff we need copy helper; + int size; + mach_header *dyldHdr_cp; +} dyldHdr_type; + +/* The closure, support functions and the actuall callback code. */ +/* 1. closure descriptor. A constant instance of this will be created. */ +struct Block_descriptor_cb { + unsigned long int reserved; // NULL + unsigned long int size; // sizeof(struct Block_literal_cb) + // optional helper functions + void (*copy_helper)(struct Block_literal_cb *dst, struct Block_literal_cb *src); // IFF (1<<25) + void (*dispose_helper)(struct Block_literal_cb *src); // IFF (1<<25) + // required ABI.2010.3.16 + const char *signature; // IFF (1<<30) + void *no_idea; // not documented in the current ABI +}; + +/* 2. This is the closure object. In this case, it will be allocated on the + stack and does not need to be moved (since the closure never escapes + from its enclosing scope). However, the infrastructure still provides + support for moving it to the heap if required. */ +typedef struct Block_literal_cb { + void *__isa; + int flags; + int reserved; + void (*invoke) (Block_literal_cb*, const dyld_shared_cache_dylib_text_info *info); + const struct Block_descriptor_cb *descriptor; + dyldHdr_type *h_holder; + uptr cache_start; +} CallbackBlk; + +/* 3. Supporting functions to allow for the closure to be copied to the heap on + demand. */ +static void +__block_copy_cb (struct Block_literal_cb *dst, struct Block_literal_cb *src) +{ + //_Block_byref_assign_copy(&dst->captured_i, src->captured_i); + _Block_object_assign(&dst->h_holder, src->h_holder, BLOCK_FIELD_IS_BYREF); +} + +static void +__block_dispose_cb (struct Block_literal_cb *src) { + //_Block_byref_release(src->captured_i); + _Block_object_dispose(src->h_holder, BLOCK_FIELD_IS_BYREF); +} + +/* 4. Here is the actual code implementing the callback. */ +static void +__block_invoke_cb (struct Block_literal_cb *_block, + const dyld_shared_cache_dylib_text_info *info) +{ + mach_header *hdr = (mach_header *)(_block->cache_start + info->textSegmentOffset); + if (IsDyldHdr(hdr)) + _block->h_holder->forwarding->dyldHdr_cp = hdr; +} + +/* 6. The constant instance of the descriptor mentioned in 1 above. */ +static const struct Block_descriptor_cb cb_descr = { + 0, sizeof (struct Block_literal_cb), __block_copy_cb, __block_dispose_cb, + "v16@?0r^{dyld_shared_cache_dylib_text_info=QQQ[16C]*Q}8", nullptr +}; + +/* Declare dyld_shared_cache_iterate_text () as taking a regular pointer to + the closure; we cannot use the ^ syntax yet in GCC. */ +extern int dyld_shared_cache_iterate_text(const uuid_t cacheUuid, CallbackBlk*); +} // extern "C" + +/* The non-blocks version of GetDyldImageHeaderViaSharedCache (). */ +static mach_header *GetDyldImageHeaderViaSharedCache(uuid_t uuid) { + size_t cacheLength; + const uptr cacheStart = (uptr)_dyld_get_shared_cache_range(&cacheLength); + CHECK(cacheStart && cacheLength); + /* __block mach_header *dyldHdr = nullptr; */ + dyldHdr_type dyldHdr + = { nullptr, &dyldHdr, 0, sizeof (struct __block_byref_dyldHdr), nullptr }; + /* Callback cb = ^(const dyld_shared_cache_dylib_text_info *info... */ + CallbackBlk cb + = { _NSConcreteStackBlock, 0x42000000, 0, __block_invoke_cb, + &cb_descr, &dyldHdr, cacheStart }; + int res = dyld_shared_cache_iterate_text ( uuid, &cb ); + CHECK_EQ(res, 0); + return dyldHdr.forwarding->dyldHdr_cp; +} +#endif + const mach_header *get_dyld_hdr() { if (!dyld_hdr) { // On macOS 13+, dyld itself has moved into the shared cache. Looking it up // via vm_region_recurse_64() causes spins/hangs/crashes. if (GetMacosAlignedVersion() >= MacosVersion(13, 0)) { - dyld_hdr = GetDyldImageHeaderViaSharedCache(); + uuid_t uuid; + if (!_dyld_get_shared_cache_uuid(uuid)) + VReport(1, "Failed get the shared cache on macOS 13+\n"); + else + dyld_hdr = GetDyldImageHeaderViaSharedCache(uuid); if (!dyld_hdr) { VReport(1, "Failed to lookup the dyld image header in the shared cache on "