Index: lib/asan/asan_internal.h =================================================================== --- lib/asan/asan_internal.h +++ lib/asan/asan_internal.h @@ -111,8 +111,16 @@ void ReserveShadowMemoryRange(uptr beg, uptr end, const char *name); -// Add convenient macro for interface functions that may be represented as -// weak hooks. +#if SANITIZER_SUPPORTS_INIT_FOR_DLOPEN +bool InitIsViaDlopen(); +void HandleDlopenInit(); +#else +static inline bool InitIsViaDlopen() { return false; } +static inline void HandleDlopenInit() { Die(); } +#endif + +// Add convenient macro for interface functions that may be represented +// as weak hooks. #define ASAN_MALLOC_HOOK(ptr, size) \ do { \ if (&__sanitizer_malloc_hook) __sanitizer_malloc_hook(ptr, size); \ Index: lib/asan/asan_mac.cc =================================================================== --- lib/asan/asan_mac.cc +++ lib/asan/asan_mac.cc @@ -204,6 +204,19 @@ asan_free(context, &stack, FROM_MALLOC); } +bool InitIsViaDlopen() { + // We have no reliable way of knowing how we are being loaded + // so make it a requirement on Apple platforms to set this environment + // variable to indicate that we want to perform initialization via + // dlopen(). + auto init_str = GetEnv("APPLE_ASAN_INIT_FOR_DLOPEN"); + if (!init_str) + return false; + if (internal_strncmp(init_str, "1", 1) == 0) + return true; + return false; +} + } // namespace __asan using namespace __asan; // NOLINT Index: lib/asan/asan_malloc_mac.cc =================================================================== --- lib/asan/asan_malloc_mac.cc +++ lib/asan/asan_malloc_mac.cc @@ -61,4 +61,13 @@ #include "sanitizer_common/sanitizer_malloc_mac.inc" +namespace COMMON_MALLOC_NAMESPACE { +void HandleDlopenInit() { + // When we are loaded via `dlopen()` path we still initialize the malloc zone + // so Symbolication clients (e.g. `leaks`) that load the ASan allocator can + // find an initialized malloc zone. + InitMallocZoneFields(); +} +} // namespace COMMON_MALLOC_NAMESPACE + #endif Index: lib/asan/asan_rtl.cc =================================================================== --- lib/asan/asan_rtl.cc +++ lib/asan/asan_rtl.cc @@ -396,6 +396,15 @@ // initialization steps look at flags(). InitializeFlags(); + // Stop performing init at this point if we are being loaded via + // dlopen() and the platform supports it. + if (SANITIZER_SUPPORTS_INIT_FOR_DLOPEN && UNLIKELY(InitIsViaDlopen())) { + asan_init_is_running = false; + Report("AddressSanitizer init is being performed for dlopen().\n"); + HandleDlopenInit(); + return; + } + AsanCheckIncompatibleRT(); AsanCheckDynamicRTPrereqs(); AvoidCVE_2016_2143(); Index: lib/sanitizer_common/sanitizer_platform.h =================================================================== --- lib/sanitizer_common/sanitizer_platform.h +++ lib/sanitizer_common/sanitizer_platform.h @@ -342,4 +342,13 @@ #define SANITIZER_SYMBOLIZER_MARKUP 0 #endif +// Enable ability to support sanitizer initialization that is +// compatible with the sanitizer library being loaded via +// `dlopen()`. +#if SANITIZER_MAC +#define SANITIZER_SUPPORTS_INIT_FOR_DLOPEN 1 +#else +#define SANITIZER_SUPPORTS_INIT_FOR_DLOPEN 0 +#endif + #endif // SANITIZER_PLATFORM_H Index: test/asan/TestCases/Darwin/init_for_dlopen.cc =================================================================== --- /dev/null +++ test/asan/TestCases/Darwin/init_for_dlopen.cc @@ -0,0 +1,41 @@ +// RUN: %clangxx -g -O0 %s -o %t + +// Check that trying to dlopen() the ASan dylib fails. +// We explictly set `abort_on_error=0` because +// - By default the lit config sets this but we don't want this +// test to implicitly depend on this. +// - It avoids requiring `--crash` to be passed to `not`. +// RUN: APPLE_ASAN_INIT_FOR_DLOPEN=0 %env_asan_opts=abort_on_error=0 not %run %t %shared_libasan 2>&1 | FileCheck -check-prefix=CHECK-DL-OPEN-FAIL %s +// RUN: %env_asan_opts=abort_on_error=0 not %run %t %shared_libasan 2>&1 | FileCheck -check-prefix=CHECK-DL-OPEN-FAIL %s + +// Check that we can successfully dlopen the ASan dylib when we set the right +// environment variable. +// RUN: env APPLE_ASAN_INIT_FOR_DLOPEN=1 %run %t %shared_libasan 2>&1 | FileCheck -check-prefix=CHECK-DL-OPEN-SUCCESS %s + +#include +#include + +// CHECK-DL-OPEN-FAIL: ERROR: Interceptors are not working + +int main(int argc, char **argv) { + if (argc != 2) { + fprintf(stderr, "Usage: %s \n", argv[0]); + return 1; + } + const char *dylib_path = argv[1]; + void *handle = dlopen(dylib_path, RTLD_LAZY); + if (!handle) { + fprintf(stderr, "Failed to dlopen: %s\n", dlerror()); + return 1; + } + // Make sure we can find a function we expect to be in the dylib. + void *fn = dlsym(handle, "__sanitizer_mz_size"); + if (!fn) { + fprintf(stderr, "Failed to get symbol: %s\n", dlerror()); + return 1; + } + // TODO(dliew): Actually call a function from the dylib that is safe to call. + // CHECK-DL-OPEN-SUCCESS: DONE + printf("DONE\n"); + return 0; +} Index: test/sanitizer_common/ios_commands/iossim_run.py =================================================================== --- test/sanitizer_common/ios_commands/iossim_run.py +++ test/sanitizer_common/ios_commands/iossim_run.py @@ -8,7 +8,7 @@ device_id = os.environ["SANITIZER_IOSSIM_TEST_DEVICE_IDENTIFIER"] -for e in ["ASAN_OPTIONS", "TSAN_OPTIONS", "UBSAN_OPTIONS"]: +for e in ["ASAN_OPTIONS", "TSAN_OPTIONS", "UBSAN_OPTIONS", "APPLE_ASAN_INIT_FOR_DLOPEN"]: if e in os.environ: os.environ["SIMCTL_CHILD_" + e] = os.environ[e]