Index: lib/sanitizer_common/sanitizer_common.h =================================================================== --- lib/sanitizer_common/sanitizer_common.h +++ lib/sanitizer_common/sanitizer_common.h @@ -723,8 +723,10 @@ void WriteToSyslog(const char *buffer); #if SANITIZER_MAC +void DarwinLogInit(bool IsLogThreadsafe); void LogFullErrorReport(const char *buffer); #else +INLINE void DarwinLogInit(bool IsLogThreadsafe) {} INLINE void LogFullErrorReport(const char *buffer) {} #endif Index: lib/sanitizer_common/sanitizer_mac.cc =================================================================== --- lib/sanitizer_common/sanitizer_mac.cc +++ lib/sanitizer_common/sanitizer_mac.cc @@ -392,6 +392,29 @@ return (signum == SIGSEGV || signum == SIGBUS) && common_flags()->handle_segv; } +#ifndef SANITIZER_GO +uint32_t is_log_threadsafe = 0; +#endif + +// The logging on OS X may call pthread_create so we need the threading +// environment to be fully initialized. +// +// NOTE: It isn't safe to write to the syslog while holding a thread registry +// lock. If the reporting thread holds the thread registry mutex, and asl_log +// waits for GCD to dispatch a new thread, the process will deadlock, because +// the pthread_create wrapper needs to acquire the lock as well. Only set +// IsLogThreadsafe to true if this deadlock scenario has been ruled out. +// +// If multiple sanitizers are enabled, it's safe to set IsLogThreadsafe=true +// in one runtime and not in the others. That's because no single thread will +// double-lock a thread-registry lock while preparing a report. +void DarwinLogInit(bool IsLogThreadsafe) { +#ifndef SANITIZER_GO + atomic_store(reinterpret_cast(&is_log_threadsafe), + uint32_t(IsLogThreadsafe), memory_order_release); +#endif +} + MacosVersion cached_macos_version = MACOS_VERSION_UNINITIALIZED; MacosVersion GetMacosVersionInternal() { @@ -471,10 +494,31 @@ #endif } +void WriteToSyslogIfPossible(const char *str) { +#ifndef SANITIZER_GO + // We can't write to syslog if it's disabled. + if (!common_flags()->log_to_syslog) + return; + + // We also can't do the write if there's a danger of double-locking a thread + // registry lock. See this discussion: https://reviews.llvm.org/D24781 + if (!atomic_load(reinterpret_cast(&is_log_threadsafe), + memory_order_acquire)) + return; + + BlockingMutexLock l(&syslog_lock); + WriteToSyslog(str); +#endif +} + void LogMessageOnPrintf(const char *str) { - // Log all printf output to CrashLog. - if (common_flags()->abort_on_error) + // If the program is about to crash, *just* copy the report into CrashLog. + if (common_flags()->abort_on_error) { CRAppendCrashLogMessage(str); + return; + } + + WriteToSyslogIfPossible(str); } void LogFullErrorReport(const char *buffer) { @@ -501,15 +545,7 @@ #endif // Log to syslog. - // The logging on OS X may call pthread_create so we need the threading - // environment to be fully initialized. Also, this should never be called when - // holding the thread registry lock since that may result in a deadlock. If - // the reporting thread holds the thread registry mutex, and asl_log waits - // for GCD to dispatch a new thread, the process will deadlock, because the - // pthread_create wrapper needs to acquire the lock as well. - BlockingMutexLock l(&syslog_lock); - if (common_flags()->log_to_syslog) - WriteToSyslog(buffer); + WriteToSyslogIfPossible(buffer); // The report is added to CrashLog as part of logging all of Printf output. #endif Index: lib/ubsan/ubsan_init.cc =================================================================== --- lib/ubsan/ubsan_init.cc +++ lib/ubsan/ubsan_init.cc @@ -40,6 +40,9 @@ CacheBinaryName(); __sanitizer_set_report_path(common_flags()->log_path); AndroidLogInit(); + // Set up logging on Darwin. There is no thread-registry lock in ubsan, so + // the logging routine may safely create a thread whenever it needs to. + DarwinLogInit(/*IsLogThreadsafe=*/true); InitializeCoverage(common_flags()->coverage, common_flags()->coverage_dir); CommonInit(); ubsan_mode = UBSAN_MODE_STANDALONE; Index: test/ubsan/TestCases/Misc/log-path_test.cc =================================================================== --- test/ubsan/TestCases/Misc/log-path_test.cc +++ test/ubsan/TestCases/Misc/log-path_test.cc @@ -20,6 +20,10 @@ // RUN: %env_ubsan_opts=log_path='"%t.log"' %run %t 4 // RUN: not cat %t.log.* +// We should print out a report even if we're logging to syslog. +// RUN: %env_ubsan_opts=log_to_syslog=1 %run %t -4 2> %t.out +// RUN: FileCheck %s --check-prefix=CHECK-ERROR < %t.out + // FIXME: log_path is not supported on Windows yet. // XFAIL: win32