Propagating prior merge from 'llvm.org/release_40'.
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e8326f0..f6eed77 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -134,7 +134,7 @@
endif()
append_list_if(COMPILER_RT_HAS_FNO_BUILTIN_FLAG -fno-builtin SANITIZER_COMMON_CFLAGS)
append_list_if(COMPILER_RT_HAS_FNO_EXCEPTIONS_FLAG -fno-exceptions SANITIZER_COMMON_CFLAGS)
-if(NOT COMPILER_RT_DEBUG)
+if(NOT COMPILER_RT_DEBUG AND NOT APPLE)
append_list_if(COMPILER_RT_HAS_FOMIT_FRAME_POINTER_FLAG -fomit-frame-pointer SANITIZER_COMMON_CFLAGS)
endif()
append_list_if(COMPILER_RT_HAS_FUNWIND_TABLES_FLAG -funwind-tables SANITIZER_COMMON_CFLAGS)
diff --git a/README.txt b/README.txt
index fc88432..2d64f00 100644
--- a/README.txt
+++ b/README.txt
@@ -8,4 +8,3 @@
terms of the license agreement found in LICENSE.txt.
================================
-
diff --git a/cmake/config-ix.cmake b/cmake/config-ix.cmake
index e0e4355..e84a066 100644
--- a/cmake/config-ix.cmake
+++ b/cmake/config-ix.cmake
@@ -199,7 +199,7 @@
list(APPEND DARWIN_EMBEDDED_PLATFORMS ios)
set(DARWIN_ios_MIN_VER_FLAG -miphoneos-version-min)
set(DARWIN_ios_SANITIZER_MIN_VER_FLAG
- ${DARWIN_ios_MIN_VER_FLAG}=7.0)
+ ${DARWIN_ios_MIN_VER_FLAG}=8.0)
endif()
if(COMPILER_RT_ENABLE_WATCHOS)
list(APPEND DARWIN_EMBEDDED_PLATFORMS watchos)
diff --git a/lib/asan/asan_allocator.cc b/lib/asan/asan_allocator.cc
index ee9b1a6..1478949 100644
--- a/lib/asan/asan_allocator.cc
+++ b/lib/asan/asan_allocator.cc
@@ -523,6 +523,18 @@
AsanThread *t = GetCurrentThread();
m->free_tid = t ? t->tid() : 0;
m->free_context_id = StackDepotPut(*stack);
+
+ Flags &fl = *flags();
+ if (fl.max_free_fill_size > 0) {
+ // We have to skip the chunk header, it contains free_context_id.
+ uptr scribble_start = (uptr)m + kChunkHeaderSize + kChunkHeader2Size;
+ if (m->UsedSize() >= kChunkHeader2Size) { // Skip Header2 in user area.
+ uptr size_to_fill = m->UsedSize() - kChunkHeader2Size;
+ size_to_fill = Min(size_to_fill, (uptr)fl.max_free_fill_size);
+ REAL(memset)((void *)scribble_start, fl.free_fill_byte, size_to_fill);
+ }
+ }
+
// Poison the region.
PoisonShadow(m->Beg(),
RoundUpTo(m->UsedSize(), SHADOW_GRANULARITY),
diff --git a/lib/asan/asan_descriptions.cc b/lib/asan/asan_descriptions.cc
index 0ecbe09..822a6a6 100644
--- a/lib/asan/asan_descriptions.cc
+++ b/lib/asan/asan_descriptions.cc
@@ -252,6 +252,9 @@
str.append("%c", var.name_pos[i]);
}
str.append("'");
+ if (var.line > 0) {
+ str.append(" (line %d)", var.line);
+ }
if (pos_descr) {
Decorator d;
// FIXME: we may want to also print the size of the access here,
diff --git a/lib/asan/asan_flags.cc b/lib/asan/asan_flags.cc
index ad5bbff..81dcc0d 100644
--- a/lib/asan/asan_flags.cc
+++ b/lib/asan/asan_flags.cc
@@ -95,6 +95,18 @@
RegisterCommonFlags(&ubsan_parser);
#endif
+ if (SANITIZER_MAC) {
+ // Support macOS MallocScribble and MallocPreScribble:
+ // <https://developer.apple.com/library/content/documentation/Performance/
+ // Conceptual/ManagingMemory/Articles/MallocDebug.html>
+ if (GetEnv("MallocScribble")) {
+ f->max_free_fill_size = 0x1000;
+ }
+ if (GetEnv("MallocPreScribble")) {
+ f->malloc_fill_byte = 0xaa;
+ }
+ }
+
// Override from ASan compile definition.
const char *asan_compile_def = MaybeUseAsanDefaultOptionsCompileDefinition();
asan_parser.ParseString(asan_compile_def);
diff --git a/lib/asan/asan_flags.inc b/lib/asan/asan_flags.inc
index 4712efb..98cbca2 100644
--- a/lib/asan/asan_flags.inc
+++ b/lib/asan/asan_flags.inc
@@ -63,8 +63,14 @@
int, max_malloc_fill_size, 0x1000, // By default, fill only the first 4K.
"ASan allocator flag. max_malloc_fill_size is the maximal amount of "
"bytes that will be filled with malloc_fill_byte on malloc.")
+ASAN_FLAG(
+ int, max_free_fill_size, 0,
+ "ASan allocator flag. max_free_fill_size is the maximal amount of "
+ "bytes that will be filled with free_fill_byte during free.")
ASAN_FLAG(int, malloc_fill_byte, 0xbe,
"Value used to fill the newly allocated memory.")
+ASAN_FLAG(int, free_fill_byte, 0x55,
+ "Value used to fill deallocated memory.")
ASAN_FLAG(bool, allow_user_poisoning, true,
"If set, user may manually mark memory regions as poisoned or "
"unpoisoned.")
diff --git a/lib/asan/asan_mac.cc b/lib/asan/asan_mac.cc
index baf533a..38189a9 100644
--- a/lib/asan/asan_mac.cc
+++ b/lib/asan/asan_mac.cc
@@ -138,7 +138,8 @@
t = AsanThread::Create(/* start_routine */ nullptr, /* arg */ nullptr,
parent_tid, stack, /* detached */ true);
t->Init();
- asanThreadRegistry().StartThread(t->tid(), 0, 0);
+ asanThreadRegistry().StartThread(t->tid(), GetTid(),
+ /* workerthread */ true, 0);
SetCurrentThread(t);
}
}
diff --git a/lib/asan/asan_report.cc b/lib/asan/asan_report.cc
index 3ad48fa..d1c3cda 100644
--- a/lib/asan/asan_report.cc
+++ b/lib/asan/asan_report.cc
@@ -88,7 +88,8 @@
char *p;
// This string is created by the compiler and has the following form:
// "n alloc_1 alloc_2 ... alloc_n"
- // where alloc_i looks like "offset size len ObjectName".
+ // where alloc_i looks like "offset size len ObjectName"
+ // or "offset size len ObjectName:line".
uptr n_objects = (uptr)internal_simple_strtoll(frame_descr, &p, 10);
if (n_objects == 0)
return false;
@@ -101,7 +102,14 @@
return false;
}
p++;
- StackVarDescr var = {beg, size, p, len};
+ char *colon_pos = internal_strchr(p, ':');
+ uptr line = 0;
+ uptr name_len = len;
+ if (colon_pos != nullptr && colon_pos < p + len) {
+ name_len = colon_pos - p;
+ line = (uptr)internal_simple_strtoll(colon_pos + 1, nullptr, 10);
+ }
+ StackVarDescr var = {beg, size, p, name_len, line};
vars->push_back(var);
p += len;
}
diff --git a/lib/asan/asan_report.h b/lib/asan/asan_report.h
index 5ebfda6..5a3533a 100644
--- a/lib/asan/asan_report.h
+++ b/lib/asan/asan_report.h
@@ -23,6 +23,7 @@
uptr size;
const char *name_pos;
uptr name_len;
+ uptr line;
};
// Returns the number of globals close to the provided address and copies
diff --git a/lib/asan/asan_thread.cc b/lib/asan/asan_thread.cc
index 537b53d..2f9fa81 100644
--- a/lib/asan/asan_thread.cc
+++ b/lib/asan/asan_thread.cc
@@ -239,7 +239,8 @@
thread_return_t AsanThread::ThreadStart(
uptr os_id, atomic_uintptr_t *signal_thread_is_registered) {
Init();
- asanThreadRegistry().StartThread(tid(), os_id, nullptr);
+ asanThreadRegistry().StartThread(tid(), os_id, /*workerthread*/ false,
+ nullptr);
if (signal_thread_is_registered)
atomic_store(signal_thread_is_registered, 1, memory_order_release);
diff --git a/lib/builtins/CMakeLists.txt b/lib/builtins/CMakeLists.txt
index 3cf7861..8ba3a4e 100644
--- a/lib/builtins/CMakeLists.txt
+++ b/lib/builtins/CMakeLists.txt
@@ -132,6 +132,7 @@
negvdi2.c
negvsi2.c
negvti2.c
+ os_version_check.c
paritydi2.c
paritysi2.c
parityti2.c
diff --git a/lib/builtins/os_version_check.c b/lib/builtins/os_version_check.c
new file mode 100644
index 0000000..711ba60
--- /dev/null
+++ b/lib/builtins/os_version_check.c
@@ -0,0 +1,170 @@
+/* ===-- os_version_check.c - OS version checking -------------------------===
+ *
+ * The LLVM Compiler Infrastructure
+ *
+ * This file is dual licensed under the MIT and the University of Illinois Open
+ * Source Licenses. See LICENSE.TXT for details.
+ *
+ * ===----------------------------------------------------------------------===
+ *
+ * This file implements the function __isOSVersionAtLeast, used by
+ * Objective-C's @available
+ *
+ * ===----------------------------------------------------------------------===
+ */
+
+#ifdef __APPLE__
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <dispatch/dispatch.h>
+#include <TargetConditionals.h>
+#include <dlfcn.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* These three variables hold the host's OS version. */
+static int32_t GlobalMajor, GlobalMinor, GlobalSubminor;
+static dispatch_once_t DispatchOnceCounter;
+
+/* Find and parse the SystemVersion.plist file. */
+static void parseSystemVersionPList(void *Unused) {
+ (void)Unused;
+ /* Load CoreFoundation dynamically */
+ const void *NullAllocator = dlsym(RTLD_DEFAULT, "kCFAllocatorNull");
+ if (!NullAllocator)
+ return;
+ const CFAllocatorRef kCFAllocatorNull =
+ *(const CFAllocatorRef *)NullAllocator;
+ typeof(CFDataCreateWithBytesNoCopy) *CFDataCreateWithBytesNoCopyFunc =
+ (typeof(CFDataCreateWithBytesNoCopy) *)dlsym(
+ RTLD_DEFAULT, "CFDataCreateWithBytesNoCopy");
+ if (!CFDataCreateWithBytesNoCopyFunc)
+ return;
+ typeof(CFPropertyListCreateWithData) *CFPropertyListCreateWithDataFunc =
+ (typeof(CFPropertyListCreateWithData) *)dlsym(
+ RTLD_DEFAULT, "CFPropertyListCreateWithData");
+ /* CFPropertyListCreateWithData was introduced only in macOS 10.6+, so it
+ * will be NULL on earlier OS versions. */
+ typeof(CFPropertyListCreateFromXMLData) *CFPropertyListCreateFromXMLDataFunc =
+ (typeof(CFPropertyListCreateFromXMLData) *)dlsym(
+ RTLD_DEFAULT, "CFPropertyListCreateFromXMLData");
+ /* CFPropertyListCreateFromXMLDataFunc is deprecated in macOS 10.10, so it
+ * might be NULL in future OS versions. */
+ if (!CFPropertyListCreateWithDataFunc && !CFPropertyListCreateFromXMLDataFunc)
+ return;
+ typeof(CFStringCreateWithCStringNoCopy) *CFStringCreateWithCStringNoCopyFunc =
+ (typeof(CFStringCreateWithCStringNoCopy) *)dlsym(
+ RTLD_DEFAULT, "CFStringCreateWithCStringNoCopy");
+ if (!CFStringCreateWithCStringNoCopyFunc)
+ return;
+ typeof(CFDictionaryGetValue) *CFDictionaryGetValueFunc =
+ (typeof(CFDictionaryGetValue) *)dlsym(RTLD_DEFAULT,
+ "CFDictionaryGetValue");
+ if (!CFDictionaryGetValueFunc)
+ return;
+ typeof(CFGetTypeID) *CFGetTypeIDFunc =
+ (typeof(CFGetTypeID) *)dlsym(RTLD_DEFAULT, "CFGetTypeID");
+ if (!CFGetTypeIDFunc)
+ return;
+ typeof(CFStringGetTypeID) *CFStringGetTypeIDFunc =
+ (typeof(CFStringGetTypeID) *)dlsym(RTLD_DEFAULT, "CFStringGetTypeID");
+ if (!CFStringGetTypeIDFunc)
+ return;
+ typeof(CFStringGetCString) *CFStringGetCStringFunc =
+ (typeof(CFStringGetCString) *)dlsym(RTLD_DEFAULT, "CFStringGetCString");
+ if (!CFStringGetCStringFunc)
+ return;
+ typeof(CFRelease) *CFReleaseFunc =
+ (typeof(CFRelease) *)dlsym(RTLD_DEFAULT, "CFRelease");
+ if (!CFReleaseFunc)
+ return;
+
+ char *PListPath = "/System/Library/CoreServices/SystemVersion.plist";
+
+#if TARGET_OS_SIMULATOR
+ char *PListPathPrefix = getenv("IPHONE_SIMULATOR_ROOT");
+ if (!PListPathPrefix)
+ return;
+ char FullPath[strlen(PListPathPrefix) + strlen(PListPath) + 1];
+ strcpy(FullPath, PListPathPrefix);
+ strcat(FullPath, PListPath);
+ PListPath = FullPath;
+#endif
+ FILE *PropertyList = fopen(PListPath, "r");
+ if (!PropertyList)
+ return;
+
+ /* Dynamically allocated stuff. */
+ CFDictionaryRef PListRef = NULL;
+ CFDataRef FileContentsRef = NULL;
+ UInt8 *PListBuf = NULL;
+
+ fseek(PropertyList, 0, SEEK_END);
+ long PListFileSize = ftell(PropertyList);
+ if (PListFileSize < 0)
+ goto Fail;
+ rewind(PropertyList);
+
+ PListBuf = malloc((size_t)PListFileSize);
+ if (!PListBuf)
+ goto Fail;
+
+ size_t NumRead = fread(PListBuf, 1, (size_t)PListFileSize, PropertyList);
+ if (NumRead != (size_t)PListFileSize)
+ goto Fail;
+
+ /* Get the file buffer into CF's format. We pass in a null allocator here *
+ * because we free PListBuf ourselves */
+ FileContentsRef = (*CFDataCreateWithBytesNoCopyFunc)(
+ NULL, PListBuf, (CFIndex)NumRead, kCFAllocatorNull);
+ if (!FileContentsRef)
+ goto Fail;
+
+ if (CFPropertyListCreateWithDataFunc)
+ PListRef = (*CFPropertyListCreateWithDataFunc)(
+ NULL, FileContentsRef, kCFPropertyListImmutable, NULL, NULL);
+ else
+ PListRef = (*CFPropertyListCreateFromXMLDataFunc)(
+ NULL, FileContentsRef, kCFPropertyListImmutable, NULL);
+ if (!PListRef)
+ goto Fail;
+
+ CFStringRef ProductVersion = (*CFStringCreateWithCStringNoCopyFunc)(
+ NULL, "ProductVersion", kCFStringEncodingASCII, kCFAllocatorNull);
+ if (!ProductVersion)
+ goto Fail;
+ CFTypeRef OpaqueValue = (*CFDictionaryGetValueFunc)(PListRef, ProductVersion);
+ (*CFReleaseFunc)(ProductVersion);
+ if (!OpaqueValue ||
+ (*CFGetTypeIDFunc)(OpaqueValue) != (*CFStringGetTypeIDFunc)())
+ goto Fail;
+
+ char VersionStr[32];
+ if (!(*CFStringGetCStringFunc)((CFStringRef)OpaqueValue, VersionStr,
+ sizeof(VersionStr), kCFStringEncodingUTF8))
+ goto Fail;
+ sscanf(VersionStr, "%d.%d.%d", &GlobalMajor, &GlobalMinor, &GlobalSubminor);
+
+Fail:
+ if (PListRef)
+ (*CFReleaseFunc)(PListRef);
+ if (FileContentsRef)
+ (*CFReleaseFunc)(FileContentsRef);
+ free(PListBuf);
+ fclose(PropertyList);
+}
+
+int32_t __isOSVersionAtLeast(int32_t Major, int32_t Minor, int32_t Subminor) {
+ /* Populate the global version variables, if they haven't already. */
+ dispatch_once_f(&DispatchOnceCounter, NULL, parseSystemVersionPList);
+
+ if (Major < GlobalMajor) return 1;
+ if (Major > GlobalMajor) return 0;
+ if (Minor < GlobalMinor) return 1;
+ if (Minor > GlobalMinor) return 0;
+ return Subminor <= GlobalSubminor;
+}
+
+#endif
diff --git a/lib/lsan/lsan_thread.cc b/lib/lsan/lsan_thread.cc
index 5dff4f7..f82a04a 100644
--- a/lib/lsan/lsan_thread.cc
+++ b/lib/lsan/lsan_thread.cc
@@ -97,7 +97,7 @@
args.tls_end = args.tls_begin + tls_size;
GetAllocatorCacheRange(&args.cache_begin, &args.cache_end);
args.dtls = DTLS_Get();
- thread_registry->StartThread(tid, os_id, &args);
+ thread_registry->StartThread(tid, os_id, /*workerthread*/ false, &args);
}
void ThreadFinish() {
diff --git a/lib/profile/InstrProfilingFile.c b/lib/profile/InstrProfilingFile.c
index f82080c..ba717ee 100644
--- a/lib/profile/InstrProfilingFile.c
+++ b/lib/profile/InstrProfilingFile.c
@@ -14,6 +14,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <signal.h>
#ifdef _MSC_VER
/* For _alloca. */
#include <malloc.h>
@@ -59,6 +60,7 @@
}
#define MAX_PID_SIZE 16
+#define MAX_SIGNAL_HANDLERS 16
/* Data structure holding the result of parsed filename pattern. */
typedef struct lprofFilename {
/* File name string possibly with %p or %h specifiers. */
@@ -79,11 +81,13 @@
* 2 profile data files. %1m is equivalent to %m. Also %m specifier
* can only appear once at the end of the name pattern. */
unsigned MergePoolSize;
+ char ExitOnSignals[MAX_SIGNAL_HANDLERS];
+ unsigned NumExitSignals;
ProfileNameSpecifier PNS;
} lprofFilename;
-COMPILER_RT_WEAK lprofFilename lprofCurFilename = {0, 0, 0, {0}, {0},
- 0, 0, 0, PNS_unknown};
+COMPILER_RT_WEAK lprofFilename lprofCurFilename = {
+ 0, 0, 0, {0}, {0}, 0, 0, 0, {0}, 0, PNS_unknown};
int getpid(void);
static int getCurFilenameLength();
@@ -172,6 +176,16 @@
return 0;
}
+/* Create the directory holding the file, if needed. */
+static void createProfileDir(const char *Filename) {
+ size_t Length = strlen(Filename);
+ if (lprofFindFirstDirSeparator(Filename)) {
+ char *Copy = (char *)COMPILER_RT_ALLOCA(Length + 1);
+ strncpy(Copy, Filename, Length + 1);
+ __llvm_profile_recursive_mkdir(Copy);
+ }
+}
+
/* Open the profile data for merging. It opens the file in r+b mode with
* file locking. If the file has content which is compatible with the
* current process, it also reads in the profile data in the file and merge
@@ -184,6 +198,7 @@
FILE *ProfileFile;
int rc;
+ createProfileDir(ProfileFileName);
ProfileFile = lprofOpenFileEx(ProfileFileName);
if (!ProfileFile)
return NULL;
@@ -233,18 +248,13 @@
if (!Filename)
return;
- /* Create the directory holding the file, if needed. */
- if (lprofFindFirstDirSeparator(Filename)) {
- char *Copy = (char *)COMPILER_RT_ALLOCA(Length + 1);
- strncpy(Copy, Filename, Length + 1);
- __llvm_profile_recursive_mkdir(Copy);
- }
-
/* By pass file truncation to allow online raw profile
* merging. */
if (lprofCurFilename.MergePoolSize)
return;
+ createProfileDir(Filename);
+
/* Truncate the file. Later we'll reopen and append. */
File = fopen(Filename, "w");
if (!File)
@@ -252,6 +262,19 @@
fclose(File);
}
+static void exitSignalHandler(int sig) {
+ (void)sig;
+ exit(0);
+}
+
+static void installExitSignalHandlers(void) {
+ unsigned I;
+ for (I = 0; I < lprofCurFilename.NumExitSignals; ++I) {
+ lprofInstallSignalHandler(lprofCurFilename.ExitOnSignals[I],
+ exitSignalHandler);
+ }
+}
+
static const char *DefaultProfileName = "default.profraw";
static void resetFilenameToDefault(void) {
if (lprofCurFilename.FilenamePat && lprofCurFilename.OwnsFilenamePat) {
@@ -262,14 +285,25 @@
lprofCurFilename.PNS = PNS_default;
}
+static int isDigit(char C) { return C >= '0' && C <= '9'; }
+
+static int isNonZeroDigit(char C) { return C >= '1' && C <= '9'; }
+
static int containsMergeSpecifier(const char *FilenamePat, int I) {
return (FilenamePat[I] == 'm' ||
- (FilenamePat[I] >= '1' && FilenamePat[I] <= '9' &&
+ (isNonZeroDigit(FilenamePat[I]) &&
/* If FilenamePat[I] is not '\0', the next byte is guaranteed
* to be in-bound as the string is null terminated. */
FilenamePat[I + 1] == 'm'));
}
+static int containsExitOnSignalSpecifier(const char *FilenamePat, int I) {
+ if (!isNonZeroDigit(FilenamePat[I]))
+ return 0;
+ return (FilenamePat[I + 1] == 'x') ||
+ (isDigit(FilenamePat[I + 1]) && FilenamePat[I + 2] == 'x');
+}
+
/* Parses the pattern string \p FilenamePat and stores the result to
* lprofcurFilename structure. */
static int parseFilenamePattern(const char *FilenamePat,
@@ -278,6 +312,7 @@
char *PidChars = &lprofCurFilename.PidChars[0];
char *Hostname = &lprofCurFilename.Hostname[0];
int MergingEnabled = 0;
+ char SignalNo;
/* Clean up cached prefix. */
if (lprofCurFilename.ProfilePathPrefix)
@@ -327,6 +362,22 @@
lprofCurFilename.MergePoolSize = FilenamePat[I] - '0';
I++; /* advance to 'm' */
}
+ } else if (containsExitOnSignalSpecifier(FilenamePat, I)) {
+ if (lprofCurFilename.NumExitSignals == MAX_SIGNAL_HANDLERS) {
+ PROF_WARN("%%x specifier has been specified too many times in %s.\n",
+ FilenamePat);
+ return -1;
+ }
+ /* Grab the signal number. */
+ SignalNo = FilenamePat[I] - '0';
+ I++; /* advance to either another digit, or 'x' */
+ if (FilenamePat[I] != 'x') {
+ SignalNo = (SignalNo * 10) + (FilenamePat[I] - '0');
+ I++; /* advance to 'x' */
+ }
+ lprofCurFilename.ExitOnSignals[lprofCurFilename.NumExitSignals] =
+ SignalNo;
+ ++lprofCurFilename.NumExitSignals;
}
}
@@ -370,6 +421,7 @@
}
truncateCurrentFile();
+ installExitSignalHandlers();
}
/* Return buffer length that is required to store the current profile
@@ -378,11 +430,12 @@
#define SIGLEN 24
static int getCurFilenameLength() {
int Len;
+ unsigned I;
if (!lprofCurFilename.FilenamePat || !lprofCurFilename.FilenamePat[0])
return 0;
if (!(lprofCurFilename.NumPids || lprofCurFilename.NumHosts ||
- lprofCurFilename.MergePoolSize))
+ lprofCurFilename.MergePoolSize || lprofCurFilename.NumExitSignals))
return strlen(lprofCurFilename.FilenamePat);
Len = strlen(lprofCurFilename.FilenamePat) +
@@ -390,6 +443,11 @@
lprofCurFilename.NumHosts * (strlen(lprofCurFilename.Hostname) - 2);
if (lprofCurFilename.MergePoolSize)
Len += SIGLEN;
+ for (I = 0; I < lprofCurFilename.NumExitSignals; ++I) {
+ Len -= 3; /* Drop the '%', signal number, and the 'x'. */
+ if (lprofCurFilename.ExitOnSignals[I] >= 10)
+ --Len; /* Drop the second digit of the signal number. */
+ }
return Len;
}
@@ -405,7 +463,7 @@
return 0;
if (!(lprofCurFilename.NumPids || lprofCurFilename.NumHosts ||
- lprofCurFilename.MergePoolSize))
+ lprofCurFilename.MergePoolSize || lprofCurFilename.NumExitSignals))
return lprofCurFilename.FilenamePat;
PidLength = strlen(lprofCurFilename.PidChars);
@@ -431,6 +489,9 @@
J += S;
if (FilenamePat[I] != 'm')
I++;
+ } else if (containsExitOnSignalSpecifier(FilenamePat, I)) {
+ while (FilenamePat[I] != 'x')
+ ++I;
}
/* Drop any unknown substitutions. */
} else
@@ -524,6 +585,7 @@
int rc, Length;
const char *Filename;
char *FilenameBuf;
+ int PDeathSig = 0;
if (lprofProfileDumped()) {
PROF_NOTE("Profile data not written to file: %s.\n",
@@ -550,10 +612,18 @@
return -1;
}
+ // Temporarily suspend getting SIGKILL when the parent exits.
+ PDeathSig = lprofSuspendSigKill();
+
/* Write profile data to the file. */
rc = writeFile(Filename);
if (rc)
PROF_ERR("Failed to write file \"%s\": %s\n", Filename, strerror(errno));
+
+ // Restore SIGKILL.
+ if (PDeathSig == 1)
+ lprofRestoreSigKill();
+
return rc;
}
diff --git a/lib/profile/InstrProfilingUtil.c b/lib/profile/InstrProfilingUtil.c
index 321c719..9b96309 100644
--- a/lib/profile/InstrProfilingUtil.c
+++ b/lib/profile/InstrProfilingUtil.c
@@ -26,9 +26,15 @@
#include <sys/utsname.h>
#endif
+#include <signal.h>
#include <stdlib.h>
#include <string.h>
+#if defined(__linux__)
+#include <signal.h>
+#include <sys/prctl.h>
+#endif
+
COMPILER_RT_VISIBILITY
void __llvm_profile_recursive_mkdir(char *path) {
int i;
@@ -219,3 +225,42 @@
#endif
return Sep;
}
+
+COMPILER_RT_VISIBILITY int lprofSuspendSigKill() {
+#if defined(__linux__)
+ int PDeachSig = 0;
+ /* Temporarily suspend getting SIGKILL upon exit of the parent process. */
+ if (prctl(PR_GET_PDEATHSIG, &PDeachSig) == 0 && PDeachSig == SIGKILL) {
+ fprintf(stderr, "set\n");
+ prctl(PR_SET_PDEATHSIG, 0);
+ }
+ return (PDeachSig == SIGKILL);
+#else
+ return 0;
+#endif
+}
+
+COMPILER_RT_VISIBILITY void lprofRestoreSigKill() {
+#if defined(__linux__)
+ fprintf(stderr, "restore \n");
+ prctl(PR_SET_PDEATHSIG, SIGKILL);
+#endif
+}
+
+COMPILER_RT_VISIBILITY void lprofInstallSignalHandler(int sig,
+ void (*handler)(int)) {
+#ifdef _WIN32
+ void (*err)(int) = signal(sig, handler);
+ if (err == SIG_ERR)
+ PROF_WARN("Unable to install an exit signal handler for %d (errno = %d).\n",
+ sig, errno);
+#else
+ struct sigaction sigact;
+ memset(&sigact, 0, sizeof(sigact));
+ sigact.sa_handler = handler;
+ int err = sigaction(sig, &sigact, NULL);
+ if (err)
+ PROF_WARN("Unable to install an exit signal handler for %d (errno = %d).\n",
+ sig, err);
+#endif
+}
diff --git a/lib/profile/InstrProfilingUtil.h b/lib/profile/InstrProfilingUtil.h
index a80fde7..f4b5b7d 100644
--- a/lib/profile/InstrProfilingUtil.h
+++ b/lib/profile/InstrProfilingUtil.h
@@ -51,4 +51,14 @@
unsigned lprofBoolCmpXchg(void **Ptr, void *OldV, void *NewV);
void *lprofPtrFetchAdd(void **Mem, long ByteIncr);
+/* Temporarily suspend SIGKILL. Return value of 1 means a restore is needed.
+ * Other return values mean no restore is needed.
+ */
+int lprofSuspendSigKill();
+
+/* Restore previously suspended SIGKILL. */
+void lprofRestoreSigKill();
+
+void lprofInstallSignalHandler(int sig, void(*handler)(int));
+
#endif /* PROFILE_INSTRPROFILINGUTIL_H */
diff --git a/lib/sanitizer_common/sanitizer_common.cc b/lib/sanitizer_common/sanitizer_common.cc
index 9824a519..5f492fe 100644
--- a/lib/sanitizer_common/sanitizer_common.cc
+++ b/lib/sanitizer_common/sanitizer_common.cc
@@ -199,23 +199,24 @@
return module;
}
-void ReportErrorSummary(const char *error_message) {
+void ReportErrorSummary(const char *error_message, const char *alt_tool_name) {
if (!common_flags()->print_summary)
return;
InternalScopedString buff(kMaxSummaryLength);
- buff.append("SUMMARY: %s: %s", SanitizerToolName, error_message);
+ buff.append("SUMMARY: %s: %s",
+ alt_tool_name ? alt_tool_name : SanitizerToolName, error_message);
__sanitizer_report_error_summary(buff.data());
}
#if !SANITIZER_GO
-void ReportErrorSummary(const char *error_type, const AddressInfo &info) {
- if (!common_flags()->print_summary)
- return;
+void ReportErrorSummary(const char *error_type, const AddressInfo &info,
+ const char *alt_tool_name) {
+ if (!common_flags()->print_summary) return;
InternalScopedString buff(kMaxSummaryLength);
buff.append("%s ", error_type);
RenderFrame(&buff, "%L %F", 0, info, common_flags()->symbolize_vs_style,
common_flags()->strip_path_prefix);
- ReportErrorSummary(buff.data());
+ ReportErrorSummary(buff.data(), alt_tool_name);
}
#endif
diff --git a/lib/sanitizer_common/sanitizer_common.h b/lib/sanitizer_common/sanitizer_common.h
index 2dabb50..e6d73e7 100644
--- a/lib/sanitizer_common/sanitizer_common.h
+++ b/lib/sanitizer_common/sanitizer_common.h
@@ -391,12 +391,16 @@
// Construct a one-line string:
// SUMMARY: SanitizerToolName: error_message
// and pass it to __sanitizer_report_error_summary.
-void ReportErrorSummary(const char *error_message);
+// If alt_tool_name is provided, it's used in place of SanitizerToolName.
+void ReportErrorSummary(const char *error_message,
+ const char *alt_tool_name = nullptr);
// Same as above, but construct error_message as:
// error_type file:line[:column][ function]
-void ReportErrorSummary(const char *error_type, const AddressInfo &info);
+void ReportErrorSummary(const char *error_type, const AddressInfo &info,
+ const char *alt_tool_name = nullptr);
// Same as above, but obtains AddressInfo by symbolizing top stack trace frame.
-void ReportErrorSummary(const char *error_type, const StackTrace *trace);
+void ReportErrorSummary(const char *error_type, const StackTrace *trace,
+ const char *alt_tool_name = nullptr);
// Math
#if SANITIZER_WINDOWS && !defined(__clang__) && !defined(__GNUC__)
diff --git a/lib/sanitizer_common/sanitizer_common_libcdep.cc b/lib/sanitizer_common/sanitizer_common_libcdep.cc
index 49ca961..3961aaf 100644
--- a/lib/sanitizer_common/sanitizer_common_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_common_libcdep.cc
@@ -47,7 +47,8 @@
sandboxing_callback = f;
}
-void ReportErrorSummary(const char *error_type, const StackTrace *stack) {
+void ReportErrorSummary(const char *error_type, const StackTrace *stack,
+ const char *alt_tool_name) {
#if !SANITIZER_GO
if (!common_flags()->print_summary)
return;
@@ -59,7 +60,7 @@
// Maybe sometimes we need to choose another frame (e.g. skip memcpy/etc).
uptr pc = StackTrace::GetPreviousInstructionPc(stack->trace[0]);
SymbolizedStack *frame = Symbolizer::GetOrInit()->SymbolizePC(pc);
- ReportErrorSummary(error_type, frame->info);
+ ReportErrorSummary(error_type, frame->info, alt_tool_name);
frame->ClearAll();
#endif
}
diff --git a/lib/sanitizer_common/sanitizer_coverage_libcdep.cc b/lib/sanitizer_common/sanitizer_coverage_libcdep.cc
index 5945ebb..a325b18 100644
--- a/lib/sanitizer_common/sanitizer_coverage_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_coverage_libcdep.cc
@@ -171,7 +171,11 @@
// - not thread-safe;
// - does not support long traces;
// - not tuned for performance.
- static const uptr kTrEventArrayMaxSize = FIRST_32_SECOND_64(1 << 22, 1 << 30);
+ // Windows doesn't do overcommit (committed virtual memory costs swap), so
+ // programs can't reliably map such large amounts of virtual memory.
+ // TODO(etienneb): Find a way to support coverage of larger executable
+static const uptr kTrEventArrayMaxSize =
+ (SANITIZER_WORDSIZE == 32 || SANITIZER_WINDOWS) ? 1 << 22 : 1 << 30;
u32 *tr_event_array;
uptr tr_event_array_size;
u32 *tr_event_pointer;
diff --git a/lib/sanitizer_common/sanitizer_thread_registry.cc b/lib/sanitizer_common/sanitizer_thread_registry.cc
index c2b75e6..c5b2e09 100644
--- a/lib/sanitizer_common/sanitizer_thread_registry.cc
+++ b/lib/sanitizer_common/sanitizer_thread_registry.cc
@@ -19,7 +19,7 @@
ThreadContextBase::ThreadContextBase(u32 tid)
: tid(tid), unique_id(0), reuse_count(), os_id(0), user_id(0),
status(ThreadStatusInvalid),
- detached(false), parent_tid(0), next(0) {
+ detached(false), workerthread(false), parent_tid(0), next(0) {
name[0] = '\0';
}
@@ -59,9 +59,10 @@
OnFinished();
}
-void ThreadContextBase::SetStarted(uptr _os_id, void *arg) {
+void ThreadContextBase::SetStarted(uptr _os_id, bool _workerthread, void *arg) {
status = ThreadStatusRunning;
os_id = _os_id;
+ workerthread = _workerthread;
OnStarted(arg);
}
@@ -266,14 +267,15 @@
}
}
-void ThreadRegistry::StartThread(u32 tid, uptr os_id, void *arg) {
+void ThreadRegistry::StartThread(u32 tid, uptr os_id, bool workerthread,
+ void *arg) {
BlockingMutexLock l(&mtx_);
running_threads_++;
CHECK_LT(tid, n_contexts_);
ThreadContextBase *tctx = threads_[tid];
CHECK_NE(tctx, 0);
CHECK_EQ(ThreadStatusCreated, tctx->status);
- tctx->SetStarted(os_id, arg);
+ tctx->SetStarted(os_id, workerthread, arg);
}
void ThreadRegistry::QuarantinePush(ThreadContextBase *tctx) {
diff --git a/lib/sanitizer_common/sanitizer_thread_registry.h b/lib/sanitizer_common/sanitizer_thread_registry.h
index a27bbb3..17b1d5d 100644
--- a/lib/sanitizer_common/sanitizer_thread_registry.h
+++ b/lib/sanitizer_common/sanitizer_thread_registry.h
@@ -45,6 +45,7 @@
ThreadStatus status;
bool detached;
+ bool workerthread;
u32 parent_tid;
ThreadContextBase *next; // For storing thread contexts in a list.
@@ -54,7 +55,7 @@
void SetDead();
void SetJoined(void *arg);
void SetFinished();
- void SetStarted(uptr _os_id, void *arg);
+ void SetStarted(uptr _os_id, bool _workerthread, void *arg);
void SetCreated(uptr _user_id, u64 _unique_id, bool _detached,
u32 _parent_tid, void *arg);
void Reset();
@@ -115,7 +116,7 @@
void DetachThread(u32 tid, void *arg);
void JoinThread(u32 tid, void *arg);
void FinishThread(u32 tid);
- void StartThread(u32 tid, uptr os_id, void *arg);
+ void StartThread(u32 tid, uptr os_id, bool workerthread, void *arg);
private:
const ThreadContextFactory context_factory_;
diff --git a/lib/sanitizer_common/tests/sanitizer_thread_registry_test.cc b/lib/sanitizer_common/tests/sanitizer_thread_registry_test.cc
index 1132bfd..f8b8c12 100644
--- a/lib/sanitizer_common/tests/sanitizer_thread_registry_test.cc
+++ b/lib/sanitizer_common/tests/sanitizer_thread_registry_test.cc
@@ -67,7 +67,7 @@
static void TestRegistry(ThreadRegistry *registry, bool has_quarantine) {
// Create and start a main thread.
EXPECT_EQ(0U, registry->CreateThread(get_uid(0), true, -1, 0));
- registry->StartThread(0, 0, 0);
+ registry->StartThread(0, 0, false, 0);
// Create a bunch of threads.
for (u32 i = 1; i <= 10; i++) {
EXPECT_EQ(i, registry->CreateThread(get_uid(i), is_detached(i), 0, 0));
@@ -75,7 +75,7 @@
CheckThreadQuantity(registry, 11, 1, 11);
// Start some of them.
for (u32 i = 1; i <= 5; i++) {
- registry->StartThread(i, 0, 0);
+ registry->StartThread(i, 0, false, 0);
}
CheckThreadQuantity(registry, 11, 6, 11);
// Finish, create and start more threads.
@@ -85,7 +85,7 @@
registry->JoinThread(i, 0);
}
for (u32 i = 6; i <= 10; i++) {
- registry->StartThread(i, 0, 0);
+ registry->StartThread(i, 0, false, 0);
}
std::vector<u32> new_tids;
for (u32 i = 11; i <= 15; i++) {
@@ -112,7 +112,7 @@
}
for (u32 i = 0; i < new_tids.size(); i++) {
u32 tid = new_tids[i];
- registry->StartThread(tid, 0, 0);
+ registry->StartThread(tid, 0, false, 0);
registry->DetachThread(tid, 0);
registry->FinishThread(tid);
}
@@ -189,7 +189,7 @@
tids.push_back(
args->registry->CreateThread(0, false, 0, (void*)args->shard));
for (int i = 0; i < kThreadsPerShard; i++)
- args->registry->StartThread(tids[i], 0, (void*)args->shard);
+ args->registry->StartThread(tids[i], 0, false, (void*)args->shard);
for (int i = 0; i < kThreadsPerShard; i++)
args->registry->FinishThread(tids[i]);
for (int i = 0; i < kThreadsPerShard; i++)
@@ -200,7 +200,7 @@
static void ThreadedTestRegistry(ThreadRegistry *registry) {
// Create and start a main thread.
EXPECT_EQ(0U, registry->CreateThread(0, true, -1, 0));
- registry->StartThread(0, 0, 0);
+ registry->StartThread(0, 0, false, 0);
pthread_t threads[kNumShards];
RunThreadArgs args[kNumShards];
for (int i = 0; i < kNumShards; i++) {
diff --git a/lib/tsan/CMakeLists.txt b/lib/tsan/CMakeLists.txt
index d519545..195ecb5 100644
--- a/lib/tsan/CMakeLists.txt
+++ b/lib/tsan/CMakeLists.txt
@@ -25,6 +25,7 @@
set(TSAN_SOURCES
rtl/tsan_clock.cc
rtl/tsan_debugging.cc
+ rtl/tsan_external.cc
rtl/tsan_fd.cc
rtl/tsan_flags.cc
rtl/tsan_ignoreset.cc
diff --git a/lib/tsan/go/tsan_go.cc b/lib/tsan/go/tsan_go.cc
index 34625c8..7fb4eb2 100644
--- a/lib/tsan/go/tsan_go.cc
+++ b/lib/tsan/go/tsan_go.cc
@@ -214,7 +214,7 @@
ThreadState *thr = AllocGoroutine();
*pthr = thr;
int goid = ThreadCreate(parent, (uptr)pc, 0, true);
- ThreadStart(thr, goid, 0);
+ ThreadStart(thr, goid, 0, /*workerthread*/ false);
}
void __tsan_go_end(ThreadState *thr) {
diff --git a/lib/tsan/rtl/tsan_debugging.cc b/lib/tsan/rtl/tsan_debugging.cc
index d9fb686..06154bc 100644
--- a/lib/tsan/rtl/tsan_debugging.cc
+++ b/lib/tsan/rtl/tsan_debugging.cc
@@ -24,6 +24,7 @@
if (typ == ReportTypeVptrRace) return "data-race-vptr";
if (typ == ReportTypeUseAfterFree) return "heap-use-after-free";
if (typ == ReportTypeVptrUseAfterFree) return "heap-use-after-free-vptr";
+ if (typ == ReportTypeExternalRace) return "external-race";
if (typ == ReportTypeThreadLeak) return "thread-leak";
if (typ == ReportTypeMutexDestroyLocked) return "locked-mutex-destroy";
if (typ == ReportTypeMutexDoubleLock) return "mutex-double-lock";
@@ -127,6 +128,16 @@
}
SANITIZER_INTERFACE_ATTRIBUTE
+int __tsan_get_report_loc_object_type(void *report, uptr idx,
+ const char **object_type) {
+ const ReportDesc *rep = (ReportDesc *)report;
+ CHECK_LT(idx, rep->locs.Size());
+ ReportLocation *loc = rep->locs[idx];
+ *object_type = GetObjectTypeFromTag(loc->external_tag);
+ return 1;
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
int __tsan_get_report_mutex(void *report, uptr idx, uptr *mutex_id, void **addr,
int *destroyed, void **trace, uptr trace_size) {
const ReportDesc *rep = (ReportDesc *)report;
diff --git a/lib/tsan/rtl/tsan_defs.h b/lib/tsan/rtl/tsan_defs.h
index 55580a5..8a0381e 100644
--- a/lib/tsan/rtl/tsan_defs.h
+++ b/lib/tsan/rtl/tsan_defs.h
@@ -149,7 +149,8 @@
// Descriptor of user's memory block.
struct MBlock {
- u64 siz;
+ u64 siz : 48;
+ u64 tag : 16;
u32 stk;
u16 tid;
};
diff --git a/lib/tsan/rtl/tsan_external.cc b/lib/tsan/rtl/tsan_external.cc
new file mode 100644
index 0000000..dc8ec62
--- /dev/null
+++ b/lib/tsan/rtl/tsan_external.cc
@@ -0,0 +1,78 @@
+//===-- tsan_external.cc --------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#include "tsan_rtl.h"
+
+namespace __tsan {
+
+#define CALLERPC ((uptr)__builtin_return_address(0))
+
+const uptr kMaxTag = 128; // Limited to 65,536, since MBlock only stores tags
+ // as 16-bit values, see tsan_defs.h.
+
+const char *registered_tags[kMaxTag];
+static atomic_uint32_t used_tags{1}; // Tag 0 means "no tag". NOLINT
+
+const char *GetObjectTypeFromTag(uptr tag) {
+ if (tag == 0) return nullptr;
+ // Invalid/corrupted tag? Better return NULL and let the caller deal with it.
+ if (tag >= atomic_load(&used_tags, memory_order_relaxed)) return nullptr;
+ return registered_tags[tag];
+}
+
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE
+void *__tsan_external_register_tag(const char *object_type) {
+ uptr new_tag = atomic_fetch_add(&used_tags, 1, memory_order_relaxed);
+ CHECK_LT(new_tag, kMaxTag);
+ registered_tags[new_tag] = internal_strdup(object_type);
+ return (void *)new_tag;
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_external_assign_tag(void *addr, void *tag) {
+ CHECK_LT(tag, atomic_load(&used_tags, memory_order_relaxed));
+ Allocator *a = allocator();
+ MBlock *b = nullptr;
+ if (a->PointerIsMine((void *)addr)) {
+ void *block_begin = a->GetBlockBegin((void *)addr);
+ if (block_begin) b = ctx->metamap.GetBlock((uptr)block_begin);
+ }
+ if (b) {
+ b->tag = (uptr)tag;
+ }
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_external_read(void *addr, void *caller_pc, void *tag) {
+ CHECK_LT(tag, atomic_load(&used_tags, memory_order_relaxed));
+ ThreadState *thr = cur_thread();
+ thr->external_tag = (uptr)tag;
+ FuncEntry(thr, (uptr)caller_pc);
+ MemoryRead(thr, CALLERPC, (uptr)addr, kSizeLog8);
+ FuncExit(thr);
+ thr->external_tag = 0;
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_external_write(void *addr, void *caller_pc, void *tag) {
+ CHECK_LT(tag, atomic_load(&used_tags, memory_order_relaxed));
+ ThreadState *thr = cur_thread();
+ thr->external_tag = (uptr)tag;
+ FuncEntry(thr, (uptr)caller_pc);
+ MemoryWrite(thr, CALLERPC, (uptr)addr, kSizeLog8);
+ FuncExit(thr);
+ thr->external_tag = 0;
+}
+} // extern "C"
+
+} // namespace __tsan
diff --git a/lib/tsan/rtl/tsan_flags.inc b/lib/tsan/rtl/tsan_flags.inc
index a48545c..e9b3e35 100644
--- a/lib/tsan/rtl/tsan_flags.inc
+++ b/lib/tsan/rtl/tsan_flags.inc
@@ -79,7 +79,7 @@
TSAN_FLAG(const char *, suppressions, "", "Suppressions file name.")
TSAN_FLAG(bool, ignore_interceptors_accesses, false,
"Ignore reads and writes from all interceptors.")
-TSAN_FLAG(bool, ignore_noninstrumented_modules, false,
+TSAN_FLAG(bool, ignore_noninstrumented_modules, SANITIZER_MAC ? true : false,
"Interceptors should only detect races when called from instrumented "
"modules.")
TSAN_FLAG(bool, shared_ptr_interceptor, true,
diff --git a/lib/tsan/rtl/tsan_interceptors.cc b/lib/tsan/rtl/tsan_interceptors.cc
index 898f32d..9bf1b28 100644
--- a/lib/tsan/rtl/tsan_interceptors.cc
+++ b/lib/tsan/rtl/tsan_interceptors.cc
@@ -881,7 +881,7 @@
internal_sched_yield();
Processor *proc = ProcCreate();
ProcWire(proc, thr);
- ThreadStart(thr, tid, GetTid());
+ ThreadStart(thr, tid, GetTid(), /*workerthread*/ false);
atomic_store(&p->tid, 0, memory_order_release);
}
void *res = callback(param);
diff --git a/lib/tsan/rtl/tsan_interceptors_mac.cc b/lib/tsan/rtl/tsan_interceptors_mac.cc
index fc5eb04..f6bf8a0 100644
--- a/lib/tsan/rtl/tsan_interceptors_mac.cc
+++ b/lib/tsan/rtl/tsan_interceptors_mac.cc
@@ -281,6 +281,12 @@
(connection, message, replyq, new_handler);
}
+TSAN_INTERCEPTOR(void, xpc_connection_cancel, xpc_connection_t connection) {
+ SCOPED_TSAN_INTERCEPTOR(xpc_connection_cancel, connection);
+ Release(thr, pc, (uptr)connection);
+ REAL(xpc_connection_cancel)(connection);
+}
+
// On macOS, libc++ is always linked dynamically, so intercepting works the
// usual way.
#define STDCXX_INTERCEPTOR TSAN_INTERCEPTOR
diff --git a/lib/tsan/rtl/tsan_interface.h b/lib/tsan/rtl/tsan_interface.h
index 4e342a5..496a871 100644
--- a/lib/tsan/rtl/tsan_interface.h
+++ b/lib/tsan/rtl/tsan_interface.h
@@ -79,6 +79,15 @@
SANITIZER_INTERFACE_ATTRIBUTE void __tsan_ignore_thread_end();
SANITIZER_INTERFACE_ATTRIBUTE
+void *__tsan_external_register_tag(const char *object_type);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_external_assign_tag(void *addr, void *tag);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_external_read(void *addr, void *caller_pc, void *tag);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_external_write(void *addr, void *caller_pc, void *tag);
+
+SANITIZER_INTERFACE_ATTRIBUTE
void __tsan_read_range(void *addr, unsigned long size); // NOLINT
SANITIZER_INTERFACE_ATTRIBUTE
void __tsan_write_range(void *addr, unsigned long size); // NOLINT
@@ -123,6 +132,10 @@
int *fd, int *suppressable, void **trace,
uptr trace_size);
+SANITIZER_INTERFACE_ATTRIBUTE
+int __tsan_get_report_loc_object_type(void *report, uptr idx,
+ const char **object_type);
+
// Returns information about mutexes included in the report.
SANITIZER_INTERFACE_ATTRIBUTE
int __tsan_get_report_mutex(void *report, uptr idx, uptr *mutex_id, void **addr,
diff --git a/lib/tsan/rtl/tsan_libdispatch_mac.cc b/lib/tsan/rtl/tsan_libdispatch_mac.cc
index d8c689e..8c759a3 100644
--- a/lib/tsan/rtl/tsan_libdispatch_mac.cc
+++ b/lib/tsan/rtl/tsan_libdispatch_mac.cc
@@ -93,14 +93,15 @@
new_context->free_context_in_callback = true;
new_context->submitted_synchronously = false;
new_context->is_barrier_block = false;
+ new_context->non_queue_sync_object = 0;
return new_context;
}
-#define GET_QUEUE_SYNC_VARS(context, q) \
- bool is_queue_serial = q && IsQueueSerial(q); \
- uptr sync_ptr = (uptr)q ?: context->non_queue_sync_object; \
- uptr serial_sync = (uptr)sync_ptr; \
- uptr concurrent_sync = ((uptr)sync_ptr) + sizeof(uptr); \
+#define GET_QUEUE_SYNC_VARS(context, q) \
+ bool is_queue_serial = q && IsQueueSerial(q); \
+ uptr sync_ptr = (uptr)q ?: context->non_queue_sync_object; \
+ uptr serial_sync = (uptr)sync_ptr; \
+ uptr concurrent_sync = sync_ptr ? ((uptr)sync_ptr) + sizeof(uptr) : 0; \
bool serial_task = context->is_barrier_block || is_queue_serial
static void dispatch_sync_pre_execute(ThreadState *thr, uptr pc,
@@ -111,8 +112,8 @@
dispatch_queue_t q = context->queue;
do {
GET_QUEUE_SYNC_VARS(context, q);
- Acquire(thr, pc, serial_sync);
- if (serial_task) Acquire(thr, pc, concurrent_sync);
+ if (serial_sync) Acquire(thr, pc, serial_sync);
+ if (serial_task && concurrent_sync) Acquire(thr, pc, concurrent_sync);
if (q) q = GetTargetQueueFromQueue(q);
} while (q);
@@ -126,7 +127,8 @@
dispatch_queue_t q = context->queue;
do {
GET_QUEUE_SYNC_VARS(context, q);
- Release(thr, pc, serial_task ? serial_sync : concurrent_sync);
+ if (serial_task && serial_sync) Release(thr, pc, serial_sync);
+ if (!serial_task && concurrent_sync) Release(thr, pc, concurrent_sync);
if (q) q = GetTargetQueueFromQueue(q);
} while (q);
diff --git a/lib/tsan/rtl/tsan_platform_mac.cc b/lib/tsan/rtl/tsan_platform_mac.cc
index 25dd241..b8d3d55 100644
--- a/lib/tsan/rtl/tsan_platform_mac.cc
+++ b/lib/tsan/rtl/tsan_platform_mac.cc
@@ -207,7 +207,7 @@
ThreadState *parent_thread_state = nullptr; // No parent.
int tid = ThreadCreate(parent_thread_state, 0, (uptr)thread, true);
CHECK_NE(tid, 0);
- ThreadStart(thr, tid, GetTid());
+ ThreadStart(thr, tid, GetTid(), /*workerthread*/ true);
}
} else if (event == PTHREAD_INTROSPECTION_THREAD_TERMINATE) {
if (thread == pthread_self()) {
diff --git a/lib/tsan/rtl/tsan_report.cc b/lib/tsan/rtl/tsan_report.cc
index 07fd412..7de0084 100644
--- a/lib/tsan/rtl/tsan_report.cc
+++ b/lib/tsan/rtl/tsan_report.cc
@@ -90,6 +90,8 @@
return "heap-use-after-free";
if (typ == ReportTypeVptrUseAfterFree)
return "heap-use-after-free (virtual call vs free)";
+ if (typ == ReportTypeExternalRace)
+ return "race on a library object";
if (typ == ReportTypeThreadLeak)
return "thread leak";
if (typ == ReportTypeMutexDestroyLocked)
@@ -152,14 +154,25 @@
: (write ? "Previous write" : "Previous read"));
}
+static const char *ExternalMopDesc(bool first, bool write) {
+ return first ? (write ? "Mutating" : "Read-only")
+ : (write ? "Previous mutating" : "Previous read-only");
+}
+
static void PrintMop(const ReportMop *mop, bool first) {
Decorator d;
char thrbuf[kThreadBufSize];
Printf("%s", d.Access());
- Printf(" %s of size %d at %p by %s",
- MopDesc(first, mop->write, mop->atomic),
- mop->size, (void*)mop->addr,
- thread_name(thrbuf, mop->tid));
+ const char *object_type = GetObjectTypeFromTag(mop->external_tag);
+ if (!object_type) {
+ Printf(" %s of size %d at %p by %s",
+ MopDesc(first, mop->write, mop->atomic), mop->size,
+ (void *)mop->addr, thread_name(thrbuf, mop->tid));
+ } else {
+ Printf(" %s access of object %s at %p by %s",
+ ExternalMopDesc(first, mop->write), object_type,
+ (void *)mop->addr, thread_name(thrbuf, mop->tid));
+ }
PrintMutexSet(mop->mset);
Printf(":\n");
Printf("%s", d.EndAccess());
@@ -183,9 +196,16 @@
global.module_offset);
} else if (loc->type == ReportLocationHeap) {
char thrbuf[kThreadBufSize];
- Printf(" Location is heap block of size %zu at %p allocated by %s:\n",
- loc->heap_chunk_size, loc->heap_chunk_start,
- thread_name(thrbuf, loc->tid));
+ const char *object_type = GetObjectTypeFromTag(loc->external_tag);
+ if (!object_type) {
+ Printf(" Location is heap block of size %zu at %p allocated by %s:\n",
+ loc->heap_chunk_size, loc->heap_chunk_start,
+ thread_name(thrbuf, loc->tid));
+ } else {
+ Printf(" Location is %s object of size %zu at %p allocated by %s:\n",
+ object_type, loc->heap_chunk_size, loc->heap_chunk_start,
+ thread_name(thrbuf, loc->tid));
+ }
print_stack = true;
} else if (loc->type == ReportLocationStack) {
Printf(" Location is stack of %s.\n\n", thread_name(thrbuf, loc->tid));
@@ -235,9 +255,15 @@
if (rt->name && rt->name[0] != '\0')
Printf(" '%s'", rt->name);
char thrbuf[kThreadBufSize];
- Printf(" (tid=%zu, %s) created by %s",
- rt->os_id, rt->running ? "running" : "finished",
- thread_name(thrbuf, rt->parent_tid));
+ const char *thread_status = rt->running ? "running" : "finished";
+ if (rt->workerthread) {
+ Printf(" (tid=%zu, %s) is a GCD worker thread\n", rt->os_id, thread_status);
+ Printf("\n");
+ Printf("%s", d.EndThreadDescription());
+ return;
+ }
+ Printf(" (tid=%zu, %s) created by %s", rt->os_id, thread_status,
+ thread_name(thrbuf, rt->parent_tid));
if (rt->stack)
Printf(" at:");
Printf("\n");
diff --git a/lib/tsan/rtl/tsan_report.h b/lib/tsan/rtl/tsan_report.h
index d0b9d74..8d8ae0f 100644
--- a/lib/tsan/rtl/tsan_report.h
+++ b/lib/tsan/rtl/tsan_report.h
@@ -24,6 +24,7 @@
ReportTypeVptrRace,
ReportTypeUseAfterFree,
ReportTypeVptrUseAfterFree,
+ ReportTypeExternalRace,
ReportTypeThreadLeak,
ReportTypeMutexDestroyLocked,
ReportTypeMutexDoubleLock,
@@ -56,6 +57,7 @@
int size;
bool write;
bool atomic;
+ uptr external_tag;
Vector<ReportMopMutex> mset;
ReportStack *stack;
@@ -75,6 +77,7 @@
DataInfo global;
uptr heap_chunk_start;
uptr heap_chunk_size;
+ uptr external_tag;
int tid;
int fd;
bool suppressable;
@@ -89,8 +92,9 @@
int id;
uptr os_id;
bool running;
+ bool workerthread;
char *name;
- int parent_tid;
+ u32 parent_tid;
ReportStack *stack;
};
diff --git a/lib/tsan/rtl/tsan_rtl.cc b/lib/tsan/rtl/tsan_rtl.cc
index bfb8358..bc5991c 100644
--- a/lib/tsan/rtl/tsan_rtl.cc
+++ b/lib/tsan/rtl/tsan_rtl.cc
@@ -381,7 +381,7 @@
// Initialize thread 0.
int tid = ThreadCreate(thr, 0, 0, true);
CHECK_EQ(tid, 0);
- ThreadStart(thr, tid, internal_getpid());
+ ThreadStart(thr, tid, GetTid(), /*workerthread*/ false);
#if TSAN_CONTAINS_UBSAN
__ubsan::InitAsPlugin();
#endif
diff --git a/lib/tsan/rtl/tsan_rtl.h b/lib/tsan/rtl/tsan_rtl.h
index 7fcb9d4..8853941 100644
--- a/lib/tsan/rtl/tsan_rtl.h
+++ b/lib/tsan/rtl/tsan_rtl.h
@@ -410,6 +410,7 @@
bool is_dead;
bool is_freeing;
bool is_vptr_access;
+ uptr external_tag;
const uptr stk_addr;
const uptr stk_size;
const uptr tls_addr;
@@ -564,7 +565,7 @@
explicit ScopedReport(ReportType typ);
~ScopedReport();
- void AddMemoryAccess(uptr addr, Shadow s, StackTrace stack,
+ void AddMemoryAccess(uptr addr, uptr external_tag, Shadow s, StackTrace stack,
const MutexSet *mset);
void AddStack(StackTrace stack, bool suppressable = false);
void AddThread(const ThreadContext *tctx, bool suppressable = false);
@@ -640,6 +641,8 @@
bool IsExpectedReport(uptr addr, uptr size);
void PrintMatchedBenignRaces();
+const char *GetObjectTypeFromTag(uptr tag);
+
#if defined(TSAN_DEBUG_OUTPUT) && TSAN_DEBUG_OUTPUT >= 1
# define DPrintf Printf
#else
@@ -713,7 +716,7 @@
void FuncExit(ThreadState *thr);
int ThreadCreate(ThreadState *thr, uptr pc, uptr uid, bool detached);
-void ThreadStart(ThreadState *thr, int tid, uptr os_id);
+void ThreadStart(ThreadState *thr, int tid, uptr os_id, bool workerthread);
void ThreadFinish(ThreadState *thr);
int ThreadTid(ThreadState *thr, uptr pc, uptr uid);
void ThreadJoin(ThreadState *thr, uptr pc, int tid);
diff --git a/lib/tsan/rtl/tsan_rtl_report.cc b/lib/tsan/rtl/tsan_rtl_report.cc
index bc8944f..31b9e97 100644
--- a/lib/tsan/rtl/tsan_rtl_report.cc
+++ b/lib/tsan/rtl/tsan_rtl_report.cc
@@ -164,8 +164,8 @@
(*rs)->suppressable = suppressable;
}
-void ScopedReport::AddMemoryAccess(uptr addr, Shadow s, StackTrace stack,
- const MutexSet *mset) {
+void ScopedReport::AddMemoryAccess(uptr addr, uptr external_tag, Shadow s,
+ StackTrace stack, const MutexSet *mset) {
void *mem = internal_alloc(MBlockReportMop, sizeof(ReportMop));
ReportMop *mop = new(mem) ReportMop;
rep_->mops.PushBack(mop);
@@ -175,6 +175,7 @@
mop->write = s.IsWrite();
mop->atomic = s.IsAtomic();
mop->stack = SymbolizeStack(stack);
+ mop->external_tag = external_tag;
if (mop->stack)
mop->stack->suppressable = true;
for (uptr i = 0; i < mset->Size(); i++) {
@@ -202,6 +203,7 @@
rt->running = (tctx->status == ThreadStatusRunning);
rt->name = internal_strdup(tctx->name);
rt->parent_tid = tctx->parent_tid;
+ rt->workerthread = tctx->workerthread;
rt->stack = 0;
rt->stack = SymbolizeStackId(tctx->creation_stack_id);
if (rt->stack)
@@ -336,6 +338,7 @@
ReportLocation *loc = ReportLocation::New(ReportLocationHeap);
loc->heap_chunk_start = (uptr)allocator()->GetBlockBegin((void *)addr);
loc->heap_chunk_size = b->siz;
+ loc->external_tag = b->tag;
loc->tid = tctx ? tctx->tid : b->tid;
loc->stack = SymbolizeStackId(b->stk);
rep_->locs.PushBack(loc);
@@ -622,6 +625,8 @@
typ = ReportTypeVptrRace;
else if (freed)
typ = ReportTypeUseAfterFree;
+ else if (thr->external_tag > 0)
+ typ = ReportTypeExternalRace;
if (IsFiredSuppression(ctx, typ, addr))
return;
@@ -650,7 +655,8 @@
ScopedReport rep(typ);
for (uptr i = 0; i < kMop; i++) {
Shadow s(thr->racy_state[i]);
- rep.AddMemoryAccess(addr, s, traces[i], i == 0 ? &thr->mset : mset2);
+ rep.AddMemoryAccess(addr, thr->external_tag, s, traces[i],
+ i == 0 ? &thr->mset : mset2);
}
for (uptr i = 0; i < kMop; i++) {
diff --git a/lib/tsan/rtl/tsan_rtl_thread.cc b/lib/tsan/rtl/tsan_rtl_thread.cc
index 5b17dc6..7357d97 100644
--- a/lib/tsan/rtl/tsan_rtl_thread.cc
+++ b/lib/tsan/rtl/tsan_rtl_thread.cc
@@ -236,7 +236,7 @@
return tid;
}
-void ThreadStart(ThreadState *thr, int tid, uptr os_id) {
+void ThreadStart(ThreadState *thr, int tid, uptr os_id, bool workerthread) {
uptr stk_addr = 0;
uptr stk_size = 0;
uptr tls_addr = 0;
@@ -266,7 +266,7 @@
ThreadRegistry *tr = ctx->thread_registry;
OnStartedArgs args = { thr, stk_addr, stk_size, tls_addr, tls_size };
- tr->StartThread(tid, os_id, &args);
+ tr->StartThread(tid, os_id, workerthread, &args);
tr->Lock();
thr->tctx = (ThreadContext*)tr->GetThreadLocked(tid);
diff --git a/lib/tsan/rtl/tsan_suppressions.cc b/lib/tsan/rtl/tsan_suppressions.cc
index bfb64e0..e39702b 100644
--- a/lib/tsan/rtl/tsan_suppressions.cc
+++ b/lib/tsan/rtl/tsan_suppressions.cc
@@ -74,6 +74,8 @@
return kSuppressionRace;
else if (typ == ReportTypeVptrUseAfterFree)
return kSuppressionRace;
+ else if (typ == ReportTypeExternalRace)
+ return kSuppressionRace;
else if (typ == ReportTypeThreadLeak)
return kSuppressionThread;
else if (typ == ReportTypeMutexDestroyLocked)
diff --git a/lib/tsan/rtl/tsan_sync.cc b/lib/tsan/rtl/tsan_sync.cc
index 44c6a26..2be0474 100644
--- a/lib/tsan/rtl/tsan_sync.cc
+++ b/lib/tsan/rtl/tsan_sync.cc
@@ -64,6 +64,7 @@
u32 idx = block_alloc_.Alloc(&thr->proc()->block_cache);
MBlock *b = block_alloc_.Map(idx);
b->siz = sz;
+ b->tag = 0;
b->tid = thr->tid;
b->stk = CurrentStackId(thr, pc);
u32 *meta = MemToMeta(p);
diff --git a/lib/ubsan/ubsan_diag.cc b/lib/ubsan/ubsan_diag.cc
index c531c5f..742802b 100644
--- a/lib/ubsan/ubsan_diag.cc
+++ b/lib/ubsan/ubsan_diag.cc
@@ -31,15 +31,16 @@
// will definitely be called when we print the first diagnostics message.
if (!flags()->print_stacktrace)
return;
- // We can only use slow unwind, as we don't have any information about stack
- // top/bottom.
- // FIXME: It's better to respect "fast_unwind_on_fatal" runtime flag and
- // fetch stack top/bottom information if we have it (e.g. if we're running
- // under ASan).
- if (StackTrace::WillUseFastUnwind(false))
- return;
+
+ uptr top = 0;
+ uptr bottom = 0;
+ bool request_fast_unwind = common_flags()->fast_unwind_on_fatal;
+ if (request_fast_unwind)
+ __sanitizer::GetThreadStackTopAndBottom(false, &top, &bottom);
+
BufferedStackTrace stack;
- stack.Unwind(kStackTraceMax, pc, bp, 0, 0, 0, false);
+ stack.Unwind(kStackTraceMax, pc, bp, nullptr, top, bottom,
+ request_fast_unwind);
stack.Print();
}
@@ -79,16 +80,16 @@
AI.line = SLoc.getLine();
AI.column = SLoc.getColumn();
AI.function = internal_strdup(""); // Avoid printing ?? as function name.
- ReportErrorSummary(ErrorKind, AI);
+ ReportErrorSummary(ErrorKind, AI, GetSanititizerToolName());
AI.Clear();
return;
}
} else if (Loc.isSymbolizedStack()) {
const AddressInfo &AI = Loc.getSymbolizedStack()->info;
- ReportErrorSummary(ErrorKind, AI);
+ ReportErrorSummary(ErrorKind, AI, GetSanititizerToolName());
return;
}
- ReportErrorSummary(ErrorKind);
+ ReportErrorSummary(ErrorKind, GetSanititizerToolName());
}
namespace {
diff --git a/lib/ubsan/ubsan_flags.cc b/lib/ubsan/ubsan_flags.cc
index e77ba55..21e2884 100644
--- a/lib/ubsan/ubsan_flags.cc
+++ b/lib/ubsan/ubsan_flags.cc
@@ -45,6 +45,7 @@
CommonFlags cf;
cf.CopyFrom(*common_flags());
cf.print_summary = false;
+ cf.external_symbolizer_path = GetEnv("UBSAN_SYMBOLIZER_PATH");
OverrideCommonFlags(cf);
}
diff --git a/lib/ubsan/ubsan_handlers.cc b/lib/ubsan/ubsan_handlers.cc
index 6ffffae..d6a8f52 100644
--- a/lib/ubsan/ubsan_handlers.cc
+++ b/lib/ubsan/ubsan_handlers.cc
@@ -38,7 +38,7 @@
const char *TypeCheckKinds[] = {
"load of", "store to", "reference binding to", "member access within",
"member call on", "constructor call on", "downcast of", "downcast of",
- "upcast of", "cast to virtual base of"};
+ "upcast of", "cast to virtual base of", "_Nonnull binding to"};
}
static void handleTypeMismatchImpl(TypeMismatchData *Data, ValueHandle Pointer,
@@ -390,7 +390,7 @@
ScopedReport R(Opts, Loc, ET);
Diag(Loc, DL_Error,
- "value %0 is outside the range of representable values of type %2")
+ "%0 is outside the range of representable values of type %2")
<< Value(*FromType, From) << *FromType << *ToType;
}
@@ -410,7 +410,8 @@
SourceLocation Loc = Data->Loc.acquire();
// This check could be more precise if we used different handlers for
// -fsanitize=bool and -fsanitize=enum.
- bool IsBool = (0 == internal_strcmp(Data->Type.getTypeName(), "'bool'"));
+ bool IsBool = (0 == internal_strcmp(Data->Type.getTypeName(), "'bool'")) ||
+ (0 == internal_strncmp(Data->Type.getTypeName(), "'BOOL'", 6));
ErrorType ET =
IsBool ? ErrorType::InvalidBoolLoad : ErrorType::InvalidEnumLoad;
@@ -472,7 +473,8 @@
Die();
}
-static void handleNonNullReturn(NonNullReturnData *Data, ReportOptions Opts) {
+static void handleNonNullReturn(NonNullReturnData *Data, ReportOptions Opts,
+ bool IsAttr) {
SourceLocation Loc = Data->Loc.acquire();
ErrorType ET = ErrorType::InvalidNullReturn;
@@ -484,21 +486,35 @@
Diag(Loc, DL_Error, "null pointer returned from function declared to never "
"return null");
if (!Data->AttrLoc.isInvalid())
- Diag(Data->AttrLoc, DL_Note, "returns_nonnull attribute specified here");
+ Diag(Data->AttrLoc, DL_Note, "%0 specified here")
+ << (IsAttr ? "returns_nonnull attribute"
+ : "_Nonnull return type annotation");
}
void __ubsan::__ubsan_handle_nonnull_return(NonNullReturnData *Data) {
GET_REPORT_OPTIONS(false);
- handleNonNullReturn(Data, Opts);
+ handleNonNullReturn(Data, Opts, true);
}
void __ubsan::__ubsan_handle_nonnull_return_abort(NonNullReturnData *Data) {
GET_REPORT_OPTIONS(true);
- handleNonNullReturn(Data, Opts);
+ handleNonNullReturn(Data, Opts, true);
Die();
}
-static void handleNonNullArg(NonNullArgData *Data, ReportOptions Opts) {
+void __ubsan::__ubsan_handle_nullability_return(NonNullReturnData *Data) {
+ GET_REPORT_OPTIONS(false);
+ handleNonNullReturn(Data, Opts, false);
+}
+
+void __ubsan::__ubsan_handle_nullability_return_abort(NonNullReturnData *Data) {
+ GET_REPORT_OPTIONS(true);
+ handleNonNullReturn(Data, Opts, false);
+ Die();
+}
+
+static void handleNonNullArg(NonNullArgData *Data, ReportOptions Opts,
+ bool IsAttr) {
SourceLocation Loc = Data->Loc.acquire();
ErrorType ET = ErrorType::InvalidNullArgument;
@@ -507,20 +523,34 @@
ScopedReport R(Opts, Loc, ET);
- Diag(Loc, DL_Error, "null pointer passed as argument %0, which is declared to "
- "never be null") << Data->ArgIndex;
+ Diag(Loc, DL_Error,
+ "null pointer passed as argument %0, which is declared to "
+ "never be null")
+ << Data->ArgIndex;
if (!Data->AttrLoc.isInvalid())
- Diag(Data->AttrLoc, DL_Note, "nonnull attribute specified here");
+ Diag(Data->AttrLoc, DL_Note, "%0 specified here")
+ << (IsAttr ? "nonnull attribute" : "_Nonnull type annotation");
}
void __ubsan::__ubsan_handle_nonnull_arg(NonNullArgData *Data) {
GET_REPORT_OPTIONS(false);
- handleNonNullArg(Data, Opts);
+ handleNonNullArg(Data, Opts, true);
}
void __ubsan::__ubsan_handle_nonnull_arg_abort(NonNullArgData *Data) {
GET_REPORT_OPTIONS(true);
- handleNonNullArg(Data, Opts);
+ handleNonNullArg(Data, Opts, true);
+ Die();
+}
+
+void __ubsan::__ubsan_handle_nullability_arg(NonNullArgData *Data) {
+ GET_REPORT_OPTIONS(false);
+ handleNonNullArg(Data, Opts, false);
+}
+
+void __ubsan::__ubsan_handle_nullability_arg_abort(NonNullArgData *Data) {
+ GET_REPORT_OPTIONS(true);
+ handleNonNullArg(Data, Opts, false);
Die();
}
diff --git a/lib/ubsan/ubsan_handlers.h b/lib/ubsan/ubsan_handlers.h
index 350eb91..5857bc2 100644
--- a/lib/ubsan/ubsan_handlers.h
+++ b/lib/ubsan/ubsan_handlers.h
@@ -136,8 +136,10 @@
SourceLocation AttrLoc;
};
-/// \brief Handle returning null from function with returns_nonnull attribute.
+/// \brief Handle returning null from function with the returns_nonnull
+/// attribute, or a return type annotated with _Nonnull.
RECOVERABLE(nonnull_return, NonNullReturnData *Data)
+RECOVERABLE(nullability_return, NonNullReturnData *Data)
struct NonNullArgData {
SourceLocation Loc;
@@ -145,8 +147,10 @@
int ArgIndex;
};
-/// \brief Handle passing null pointer to function with nonnull attribute.
+/// \brief Handle passing null pointer to a function parameter with the nonnull
+/// attribute, or a _Nonnull type annotation.
RECOVERABLE(nonnull_arg, NonNullArgData *Data)
+RECOVERABLE(nullability_arg, NonNullArgData *Data)
/// \brief Known CFI check kinds.
/// Keep in sync with the enum of the same name in CodeGenFunction.h
diff --git a/lib/ubsan/ubsan_init.cc b/lib/ubsan/ubsan_init.cc
index b4f42c4..307bca3 100644
--- a/lib/ubsan/ubsan_init.cc
+++ b/lib/ubsan/ubsan_init.cc
@@ -23,6 +23,10 @@
using namespace __ubsan;
+const char *__ubsan::GetSanititizerToolName() {
+ return "UndefinedBehaviorSanitizer";
+}
+
static enum {
UBSAN_MODE_UNKNOWN = 0,
UBSAN_MODE_STANDALONE,
@@ -35,7 +39,7 @@
}
static void CommonStandaloneInit() {
- SanitizerToolName = "UndefinedBehaviorSanitizer";
+ SanitizerToolName = GetSanititizerToolName();
InitializeFlags();
CacheBinaryName();
__sanitizer_set_report_path(common_flags()->log_path);
diff --git a/lib/ubsan/ubsan_init.h b/lib/ubsan/ubsan_init.h
index 103ae24..f12fc2c 100644
--- a/lib/ubsan/ubsan_init.h
+++ b/lib/ubsan/ubsan_init.h
@@ -15,6 +15,9 @@
namespace __ubsan {
+// Get the full tool name for UBSan.
+const char *GetSanititizerToolName();
+
// Initialize UBSan as a standalone tool. Typically should be called early
// during initialization.
void InitAsStandalone();
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 9b9c515..addc579 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -5,9 +5,8 @@
${CMAKE_CURRENT_SOURCE_DIR}/lit.common.configured.in
${CMAKE_CURRENT_BINARY_DIR}/lit.common.configured)
-# BlocksRuntime and builtins testsuites are not yet ported to lit.
+# BlocksRuntime (and most of builtins) testsuites are not yet ported to lit.
# add_subdirectory(BlocksRuntime)
-# add_subdirectory(builtins)
set(SANITIZER_COMMON_LIT_TEST_DEPS)
if(COMPILER_RT_STANDALONE_BUILD)
@@ -39,6 +38,9 @@
# Run sanitizer tests only if we're sure that clang would produce
# working binaries.
if(COMPILER_RT_CAN_EXECUTE_TESTS)
+ if(COMPILER_RT_BUILD_BUILTINS)
+ add_subdirectory(builtins)
+ endif()
if(COMPILER_RT_HAS_ASAN)
add_subdirectory(asan)
endif()
diff --git a/test/asan/TestCases/Darwin/scribble.cc b/test/asan/TestCases/Darwin/scribble.cc
new file mode 100644
index 0000000..33f64e1
--- /dev/null
+++ b/test/asan/TestCases/Darwin/scribble.cc
@@ -0,0 +1,57 @@
+// RUN: %clang_asan -O2 %s -o %t
+// RUN: %run %t 2>&1 | FileCheck --check-prefix=CHECK-NOSCRIBBLE %s
+// RUN: env MallocScribble=1 MallocPreScribble=1 %run %t 2>&1 | FileCheck --check-prefix=CHECK-SCRIBBLE %s
+// RUN: %env_asan_opts=max_free_fill_size=4096 %run %t 2>&1 | FileCheck --check-prefix=CHECK-SCRIBBLE %s
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct Isa {
+ const char *class_name;
+};
+
+struct MyClass {
+ long padding;
+ Isa *isa;
+ long data;
+
+ void print_my_class_name();
+};
+
+__attribute__((no_sanitize("address")))
+void MyClass::print_my_class_name() {
+ fprintf(stderr, "this = %p\n", this);
+ fprintf(stderr, "padding = 0x%lx\n", this->padding);
+ fprintf(stderr, "isa = %p\n", this->isa);
+
+ if ((uint32_t)(uintptr_t)this->isa != 0x55555555) {
+ fprintf(stderr, "class name: %s\n", this->isa->class_name);
+ }
+}
+
+int main() {
+ Isa *my_class_isa = (Isa *)malloc(sizeof(Isa));
+ memset(my_class_isa, 0x77, sizeof(Isa));
+ my_class_isa->class_name = "MyClass";
+
+ MyClass *my_object = (MyClass *)malloc(sizeof(MyClass));
+ memset(my_object, 0x88, sizeof(MyClass));
+ my_object->isa = my_class_isa;
+ my_object->data = 42;
+
+ my_object->print_my_class_name();
+ // CHECK-SCRIBBLE: class name: MyClass
+ // CHECK-NOSCRIBBLE: class name: MyClass
+
+ free(my_object);
+
+ my_object->print_my_class_name();
+ // CHECK-NOSCRIBBLE: class name: MyClass
+ // CHECK-SCRIBBLE: isa = {{(0x)?}}{{5555555555555555|55555555}}
+
+ fprintf(stderr, "okthxbai!\n");
+ // CHECK-SCRIBBLE: okthxbai!
+ // CHECK-NOSCRIBBLE: okthxbai!
+}
diff --git a/test/asan/TestCases/Linux/memmem_test.cc b/test/asan/TestCases/Linux/memmem_test.cc
index 661381c..a838cb5 100644
--- a/test/asan/TestCases/Linux/memmem_test.cc
+++ b/test/asan/TestCases/Linux/memmem_test.cc
@@ -15,10 +15,10 @@
// A1: AddressSanitizer: stack-buffer-overflow
// A1: {{#0.*memmem}}
// A1-NEXT: {{#1.*main}}
- // A1: 'a1' <== Memory access at offset
+ // A1: 'a1'{{.*}} <== Memory access at offset
//
// A2: AddressSanitizer: stack-buffer-overflow
// A2: {{#0.*memmem}}
- // A2: 'a2' <== Memory access at offset
+ // A2: 'a2'{{.*}} <== Memory access at offset
return res == NULL;
}
diff --git a/test/asan/TestCases/Posix/stack-use-after-return.cc b/test/asan/TestCases/Posix/stack-use-after-return.cc
index cf114be..7c03037 100644
--- a/test/asan/TestCases/Posix/stack-use-after-return.cc
+++ b/test/asan/TestCases/Posix/stack-use-after-return.cc
@@ -48,11 +48,11 @@
// CHECK: WRITE of size 1 {{.*}} thread T0
// CHECK: #0{{.*}}Func2{{.*}}stack-use-after-return.cc:[[@LINE-2]]
// CHECK: is located in stack of thread T0 at offset
- // CHECK: 'local' <== Memory access at offset {{16|32}} is inside this variable
+ // CHECK: 'local'{{.*}} <== Memory access at offset {{16|32}} is inside this variable
// THREAD: WRITE of size 1 {{.*}} thread T{{[1-9]}}
// THREAD: #0{{.*}}Func2{{.*}}stack-use-after-return.cc:[[@LINE-6]]
// THREAD: is located in stack of thread T{{[1-9]}} at offset
- // THREAD: 'local' <== Memory access at offset {{16|32}} is inside this variable
+ // THREAD: 'local'{{.*}} <== Memory access at offset {{16|32}} is inside this variable
// CHECK-20: T0: FakeStack created:{{.*}} stack_size_log: 20
// CHECK-24: T0: FakeStack created:{{.*}} stack_size_log: 24
}
diff --git a/test/asan/TestCases/Windows/dll_intercept_memchr.cc b/test/asan/TestCases/Windows/dll_intercept_memchr.cc
index 4f794a2..6360cec 100644
--- a/test/asan/TestCases/Windows/dll_intercept_memchr.cc
+++ b/test/asan/TestCases/Windows/dll_intercept_memchr.cc
@@ -22,6 +22,6 @@
// CHECK-NEXT: test_function {{.*}}dll_intercept_memchr.cc:[[@LINE-5]]
// CHECK: Address [[ADDR]] is located in stack of thread T0 at offset {{.*}} in frame
// CHECK-NEXT: test_function {{.*}}dll_intercept_memchr.cc
-// CHECK: 'buff' <== Memory access at offset {{.*}} overflows this variable
+// CHECK: 'buff'{{.*}} <== Memory access at offset {{.*}} overflows this variable
return 0;
}
diff --git a/test/asan/TestCases/Windows/dll_intercept_memcpy.cc b/test/asan/TestCases/Windows/dll_intercept_memcpy.cc
index 736e696..a5981fa 100644
--- a/test/asan/TestCases/Windows/dll_intercept_memcpy.cc
+++ b/test/asan/TestCases/Windows/dll_intercept_memcpy.cc
@@ -27,6 +27,6 @@
// CHECK-NEXT: test_function {{.*}}dll_intercept_memcpy.cc:[[@LINE-4]]
// CHECK: Address [[ADDR]] is located in stack of thread T0 at offset {{.*}} in frame
// CHECK-NEXT: test_function {{.*}}dll_intercept_memcpy.cc
-// CHECK: 'buff2' <== Memory access at offset {{.*}} overflows this variable
+// CHECK: 'buff2'{{.*}} <== Memory access at offset {{.*}} overflows this variable
return 0;
}
diff --git a/test/asan/TestCases/Windows/dll_intercept_memcpy_indirect.cc b/test/asan/TestCases/Windows/dll_intercept_memcpy_indirect.cc
index 4e28905..f05ee21 100644
--- a/test/asan/TestCases/Windows/dll_intercept_memcpy_indirect.cc
+++ b/test/asan/TestCases/Windows/dll_intercept_memcpy_indirect.cc
@@ -29,6 +29,6 @@
// CHECK-NEXT: test_function {{.*}}dll_intercept_memcpy_indirect.cc:[[@LINE-5]]
// CHECK: Address [[ADDR]] is located in stack of thread T0 at offset {{.*}} in frame
// CHECK-NEXT: test_function {{.*}}dll_intercept_memcpy_indirect.cc
-// CHECK: 'buff2' <== Memory access at offset {{.*}} overflows this variable
+// CHECK: 'buff2'{{.*}} <== Memory access at offset {{.*}} overflows this variable
return 0;
}
diff --git a/test/asan/TestCases/Windows/dll_intercept_memset.cc b/test/asan/TestCases/Windows/dll_intercept_memset.cc
index d4be376..4baa0a1 100644
--- a/test/asan/TestCases/Windows/dll_intercept_memset.cc
+++ b/test/asan/TestCases/Windows/dll_intercept_memset.cc
@@ -27,6 +27,6 @@
// CHECK-NEXT: test_function {{.*}}dll_intercept_memset.cc:[[@LINE-4]]
// CHECK: Address [[ADDR]] is located in stack of thread T0 at offset {{.*}} in frame
// CHECK-NEXT: test_function {{.*}}dll_intercept_memset.cc
-// CHECK: 'buff' <== Memory access at offset {{.*}} overflows this variable
+// CHECK: 'buff'{{.*}} <== Memory access at offset {{.*}} overflows this variable
return 0;
}
diff --git a/test/asan/TestCases/Windows/dll_noreturn.cc b/test/asan/TestCases/Windows/dll_noreturn.cc
index 8b5e3d0..2f6f0c7 100644
--- a/test/asan/TestCases/Windows/dll_noreturn.cc
+++ b/test/asan/TestCases/Windows/dll_noreturn.cc
@@ -17,7 +17,7 @@
//
// CHECK: Address [[ADDR]] is located in stack of thread T0 at offset [[OFFSET:.*]] in frame
// CHECK-NEXT: noreturn_f{{.*}}dll_noreturn.cc
-// CHECK: 'buffer' <== Memory access at offset [[OFFSET]] underflows this variable
+// CHECK: 'buffer'{{.*}} <== Memory access at offset [[OFFSET]] underflows this variable
// CHECK-LABEL: SUMMARY
}
diff --git a/test/asan/TestCases/Windows/dll_poison_unpoison.cc b/test/asan/TestCases/Windows/dll_poison_unpoison.cc
index 9b25a12..6bd58ec 100644
--- a/test/asan/TestCases/Windows/dll_poison_unpoison.cc
+++ b/test/asan/TestCases/Windows/dll_poison_unpoison.cc
@@ -30,6 +30,6 @@
//
// CHECK: [[ADDR]] is located in stack of thread T0 at offset [[OFFSET:.*]] in frame
// CHECK-NEXT: test_function{{.*}}\dll_poison_unpoison.cc
-// CHECK: 'buffer' <== Memory access at offset [[OFFSET]] is inside this variable
+// CHECK: 'buffer'{{.*}} <== Memory access at offset [[OFFSET]] is inside this variable
return 0;
}
diff --git a/test/asan/TestCases/Windows/dll_stack_use_after_return.cc b/test/asan/TestCases/Windows/dll_stack_use_after_return.cc
index 6428718..b6166d6 100644
--- a/test/asan/TestCases/Windows/dll_stack_use_after_return.cc
+++ b/test/asan/TestCases/Windows/dll_stack_use_after_return.cc
@@ -22,7 +22,7 @@
//
// CHECK: Address [[ADDR]] is located in stack of thread T0 at offset [[OFFSET:.*]] in frame
// CHECK-NEXT: #0 {{.*}} foo{{.*}}dll_stack_use_after_return.cc
-// CHECK: 'stack_buffer' <== Memory access at offset [[OFFSET]] is inside this variable
+// CHECK: 'stack_buffer'{{.*}} <== Memory access at offset [[OFFSET]] is inside this variable
return 0;
}
diff --git a/test/asan/TestCases/Windows/dll_thread_stack_array_left_oob.cc b/test/asan/TestCases/Windows/dll_thread_stack_array_left_oob.cc
index dc7c7c6..75a094e 100644
--- a/test/asan/TestCases/Windows/dll_thread_stack_array_left_oob.cc
+++ b/test/asan/TestCases/Windows/dll_thread_stack_array_left_oob.cc
@@ -16,7 +16,7 @@
// CHECK: Address [[ADDR]] is located in stack of thread T1 at offset [[OFFSET:.*]] in frame
// CHECK-NEXT: thread_proc{{.*}}dll_thread_stack_array_left_oob.cc
//
-// CHECK: 'stack_buffer' <== Memory access at offset [[OFFSET]] underflows this variable
+// CHECK: 'stack_buffer'{{.*}} <== Memory access at offset [[OFFSET]] underflows this variable
return 0;
}
diff --git a/test/asan/TestCases/Windows/intercept_memcpy.cc b/test/asan/TestCases/Windows/intercept_memcpy.cc
index 6e45e7f..d71333d 100644
--- a/test/asan/TestCases/Windows/intercept_memcpy.cc
+++ b/test/asan/TestCases/Windows/intercept_memcpy.cc
@@ -27,5 +27,5 @@
// CHECK-NEXT: main {{.*}}intercept_memcpy.cc:[[@LINE-5]]
// CHECK: Address [[ADDR]] is located in stack of thread T0 at offset {{.*}} in frame
// CHECK-NEXT: #0 {{.*}} main
-// CHECK: 'buff2' <== Memory access at offset {{.*}} overflows this variable
+// CHECK: 'buff2'{{.*}} <== Memory access at offset {{.*}} overflows this variable
}
diff --git a/test/asan/TestCases/Windows/intercept_strlen.cc b/test/asan/TestCases/Windows/intercept_strlen.cc
index 928a286..938e6c9 100644
--- a/test/asan/TestCases/Windows/intercept_strlen.cc
+++ b/test/asan/TestCases/Windows/intercept_strlen.cc
@@ -22,6 +22,6 @@
// CHECK-NEXT: main {{.*}}intercept_strlen.cc:[[@LINE-5]]
// CHECK: Address [[ADDR]] is located in stack of thread T0 at offset {{.*}} in frame
// CHECK-NEXT: main {{.*}}intercept_strlen.cc
-// CHECK: 'str' <== Memory access at offset {{.*}} overflows this variable
+// CHECK: 'str'{{.*}} <== Memory access at offset {{.*}} overflows this variable
return len < 6;
}
diff --git a/test/asan/TestCases/Windows/stack_array_left_oob.cc b/test/asan/TestCases/Windows/stack_array_left_oob.cc
index 845a1f3..8d601fc 100644
--- a/test/asan/TestCases/Windows/stack_array_left_oob.cc
+++ b/test/asan/TestCases/Windows/stack_array_left_oob.cc
@@ -12,5 +12,5 @@
// CHECK-NEXT: {{#0 .* main .*stack_array_left_oob.cc}}:[[@LINE-3]]
// CHECK: Address [[ADDR]] is located in stack of thread T0 at offset [[OFFSET:.*]] in frame
// CHECK-NEXT: {{#0 .* main .*stack_array_left_oob.cc}}
-// CHECK: 'buffer' <== Memory access at offset [[OFFSET]] underflows this variable
+// CHECK: 'buffer'{{.*}} <== Memory access at offset [[OFFSET]] underflows this variable
}
diff --git a/test/asan/TestCases/Windows/stack_array_right_oob.cc b/test/asan/TestCases/Windows/stack_array_right_oob.cc
index a370246..721834d 100644
--- a/test/asan/TestCases/Windows/stack_array_right_oob.cc
+++ b/test/asan/TestCases/Windows/stack_array_right_oob.cc
@@ -12,5 +12,5 @@
// CHECK-NEXT: {{#0 .* main .*stack_array_right_oob.cc}}:[[@LINE-3]]
// CHECK: Address [[ADDR]] is located in stack of thread T0 at offset [[OFFSET:.*]] in frame
// CHECK-NEXT: {{#0 .* main .*stack_array_right_oob.cc}}
-// CHECK: 'buffer' <== Memory access at offset [[OFFSET]] overflows this variable
+// CHECK: 'buffer'{{.*}} <== Memory access at offset [[OFFSET]] overflows this variable
}
diff --git a/test/asan/TestCases/Windows/stack_use_after_return.cc b/test/asan/TestCases/Windows/stack_use_after_return.cc
index 9c31922..ca1c142 100644
--- a/test/asan/TestCases/Windows/stack_use_after_return.cc
+++ b/test/asan/TestCases/Windows/stack_use_after_return.cc
@@ -18,5 +18,5 @@
// CHECK: is located in stack of thread T0 at offset [[OFFSET:.*]] in frame
// CHECK-NEXT: {{#0 0x.* in foo.*stack_use_after_return.cc}}
//
-// CHECK: 'stack_buffer' <== Memory access at offset [[OFFSET]] is inside this variable
+// CHECK: 'stack_buffer'{{.*}} <== Memory access at offset [[OFFSET]] is inside this variable
}
diff --git a/test/asan/TestCases/Windows/wrong_downcast_on_stack.cc b/test/asan/TestCases/Windows/wrong_downcast_on_stack.cc
index 2859ecc..7848cf3 100644
--- a/test/asan/TestCases/Windows/wrong_downcast_on_stack.cc
+++ b/test/asan/TestCases/Windows/wrong_downcast_on_stack.cc
@@ -20,7 +20,7 @@
// CHECK-NEXT: {{#0 0x[0-9a-f]* in main .*wrong_downcast_on_stack.cc}}:[[@LINE-3]]
// CHECK: [[ADDR]] is located in stack of thread T0 at offset [[OFFSET:[0-9]+]] in frame
// CHECK-NEXT: {{#0 0x[0-9a-f]* in main }}
-// CHECK: 'p' <== Memory access at offset [[OFFSET]] overflows this variable
+// CHECK: 'p'{{.*}} <== Memory access at offset [[OFFSET]] overflows this variable
return 0;
}
diff --git a/test/asan/TestCases/stack-buffer-overflow-with-position.cc b/test/asan/TestCases/stack-buffer-overflow-with-position.cc
index 88f5825..2a575f7 100644
--- a/test/asan/TestCases/stack-buffer-overflow-with-position.cc
+++ b/test/asan/TestCases/stack-buffer-overflow-with-position.cc
@@ -30,15 +30,15 @@
// make sure BBB and CCC are not removed;
return *(short*)(p) + BBB[argc % 2] + CCC[argc % 2];
}
-// CHECK-m2: 'AAA' <== {{.*}}underflows this variable
-// CHECK-m1: 'AAA' <== {{.*}}partially underflows this variable
-// CHECK-9: 'AAA' <== {{.*}}partially overflows this variable
-// CHECK-10: 'AAA' <== {{.*}}overflows this variable
-// CHECK-30: 'BBB' <== {{.*}}underflows this variable
-// CHECK-31: 'BBB' <== {{.*}}partially underflows this variable
-// CHECK-41: 'BBB' <== {{.*}}partially overflows this variable
-// CHECK-42: 'BBB' <== {{.*}}overflows this variable
-// CHECK-62: 'CCC' <== {{.*}}underflows this variable
-// CHECK-63: 'CCC' <== {{.*}}partially underflows this variable
-// CHECK-73: 'CCC' <== {{.*}}partially overflows this variable
-// CHECK-74: 'CCC' <== {{.*}}overflows this variable
+// CHECK-m2: 'AAA'{{.*}} <== {{.*}}underflows this variable
+// CHECK-m1: 'AAA'{{.*}} <== {{.*}}partially underflows this variable
+// CHECK-9: 'AAA'{{.*}} <== {{.*}}partially overflows this variable
+// CHECK-10: 'AAA'{{.*}} <== {{.*}}overflows this variable
+// CHECK-30: 'BBB'{{.*}} <== {{.*}}underflows this variable
+// CHECK-31: 'BBB'{{.*}} <== {{.*}}partially underflows this variable
+// CHECK-41: 'BBB'{{.*}} <== {{.*}}partially overflows this variable
+// CHECK-42: 'BBB'{{.*}} <== {{.*}}overflows this variable
+// CHECK-62: 'CCC'{{.*}} <== {{.*}}underflows this variable
+// CHECK-63: 'CCC'{{.*}} <== {{.*}}partially underflows this variable
+// CHECK-73: 'CCC'{{.*}} <== {{.*}}partially overflows this variable
+// CHECK-74: 'CCC'{{.*}} <== {{.*}}overflows this variable
diff --git a/test/asan/TestCases/strcasestr-1.c b/test/asan/TestCases/strcasestr-1.c
index c38871e..dccfbcd 100644
--- a/test/asan/TestCases/strcasestr-1.c
+++ b/test/asan/TestCases/strcasestr-1.c
@@ -19,7 +19,7 @@
char s1[4] = "abC";
__asan_poison_memory_region ((char *)&s1[2], 2);
r = strcasestr(s1, s2);
- // CHECK:'s1' <== Memory access at offset {{[0-9]+}} partially overflows this variable
+ // CHECK:'s1'{{.*}} <== Memory access at offset {{[0-9]+}} partially overflows this variable
assert(r == s1 + 2);
return 0;
}
diff --git a/test/asan/TestCases/strcasestr-2.c b/test/asan/TestCases/strcasestr-2.c
index 47fd692..70de2dd 100644
--- a/test/asan/TestCases/strcasestr-2.c
+++ b/test/asan/TestCases/strcasestr-2.c
@@ -20,6 +20,6 @@
__asan_poison_memory_region ((char *)&s2[2], 2);
r = strcasestr(s1, s2);
assert(r == 0);
- // CHECK:'s2' <== Memory access at offset {{[0-9]+}} partially overflows this variable
+ // CHECK:'s2'{{.*}} <== Memory access at offset {{[0-9]+}} partially overflows this variable
return 0;
}
diff --git a/test/asan/TestCases/strcspn-1.c b/test/asan/TestCases/strcspn-1.c
index 6cda2e2..2a9f7d7 100644
--- a/test/asan/TestCases/strcspn-1.c
+++ b/test/asan/TestCases/strcspn-1.c
@@ -14,7 +14,7 @@
char s1[4] = "caB";
__asan_poison_memory_region ((char *)&s1[2], 2);
r = strcspn(s1, s2);
- // CHECK:'s1' <== Memory access at offset {{[0-9]+}} partially overflows this variable
+ // CHECK:'s1'{{.*}} <== Memory access at offset {{[0-9]+}} partially overflows this variable
assert(r == 1);
return 0;
}
diff --git a/test/asan/TestCases/strcspn-2.c b/test/asan/TestCases/strcspn-2.c
index 8bb4b8a..a51fb91 100644
--- a/test/asan/TestCases/strcspn-2.c
+++ b/test/asan/TestCases/strcspn-2.c
@@ -14,7 +14,7 @@
char s2[4] = "abc";
__asan_poison_memory_region ((char *)&s2[2], 2);
r = strcspn(s1, s2);
- // CHECK:'s2' <== Memory access at offset {{[0-9]+}} partially overflows this variable
+ // CHECK:'s2'{{.*}} <== Memory access at offset {{[0-9]+}} partially overflows this variable
assert(r == 0);
return 0;
}
diff --git a/test/asan/TestCases/strpbrk-1.c b/test/asan/TestCases/strpbrk-1.c
index 626e877..eb32326 100644
--- a/test/asan/TestCases/strpbrk-1.c
+++ b/test/asan/TestCases/strpbrk-1.c
@@ -14,7 +14,7 @@
char s1[4] = "cab";
__asan_poison_memory_region ((char *)&s1[2], 2);
r = strpbrk(s1, s2);
- // CHECK:'s1' <== Memory access at offset {{[0-9]+}} partially overflows this variable
+ // CHECK:'s1'{{.*}} <== Memory access at offset {{[0-9]+}} partially overflows this variable
assert(r == s1 + 1);
return 0;
}
diff --git a/test/asan/TestCases/strpbrk-2.c b/test/asan/TestCases/strpbrk-2.c
index 29f3150..1f24656 100644
--- a/test/asan/TestCases/strpbrk-2.c
+++ b/test/asan/TestCases/strpbrk-2.c
@@ -14,7 +14,7 @@
char s2[4] = "bca";
__asan_poison_memory_region ((char *)&s2[2], 2);
r = strpbrk(s1, s2);
- // CHECK:'s2' <== Memory access at offset {{[0-9]+}} partially overflows this variable
+ // CHECK:'s2'{{.*}} <== Memory access at offset {{[0-9]+}} partially overflows this variable
assert(r == s1);
return 0;
}
diff --git a/test/asan/TestCases/strspn-1.c b/test/asan/TestCases/strspn-1.c
index b0c40ea..5ddb14f 100644
--- a/test/asan/TestCases/strspn-1.c
+++ b/test/asan/TestCases/strspn-1.c
@@ -14,7 +14,7 @@
char s1[4] = "acb";
__asan_poison_memory_region ((char *)&s1[2], 2);
r = strspn(s1, s2);
- // CHECK:'s1' <== Memory access at offset {{[0-9]+}} partially overflows this variable
+ // CHECK:'s1'{{.*}} <== Memory access at offset {{[0-9]+}} partially overflows this variable
assert(r == 1);
return 0;
}
diff --git a/test/asan/TestCases/strspn-2.c b/test/asan/TestCases/strspn-2.c
index 4c89910..d564ef8 100644
--- a/test/asan/TestCases/strspn-2.c
+++ b/test/asan/TestCases/strspn-2.c
@@ -14,7 +14,7 @@
char s2[5] = "abcd";
__asan_poison_memory_region ((char *)&s2[3], 2);
r = strspn(s1, s2);
- // CHECK:'s2' <== Memory access at offset {{[0-9]+}} partially overflows this variable
+ // CHECK:'s2'{{.*}} <== Memory access at offset {{[0-9]+}} partially overflows this variable
assert(r >= 2);
return 0;
}
diff --git a/test/asan/TestCases/strstr-1.c b/test/asan/TestCases/strstr-1.c
index 06a8a8a..319cff5 100644
--- a/test/asan/TestCases/strstr-1.c
+++ b/test/asan/TestCases/strstr-1.c
@@ -15,7 +15,7 @@
char s1[4] = "acb";
__asan_poison_memory_region ((char *)&s1[2], 2);
r = strstr(s1, s2);
- // CHECK:'s1' <== Memory access at offset {{[0-9]+}} {{partially overflows this variable|is inside this variable}}
+ // CHECK:'s1'{{.*}} <== Memory access at offset {{[0-9]+}} {{partially overflows this variable|is inside this variable}}
assert(r == s1 + 1);
return 0;
}
diff --git a/test/asan/TestCases/strstr-2.c b/test/asan/TestCases/strstr-2.c
index 8bc6e99..4d00c6e 100644
--- a/test/asan/TestCases/strstr-2.c
+++ b/test/asan/TestCases/strstr-2.c
@@ -15,7 +15,7 @@
char s2[4] = "cab";
__asan_poison_memory_region ((char *)&s2[2], 2);
r = strstr(s1, s2);
- // CHECK:'s2' <== Memory access at offset {{[0-9]+}} partially overflows this variable
+ // CHECK:'s2'{{.*}} <== Memory access at offset {{[0-9]+}} partially overflows this variable
assert(r == 0);
return 0;
}
diff --git a/test/asan/TestCases/use-after-scope-inlined.cc b/test/asan/TestCases/use-after-scope-inlined.cc
index 98a455c..bed9814 100644
--- a/test/asan/TestCases/use-after-scope-inlined.cc
+++ b/test/asan/TestCases/use-after-scope-inlined.cc
@@ -21,8 +21,8 @@
// CHECK: READ of size 4 at 0x{{.*}} thread T0
// CHECK: #0 0x{{.*}} in main
// CHECK: {{.*}}use-after-scope-inlined.cc:[[@LINE-4]]
- // CHECK: Address 0x{{.*}} is located in stack of thread T0 at offset
- // CHECK: [[OFFSET:[^ ]*]] in frame
- // CHECK: main
- // CHECK: {{\[}}[[OFFSET]], {{.*}}) 'x.i:[[@LINE-15]]'
+ // CHECK: Address 0x{{.*}} is located in stack of thread T0 at offset [[OFFSET:[^ ]*]] in frame
+ // CHECK: {{.*}} in main
+ // CHECK: This frame has
+ // CHECK: {{\[}}[[OFFSET]], {{.*}}) 'x.i' (line [[@LINE-15]])
}
diff --git a/test/asan/TestCases/use-after-scope.cc b/test/asan/TestCases/use-after-scope.cc
index d92dae6..4c5998a 100644
--- a/test/asan/TestCases/use-after-scope.cc
+++ b/test/asan/TestCases/use-after-scope.cc
@@ -1,6 +1,10 @@
// RUN: %clangxx_asan -O1 -fsanitize-address-use-after-scope %s -o %t && \
// RUN: not %run %t 2>&1 | FileCheck %s
+// -fsanitize-address-use-after-scope is now on by default:
+// RUN: %clangxx_asan -O1 %s -o %t && \
+// RUN: not %run %t 2>&1 | FileCheck %s
+
volatile int *p = 0;
int main() {
diff --git a/test/asan/Unit/lit.site.cfg.in b/test/asan/Unit/lit.site.cfg.in
index 55631a6..1c59a6b 100644
--- a/test/asan/Unit/lit.site.cfg.in
+++ b/test/asan/Unit/lit.site.cfg.in
@@ -27,3 +27,6 @@
# Set LD_LIBRARY_PATH to pick dynamic runtime up properly.
push_ld_library_path(config, config.compiler_rt_libdir)
+
+if config.host_os == 'Darwin':
+ config.parallelism_group = config.darwin_sanitizer_parallelism_group_func
diff --git a/test/asan/lit.cfg b/test/asan/lit.cfg
index 7703f5a..5ebe414 100644
--- a/test/asan/lit.cfg
+++ b/test/asan/lit.cfg
@@ -241,3 +241,6 @@
# Only run the tests on supported OSs.
if config.host_os not in ['Linux', 'Darwin', 'FreeBSD', 'Windows']:
config.unsupported = True
+
+if config.host_os == 'Darwin' and config.target_arch in ["x86_64", "x86_64h"]:
+ config.parallelism_group = "darwin-64bit-sanitizer"
diff --git a/test/builtins/CMakeLists.txt b/test/builtins/CMakeLists.txt
new file mode 100644
index 0000000..443e552
--- /dev/null
+++ b/test/builtins/CMakeLists.txt
@@ -0,0 +1,15 @@
+set(BUILTINS_LIT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
+
+set(BUILTINS_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS} builtins)
+set(BUILTINS_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/TestCases)
+
+# Test cases.
+configure_lit_site_cfg(
+ ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
+ ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg
+)
+
+add_lit_testsuite(check-builtins "Running the Builtins tests"
+ ${BUILTINS_TESTSUITES}
+ DEPENDS ${BUILTINS_TEST_DEPS})
+set_target_properties(check-builtins PROPERTIES FOLDER "Compiler-RT Misc")
diff --git a/test/builtins/TestCases/Darwin/lit.local.cfg b/test/builtins/TestCases/Darwin/lit.local.cfg
new file mode 100644
index 0000000..a85dfcd
--- /dev/null
+++ b/test/builtins/TestCases/Darwin/lit.local.cfg
@@ -0,0 +1,9 @@
+def getRoot(config):
+ if not config.parent:
+ return config
+ return getRoot(config.parent)
+
+root = getRoot(config)
+
+if root.host_os not in ['Darwin']:
+ config.unsupported = True
diff --git a/test/builtins/TestCases/Darwin/os_version_check_test.c b/test/builtins/TestCases/Darwin/os_version_check_test.c
new file mode 100644
index 0000000..2692cd3
--- /dev/null
+++ b/test/builtins/TestCases/Darwin/os_version_check_test.c
@@ -0,0 +1,19 @@
+// RUN: %clang %s -o %t -mmacosx-version-min=10.5 -framework CoreFoundation -DMAJOR=%macos_version_major -DMINOR=%macos_version_minor -DSUBMINOR=%macos_version_subminor
+// RUN: %run %t
+
+int __isOSVersionAtLeast(int Major, int Minor, int Subminor);
+
+int main() {
+ if (!__isOSVersionAtLeast(MAJOR, MINOR, SUBMINOR))
+ return 1;
+ if (__isOSVersionAtLeast(MAJOR, MINOR, SUBMINOR + 1))
+ return 1;
+ if (SUBMINOR && __isOSVersionAtLeast(MAJOR + 1, MINOR, SUBMINOR - 1))
+ return 1;
+ if (SUBMINOR && !__isOSVersionAtLeast(MAJOR, MINOR, SUBMINOR - 1))
+ return 1;
+ if (MAJOR && !__isOSVersionAtLeast(MAJOR - 1, MINOR + 1, SUBMINOR))
+ return 1;
+
+ return 0;
+}
diff --git a/test/builtins/TestCases/Darwin/os_version_check_test_no_core_foundation.c b/test/builtins/TestCases/Darwin/os_version_check_test_no_core_foundation.c
new file mode 100644
index 0000000..4e0da35
--- /dev/null
+++ b/test/builtins/TestCases/Darwin/os_version_check_test_no_core_foundation.c
@@ -0,0 +1,12 @@
+// RUN: %clang %s -o %t -mmacosx-version-min=10.5
+// RUN: %run %t
+
+int __isOSVersionAtLeast(int Major, int Minor, int Subminor);
+
+int main() {
+ // When CoreFoundation isn't linked, we expect the system version to be 0, 0,
+ // 0.
+ if (__isOSVersionAtLeast(1, 0, 0))
+ return 1;
+ return 0;
+}
diff --git a/test/builtins/lit.cfg b/test/builtins/lit.cfg
new file mode 100644
index 0000000..0044df7
--- /dev/null
+++ b/test/builtins/lit.cfg
@@ -0,0 +1,20 @@
+# -*- Python -*-
+
+import os
+
+# Setup config name.
+config.name = 'Builtins'
+
+# Setup source root.
+config.test_source_root = os.path.dirname(__file__)
+
+# Test suffixes.
+config.suffixes = ['.c', '.cc', '.cpp', '.m', '.mm']
+
+# Define %clang and %clangxx substitutions to use in test RUN lines.
+config.substitutions.append( ("%clang ", " " + config.clang + " ") )
+
+if config.host_os == 'Darwin':
+ config.substitutions.append( ("%macos_version_major", str(config.darwin_osx_version[0])) )
+ config.substitutions.append( ("%macos_version_minor", str(config.darwin_osx_version[1])) )
+ config.substitutions.append( ("%macos_version_subminor", str(config.darwin_osx_version[2])) )
diff --git a/test/builtins/lit.site.cfg.in b/test/builtins/lit.site.cfg.in
new file mode 100644
index 0000000..c7fe82f
--- /dev/null
+++ b/test/builtins/lit.site.cfg.in
@@ -0,0 +1,7 @@
+@LIT_SITE_CFG_IN_HEADER@
+
+# Load common config for all compiler-rt lit tests.
+lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/test/lit.common.configured")
+
+# Load tool-specific config that would do the real work.
+lit_config.load_config(config, "@BUILTINS_LIT_SOURCE_DIR@/lit.cfg")
diff --git a/test/lit.common.cfg b/test/lit.common.cfg
index b875992..e90d738 100644
--- a/test/lit.common.cfg
+++ b/test/lit.common.cfg
@@ -76,6 +76,9 @@
if re.match(r'^x86_64.*-linux', config.target_triple):
config.available_features.add("x86_64-linux")
+if lit.util.isMacOSTriple(config.target_triple):
+ config.available_features.add('darwin')
+
# Use ugly construction to explicitly prohibit "clang", "clang++" etc.
# in RUN lines.
config.substitutions.append(
@@ -126,9 +129,11 @@
lit.util.usePlatformSdkOnDarwin(config, lit_config)
if config.host_os == 'Darwin':
+ osx_version = (10, 0, 0)
try:
osx_version = subprocess.check_output(["sw_vers", "-productVersion"])
osx_version = tuple(int(x) for x in osx_version.split('.'))
+ if len(osx_version) == 2: osx_version = (osx_version[0], osx_version[1], 0)
if osx_version >= (10, 11):
config.available_features.add('osx-autointerception')
config.available_features.add('osx-ld64-live_support')
@@ -141,6 +146,8 @@
except:
pass
+ config.darwin_osx_version = osx_version
+
# Detect x86_64h
try:
output = subprocess.check_output(["sysctl", "hw.cpusubtype"])
@@ -216,3 +223,9 @@
# retries. We don't do this on otther platforms because it's slower.
if platform.system() == 'Windows':
config.test_retry_attempts = 2
+
+# Only run up to 3 64-bit sanitized processes simultaneously on Darwin.
+# Using more scales badly and hogs the system due to inefficient handling
+# of large mmap'd regions (terabytes) by the kernel.
+if platform.system() == 'Darwin':
+ lit_config.parallelism_groups["darwin-64bit-sanitizer"] = 3
diff --git a/test/profile/Linux/instrprof-dir.c b/test/profile/Linux/instrprof-dir.c
new file mode 100644
index 0000000..9d9af6d
--- /dev/null
+++ b/test/profile/Linux/instrprof-dir.c
@@ -0,0 +1,13 @@
+// RUN: %clang_pgogen -o %t %s
+// RUN: env LLVM_PROFILE_FILE="%t.d/%m.profraw"
+// RUN: rm -fr %t.d
+// RUN: %run %t %t.d
+
+#include <errno.h>
+#include <unistd.h>
+
+int main(int argc, char **argv) {
+ if (access(argv[1], F_OK) == 0)
+ return 1; // %t.d should not exist yet.
+ return !(errno == ENOENT);
+}
diff --git a/test/profile/Linux/prctl.c b/test/profile/Linux/prctl.c
new file mode 100644
index 0000000..43baf65
--- /dev/null
+++ b/test/profile/Linux/prctl.c
@@ -0,0 +1,36 @@
+// RUN: %clang_pgogen -O2 -o %t %s
+// RUN: rm -rf default_*.profraw
+// RUN: %run %t && sleep 1
+// RUN: llvm-profdata show default_*.profraw 2>&1 | FileCheck %s
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/prctl.h>
+#include <unistd.h>
+
+#define FAKE_COUNT_SZ 2000000
+/* fake counts to increse the profile size. */
+unsigned long long __attribute__((section("__llvm_prf_cnts")))
+counts[FAKE_COUNT_SZ];
+
+int main(int argc, char **argv) {
+ pid_t pid = fork();
+ if (pid == 0) {
+ int i;
+ int sum = 0;
+ /* child process: sleep 500us and get to runtime before the
+ * main process exits. */
+ prctl(PR_SET_PDEATHSIG, SIGKILL);
+ usleep(500);
+ for (i = 0; i < 5000; ++i)
+ sum += i * i * i;
+ printf("child process (%d): sum=%d\n", getpid(), sum);
+ } else if (pid > 0) {
+ /* parent process: sleep 100us to get into profile runtime first. */
+ usleep(100);
+ }
+ return 0;
+}
+
+// CHECK-NOT: Empty raw profile file
diff --git a/test/profile/instrprof-exit-on-signal.c b/test/profile/instrprof-exit-on-signal.c
new file mode 100644
index 0000000..efe1013
--- /dev/null
+++ b/test/profile/instrprof-exit-on-signal.c
@@ -0,0 +1,26 @@
+// RUN: %clang_profgen -o %t %s
+//
+// Verify SIGTERM handling.
+// RUN: %run LLVM_PROFILE_FILE="%15x%t.profraw" %t 15
+// RUN: llvm-profdata show %t.profraw | FileCheck %s
+//
+// Verify SIGUSR1 handling.
+// RUN: %run LLVM_PROFILE_FILE="%30x%t.profraw" %t 30
+// RUN: llvm-profdata show %t.profraw | FileCheck %s
+
+#include <stdlib.h>
+#include <signal.h>
+#include <unistd.h>
+
+// CHECK: Total functions: 1
+int main(int argc, char **argv) {
+ (void)argc;
+
+ int sig = atoi(argv[1]);
+ kill(getpid(), sig);
+
+ while (1) {
+ /* loop forever */
+ }
+ return 1;
+}
diff --git a/test/tsan/Darwin/dispatch_main.mm b/test/tsan/Darwin/dispatch_main.mm
index 7588754..f4c1e44 100644
--- a/test/tsan/Darwin/dispatch_main.mm
+++ b/test/tsan/Darwin/dispatch_main.mm
@@ -2,7 +2,7 @@
// quits the main thread.
// RUN: %clang_tsan %s -o %t -framework Foundation
-// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s
+// RUN: %run %t 2>&1 | FileCheck %s
#import <Foundation/Foundation.h>
diff --git a/test/tsan/Darwin/dispatch_once_deadlock.mm b/test/tsan/Darwin/dispatch_once_deadlock.mm
index e88cdc0..e109f64 100644
--- a/test/tsan/Darwin/dispatch_once_deadlock.mm
+++ b/test/tsan/Darwin/dispatch_once_deadlock.mm
@@ -1,7 +1,7 @@
// Check that calling dispatch_once from a report callback works.
// RUN: %clang_tsan %s -o %t -framework Foundation
-// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 not %run %t 2>&1 | FileCheck %s
+// RUN: not %run %t 2>&1 | FileCheck %s
#import <Foundation/Foundation.h>
#import <pthread.h>
diff --git a/test/tsan/Darwin/external.cc b/test/tsan/Darwin/external.cc
new file mode 100644
index 0000000..2605480
--- /dev/null
+++ b/test/tsan/Darwin/external.cc
@@ -0,0 +1,163 @@
+// RUN: %clangxx_tsan %s -shared -DSHARED_LIB \
+// RUN: -o %t-lib-instrumented.dylib \
+// RUN: -install_name @rpath/`basename %t-lib-instrumented.dylib`
+
+// RUN: %clangxx_tsan %s -shared -DSHARED_LIB -fno-sanitize=thread \
+// RUN: -o %t-lib-noninstrumented.dylib \
+// RUN: -install_name @rpath/`basename %t-lib-noninstrumented.dylib`
+
+// RUN: %clangxx_tsan %s -shared -DSHARED_LIB -fno-sanitize=thread -DUSE_TSAN_CALLBACKS \
+// RUN: -o %t-lib-noninstrumented-callbacks.dylib \
+// RUN: -install_name @rpath/`basename %t-lib-noninstrumented-callbacks.dylib`
+
+// RUN: %clangxx_tsan %s %t-lib-instrumented.dylib -o %t-lib-instrumented
+// RUN: %clangxx_tsan %s %t-lib-noninstrumented.dylib -o %t-lib-noninstrumented
+// RUN: %clangxx_tsan %s %t-lib-noninstrumented-callbacks.dylib -o %t-lib-noninstrumented-callbacks
+
+// RUN: %deflake %run %t-lib-instrumented 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK --check-prefix=TEST1
+// RUN: %run %t-lib-noninstrumented 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK --check-prefix=TEST2
+// RUN: %deflake %run %t-lib-noninstrumented-callbacks 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK --check-prefix=TEST3
+
+#include <thread>
+
+#include <dlfcn.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+struct MyObject;
+typedef MyObject *MyObjectRef;
+extern "C" {
+ void InitializeLibrary();
+ MyObject *ObjectCreate();
+ long ObjectRead(MyObject *);
+ void ObjectWrite(MyObject *, long);
+ void ObjectWriteAnother(MyObject *, long);
+}
+
+#if defined(SHARED_LIB)
+
+struct MyObject {
+ long _val;
+ long _another;
+};
+
+#if defined(USE_TSAN_CALLBACKS)
+static void *tag;
+void *(*callback_register_tag)(const char *object_type);
+void *(*callback_assign_tag)(void *addr, void *tag);
+void (*callback_read)(void *addr, void *caller_pc, void *tag);
+void (*callback_write)(void *addr, void *caller_pc, void *tag);
+#endif
+
+void InitializeLibrary() {
+#if defined(USE_TSAN_CALLBACKS)
+ callback_register_tag = (decltype(callback_register_tag))dlsym(RTLD_DEFAULT, "__tsan_external_register_tag");
+ callback_assign_tag = (decltype(callback_assign_tag))dlsym(RTLD_DEFAULT, "__tsan_external_assign_tag");
+ callback_read = (decltype(callback_read))dlsym(RTLD_DEFAULT, "__tsan_external_read");
+ callback_write = (decltype(callback_write))dlsym(RTLD_DEFAULT, "__tsan_external_write");
+ tag = callback_register_tag("MyLibrary::MyObject");
+#endif
+}
+
+MyObject *ObjectCreate() {
+ MyObject *ref = (MyObject *)malloc(sizeof(MyObject));
+#if defined(USE_TSAN_CALLBACKS)
+ callback_assign_tag(ref, tag);
+#endif
+ return ref;
+}
+
+long ObjectRead(MyObject *ref) {
+#if defined(USE_TSAN_CALLBACKS)
+ callback_read(ref, __builtin_return_address(0), tag);
+#endif
+ return ref->_val;
+}
+
+void ObjectWrite(MyObject *ref, long val) {
+#if defined(USE_TSAN_CALLBACKS)
+ callback_write(ref, __builtin_return_address(0), tag);
+#endif
+ ref->_val = val;
+}
+
+void ObjectWriteAnother(MyObject *ref, long val) {
+#if defined(USE_TSAN_CALLBACKS)
+ callback_write(ref, __builtin_return_address(0), tag);
+#endif
+ ref->_another = val;
+}
+
+#else // defined(SHARED_LIB)
+
+int main(int argc, char *argv[]) {
+ InitializeLibrary();
+
+ {
+ MyObjectRef ref = ObjectCreate();
+ std::thread t1([ref]{ ObjectRead(ref); });
+ std::thread t2([ref]{ ObjectRead(ref); });
+ t1.join();
+ t2.join();
+ }
+
+ // CHECK-NOT: WARNING: ThreadSanitizer
+
+ fprintf(stderr, "RR test done\n");
+ // CHECK: RR test done
+
+ {
+ MyObjectRef ref = ObjectCreate();
+ std::thread t1([ref]{ ObjectRead(ref); });
+ std::thread t2([ref]{ ObjectWrite(ref, 66); });
+ t1.join();
+ t2.join();
+ }
+
+ // TEST1: WARNING: ThreadSanitizer: data race
+ // TEST1: {{Write|Read}} of size 8 at
+ // TEST1: Previous {{write|read}} of size 8 at
+ // TEST1: Location is heap block of size 16 at
+
+ // TEST2-NOT: WARNING: ThreadSanitizer
+
+ // TEST3: WARNING: ThreadSanitizer: race on a library object
+ // TEST3: {{Mutating|read-only}} access of object MyLibrary::MyObject at
+ // TEST3: {{ObjectWrite|ObjectRead}}
+ // TEST3: Previous {{mutating|read-only}} access of object MyLibrary::MyObject at
+ // TEST3: {{ObjectWrite|ObjectRead}}
+ // TEST3: Location is MyLibrary::MyObject object of size 16 at
+ // TEST3: {{ObjectCreate}}
+
+ fprintf(stderr, "RW test done\n");
+ // CHECK: RW test done
+
+ {
+ MyObjectRef ref = ObjectCreate();
+ std::thread t1([ref]{ ObjectWrite(ref, 76); });
+ std::thread t2([ref]{ ObjectWriteAnother(ref, 77); });
+ t1.join();
+ t2.join();
+ }
+
+ // TEST1-NOT: WARNING: ThreadSanitizer: data race
+
+ // TEST2-NOT: WARNING: ThreadSanitizer
+
+ // TEST3: WARNING: ThreadSanitizer: race on a library object
+ // TEST3: Mutating access of object MyLibrary::MyObject at
+ // TEST3: {{ObjectWrite|ObjectWriteAnother}}
+ // TEST3: Previous mutating access of object MyLibrary::MyObject at
+ // TEST3: {{ObjectWrite|ObjectWriteAnother}}
+ // TEST3: Location is MyLibrary::MyObject object of size 16 at
+ // TEST3: {{ObjectCreate}}
+
+ fprintf(stderr, "WW test done\n");
+ // CHECK: WW test done
+}
+
+#endif // defined(SHARED_LIB)
diff --git a/test/tsan/Darwin/gcd-after-null.mm b/test/tsan/Darwin/gcd-after-null.mm
new file mode 100644
index 0000000..7c9913c
--- /dev/null
+++ b/test/tsan/Darwin/gcd-after-null.mm
@@ -0,0 +1,23 @@
+// Regression test to make sure we don't crash when dispatch_after is called with a NULL queue.
+
+// RUN: %clang_tsan %s -o %t -framework Foundation
+// RUN: %run %t 2>&1 | FileCheck %s
+
+#import <Foundation/Foundation.h>
+
+int main(int argc, const char *argv[]) {
+ fprintf(stderr, "start\n");
+
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_MSEC)), NULL, ^{
+ dispatch_async(dispatch_get_main_queue(), ^{
+ CFRunLoopStop(CFRunLoopGetMain());
+ });
+ });
+ CFRunLoopRun();
+
+ fprintf(stderr, "done\n");
+ return 0;
+}
+
+// CHECK: start
+// CHECK: done
diff --git a/test/tsan/Darwin/gcd-after.mm b/test/tsan/Darwin/gcd-after.mm
index 49b6bc6..4d66c50 100644
--- a/test/tsan/Darwin/gcd-after.mm
+++ b/test/tsan/Darwin/gcd-after.mm
@@ -1,5 +1,5 @@
// RUN: %clang_tsan %s -o %t -framework Foundation
-// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s
+// RUN: %run %t 2>&1 | FileCheck %s
#import <Foundation/Foundation.h>
diff --git a/test/tsan/Darwin/gcd-apply-race.mm b/test/tsan/Darwin/gcd-apply-race.mm
index 028be1a..a7bf663 100644
--- a/test/tsan/Darwin/gcd-apply-race.mm
+++ b/test/tsan/Darwin/gcd-apply-race.mm
@@ -1,5 +1,5 @@
// RUN: %clang_tsan %s -o %t -framework Foundation
-// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %deflake %run %t 2>&1 | FileCheck %s
+// RUN: %deflake %run %t 2>&1 | FileCheck %s
#import <Foundation/Foundation.h>
diff --git a/test/tsan/Darwin/gcd-apply.mm b/test/tsan/Darwin/gcd-apply.mm
index a7dc374..d9d2562 100644
--- a/test/tsan/Darwin/gcd-apply.mm
+++ b/test/tsan/Darwin/gcd-apply.mm
@@ -1,5 +1,5 @@
// RUN: %clang_tsan %s -o %t -framework Foundation
-// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s
+// RUN: %run %t 2>&1 | FileCheck %s
#import <Foundation/Foundation.h>
diff --git a/test/tsan/Darwin/gcd-async-norace.mm b/test/tsan/Darwin/gcd-async-norace.mm
index c7e28b4..83f8c0d 100644
--- a/test/tsan/Darwin/gcd-async-norace.mm
+++ b/test/tsan/Darwin/gcd-async-norace.mm
@@ -1,5 +1,5 @@
// RUN: %clang_tsan %s -o %t -framework Foundation
-// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s
+// RUN: %run %t 2>&1 | FileCheck %s
#import <Foundation/Foundation.h>
diff --git a/test/tsan/Darwin/gcd-async-race.mm b/test/tsan/Darwin/gcd-async-race.mm
index 1002a56..cb8fb4b 100644
--- a/test/tsan/Darwin/gcd-async-race.mm
+++ b/test/tsan/Darwin/gcd-async-race.mm
@@ -1,5 +1,5 @@
// RUN: %clang_tsan %s -o %t -framework Foundation
-// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %deflake %run %t 2>&1 | FileCheck %s
+// RUN: %deflake %run %t 2>&1 | FileCheck %s
#import <Foundation/Foundation.h>
diff --git a/test/tsan/Darwin/gcd-barrier-race.mm b/test/tsan/Darwin/gcd-barrier-race.mm
index c42eaeb..c11e147 100644
--- a/test/tsan/Darwin/gcd-barrier-race.mm
+++ b/test/tsan/Darwin/gcd-barrier-race.mm
@@ -1,5 +1,5 @@
// RUN: %clang_tsan %s -o %t -framework Foundation
-// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %deflake %run %t 2>&1 | FileCheck %s
+// RUN: %deflake %run %t 2>&1 | FileCheck %s
#import <Foundation/Foundation.h>
diff --git a/test/tsan/Darwin/gcd-barrier.mm b/test/tsan/Darwin/gcd-barrier.mm
index 6f58cae..9d4dcb2 100644
--- a/test/tsan/Darwin/gcd-barrier.mm
+++ b/test/tsan/Darwin/gcd-barrier.mm
@@ -1,5 +1,5 @@
// RUN: %clang_tsan %s -o %t -framework Foundation
-// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s
+// RUN: %run %t 2>&1 | FileCheck %s
#import <Foundation/Foundation.h>
diff --git a/test/tsan/Darwin/gcd-blocks.mm b/test/tsan/Darwin/gcd-blocks.mm
index e008260..1aac7e1 100644
--- a/test/tsan/Darwin/gcd-blocks.mm
+++ b/test/tsan/Darwin/gcd-blocks.mm
@@ -1,5 +1,5 @@
// RUN: %clangxx_tsan %s -o %t -framework Foundation
-// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s
+// RUN: %run %t 2>&1 | FileCheck %s
#import <Foundation/Foundation.h>
diff --git a/test/tsan/Darwin/gcd-data.mm b/test/tsan/Darwin/gcd-data.mm
index a5154dc..d451cf5 100644
--- a/test/tsan/Darwin/gcd-data.mm
+++ b/test/tsan/Darwin/gcd-data.mm
@@ -1,5 +1,5 @@
// RUN: %clang_tsan %s -o %t -framework Foundation
-// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s
+// RUN: %run %t 2>&1 | FileCheck %s
#import <Foundation/Foundation.h>
diff --git a/test/tsan/Darwin/gcd-fd.mm b/test/tsan/Darwin/gcd-fd.mm
index 75da9cd..838cf20 100644
--- a/test/tsan/Darwin/gcd-fd.mm
+++ b/test/tsan/Darwin/gcd-fd.mm
@@ -1,5 +1,5 @@
// RUN: %clang_tsan %s -o %t -framework Foundation
-// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s
+// RUN: %run %t 2>&1 | FileCheck %s
#import <Foundation/Foundation.h>
diff --git a/test/tsan/Darwin/gcd-groups-destructor.mm b/test/tsan/Darwin/gcd-groups-destructor.mm
index 19c2c9b..05c65c0 100644
--- a/test/tsan/Darwin/gcd-groups-destructor.mm
+++ b/test/tsan/Darwin/gcd-groups-destructor.mm
@@ -1,5 +1,5 @@
// RUN: %clangxx_tsan %s -o %t -framework Foundation
-// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s
+// RUN: %run %t 2>&1 | FileCheck %s
#import <Foundation/Foundation.h>
diff --git a/test/tsan/Darwin/gcd-groups-leave.mm b/test/tsan/Darwin/gcd-groups-leave.mm
index 6ecf85f..49fd8e2 100644
--- a/test/tsan/Darwin/gcd-groups-leave.mm
+++ b/test/tsan/Darwin/gcd-groups-leave.mm
@@ -1,5 +1,5 @@
// RUN: %clang_tsan %s -o %t -framework Foundation
-// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s
+// RUN: %run %t 2>&1 | FileCheck %s
#import <Foundation/Foundation.h>
diff --git a/test/tsan/Darwin/gcd-groups-norace.mm b/test/tsan/Darwin/gcd-groups-norace.mm
index 64ec386..e850169 100644
--- a/test/tsan/Darwin/gcd-groups-norace.mm
+++ b/test/tsan/Darwin/gcd-groups-norace.mm
@@ -1,5 +1,5 @@
// RUN: %clang_tsan %s -o %t -framework Foundation
-// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s
+// RUN: %run %t 2>&1 | FileCheck %s
#import <Foundation/Foundation.h>
diff --git a/test/tsan/Darwin/gcd-groups-stress.mm b/test/tsan/Darwin/gcd-groups-stress.mm
index 457d9af..cfe4deb 100644
--- a/test/tsan/Darwin/gcd-groups-stress.mm
+++ b/test/tsan/Darwin/gcd-groups-stress.mm
@@ -1,5 +1,5 @@
// RUN: %clang_tsan %s -o %t -framework Foundation
-// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s
+// RUN: %run %t 2>&1 | FileCheck %s
#import <Foundation/Foundation.h>
diff --git a/test/tsan/Darwin/gcd-io-barrier-race.mm b/test/tsan/Darwin/gcd-io-barrier-race.mm
index fffc19b..137c3b2 100644
--- a/test/tsan/Darwin/gcd-io-barrier-race.mm
+++ b/test/tsan/Darwin/gcd-io-barrier-race.mm
@@ -1,5 +1,5 @@
// RUN: %clang_tsan %s -o %t -framework Foundation
-// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %deflake %run %t 2>&1 | FileCheck %s
+// RUN: %deflake %run %t 2>&1 | FileCheck %s
#import <Foundation/Foundation.h>
diff --git a/test/tsan/Darwin/gcd-io-barrier.mm b/test/tsan/Darwin/gcd-io-barrier.mm
index fe30138..af92b03 100644
--- a/test/tsan/Darwin/gcd-io-barrier.mm
+++ b/test/tsan/Darwin/gcd-io-barrier.mm
@@ -1,5 +1,5 @@
// RUN: %clang_tsan %s -o %t -framework Foundation
-// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s
+// RUN: %run %t 2>&1 | FileCheck %s
#import <Foundation/Foundation.h>
diff --git a/test/tsan/Darwin/gcd-io-cleanup.mm b/test/tsan/Darwin/gcd-io-cleanup.mm
index b15fa0d..570e37d 100644
--- a/test/tsan/Darwin/gcd-io-cleanup.mm
+++ b/test/tsan/Darwin/gcd-io-cleanup.mm
@@ -1,5 +1,5 @@
// RUN: %clang_tsan %s -o %t -framework Foundation
-// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s
+// RUN: %run %t 2>&1 | FileCheck %s
#import <Foundation/Foundation.h>
diff --git a/test/tsan/Darwin/gcd-io-race.mm b/test/tsan/Darwin/gcd-io-race.mm
index 0bec28f..99000fc 100644
--- a/test/tsan/Darwin/gcd-io-race.mm
+++ b/test/tsan/Darwin/gcd-io-race.mm
@@ -1,5 +1,5 @@
// RUN: %clang_tsan %s -o %t -framework Foundation
-// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %deflake %run %t 2>&1 | FileCheck %s
+// RUN: %deflake %run %t 2>&1 | FileCheck %s
// REQUIRES: disabled
diff --git a/test/tsan/Darwin/gcd-io.mm b/test/tsan/Darwin/gcd-io.mm
index 4a1726d..70ded40 100644
--- a/test/tsan/Darwin/gcd-io.mm
+++ b/test/tsan/Darwin/gcd-io.mm
@@ -1,5 +1,5 @@
// RUN: %clang_tsan %s -o %t -framework Foundation
-// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s
+// RUN: %run %t 2>&1 | FileCheck %s
#import <Foundation/Foundation.h>
diff --git a/test/tsan/Darwin/gcd-once.mm b/test/tsan/Darwin/gcd-once.mm
index 3e4a533..70e588a 100644
--- a/test/tsan/Darwin/gcd-once.mm
+++ b/test/tsan/Darwin/gcd-once.mm
@@ -1,5 +1,5 @@
// RUN: %clang_tsan %s -o %t -framework Foundation
-// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s
+// RUN: %run %t 2>&1 | FileCheck %s
#import <Foundation/Foundation.h>
diff --git a/test/tsan/Darwin/gcd-semaphore-norace.mm b/test/tsan/Darwin/gcd-semaphore-norace.mm
index 20bc572..fd5d146 100644
--- a/test/tsan/Darwin/gcd-semaphore-norace.mm
+++ b/test/tsan/Darwin/gcd-semaphore-norace.mm
@@ -1,5 +1,5 @@
// RUN: %clang_tsan %s -o %t -framework Foundation
-// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s
+// RUN: %run %t 2>&1 | FileCheck %s
#import <Foundation/Foundation.h>
diff --git a/test/tsan/Darwin/gcd-serial-queue-norace.mm b/test/tsan/Darwin/gcd-serial-queue-norace.mm
index 95efbb7..8754b618 100644
--- a/test/tsan/Darwin/gcd-serial-queue-norace.mm
+++ b/test/tsan/Darwin/gcd-serial-queue-norace.mm
@@ -1,5 +1,5 @@
// RUN: %clang_tsan %s -o %t -framework Foundation
-// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s
+// RUN: %run %t 2>&1 | FileCheck %s
#import <Foundation/Foundation.h>
diff --git a/test/tsan/Darwin/gcd-source-cancel.mm b/test/tsan/Darwin/gcd-source-cancel.mm
index 86e1b28..8aa6f10 100644
--- a/test/tsan/Darwin/gcd-source-cancel.mm
+++ b/test/tsan/Darwin/gcd-source-cancel.mm
@@ -1,5 +1,5 @@
// RUN: %clang_tsan %s -o %t -framework Foundation
-// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s
+// RUN: %run %t 2>&1 | FileCheck %s
#import <Foundation/Foundation.h>
diff --git a/test/tsan/Darwin/gcd-source-cancel2.mm b/test/tsan/Darwin/gcd-source-cancel2.mm
index 956fe87..92b31d7 100644
--- a/test/tsan/Darwin/gcd-source-cancel2.mm
+++ b/test/tsan/Darwin/gcd-source-cancel2.mm
@@ -1,5 +1,5 @@
// RUN: %clang_tsan %s -o %t -framework Foundation
-// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s
+// RUN: %run %t 2>&1 | FileCheck %s
#import <Foundation/Foundation.h>
diff --git a/test/tsan/Darwin/gcd-source-event.mm b/test/tsan/Darwin/gcd-source-event.mm
index e50cb56..e707b65 100644
--- a/test/tsan/Darwin/gcd-source-event.mm
+++ b/test/tsan/Darwin/gcd-source-event.mm
@@ -1,5 +1,5 @@
// RUN: %clang_tsan %s -o %t -framework Foundation
-// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s
+// RUN: %run %t 2>&1 | FileCheck %s
#import <Foundation/Foundation.h>
diff --git a/test/tsan/Darwin/gcd-source-event2.mm b/test/tsan/Darwin/gcd-source-event2.mm
index c45d481..b10e474 100644
--- a/test/tsan/Darwin/gcd-source-event2.mm
+++ b/test/tsan/Darwin/gcd-source-event2.mm
@@ -1,5 +1,5 @@
// RUN: %clang_tsan %s -o %t -framework Foundation
-// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s
+// RUN: %run %t 2>&1 | FileCheck %s
#import <Foundation/Foundation.h>
diff --git a/test/tsan/Darwin/gcd-source-registration.mm b/test/tsan/Darwin/gcd-source-registration.mm
index db22613..d6d339f 100644
--- a/test/tsan/Darwin/gcd-source-registration.mm
+++ b/test/tsan/Darwin/gcd-source-registration.mm
@@ -1,5 +1,5 @@
// RUN: %clang_tsan %s -o %t -framework Foundation
-// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s
+// RUN: %run %t 2>&1 | FileCheck %s
#import <Foundation/Foundation.h>
diff --git a/test/tsan/Darwin/gcd-source-registration2.mm b/test/tsan/Darwin/gcd-source-registration2.mm
index 4431bc9..6365787 100644
--- a/test/tsan/Darwin/gcd-source-registration2.mm
+++ b/test/tsan/Darwin/gcd-source-registration2.mm
@@ -1,5 +1,5 @@
// RUN: %clang_tsan %s -o %t -framework Foundation
-// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s
+// RUN: %run %t 2>&1 | FileCheck %s
#import <Foundation/Foundation.h>
diff --git a/test/tsan/Darwin/gcd-source-serial.mm b/test/tsan/Darwin/gcd-source-serial.mm
index c0989fc..9922030 100644
--- a/test/tsan/Darwin/gcd-source-serial.mm
+++ b/test/tsan/Darwin/gcd-source-serial.mm
@@ -1,5 +1,5 @@
// RUN: %clang_tsan %s -o %t -framework Foundation
-// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s
+// RUN: %run %t 2>&1 | FileCheck %s
#import <Foundation/Foundation.h>
diff --git a/test/tsan/Darwin/gcd-suspend.mm b/test/tsan/Darwin/gcd-suspend.mm
index 3e8818a..561e7c0 100644
--- a/test/tsan/Darwin/gcd-suspend.mm
+++ b/test/tsan/Darwin/gcd-suspend.mm
@@ -1,5 +1,5 @@
// RUN: %clang_tsan %s -o %t -framework Foundation
-// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s
+// RUN: %run %t 2>&1 | FileCheck %s
#import <Foundation/Foundation.h>
diff --git a/test/tsan/Darwin/gcd-sync-norace.mm b/test/tsan/Darwin/gcd-sync-norace.mm
index c683524..18bf973 100644
--- a/test/tsan/Darwin/gcd-sync-norace.mm
+++ b/test/tsan/Darwin/gcd-sync-norace.mm
@@ -1,5 +1,5 @@
// RUN: %clang_tsan %s -o %t -framework Foundation
-// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s
+// RUN: %run %t 2>&1 | FileCheck %s
#import <Foundation/Foundation.h>
diff --git a/test/tsan/Darwin/gcd-sync-race.mm b/test/tsan/Darwin/gcd-sync-race.mm
index 650faa4..b7f3266 100644
--- a/test/tsan/Darwin/gcd-sync-race.mm
+++ b/test/tsan/Darwin/gcd-sync-race.mm
@@ -1,5 +1,5 @@
// RUN: %clang_tsan %s -o %t -framework Foundation
-// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %deflake %run %t 2>&1 | FileCheck %s
+// RUN: %deflake %run %t 2>&1 | FileCheck %s
#import <Foundation/Foundation.h>
diff --git a/test/tsan/Darwin/gcd-target-queue-norace.mm b/test/tsan/Darwin/gcd-target-queue-norace.mm
index 36cb1b9..fbfa658 100644
--- a/test/tsan/Darwin/gcd-target-queue-norace.mm
+++ b/test/tsan/Darwin/gcd-target-queue-norace.mm
@@ -1,5 +1,5 @@
// RUN: %clang_tsan %s -o %t -framework Foundation
-// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s
+// RUN: %run %t 2>&1 | FileCheck %s
#import <Foundation/Foundation.h>
diff --git a/test/tsan/Darwin/ignore-noninstrumented.mm b/test/tsan/Darwin/ignore-noninstrumented.mm
index 5e44531..528e07b 100644
--- a/test/tsan/Darwin/ignore-noninstrumented.mm
+++ b/test/tsan/Darwin/ignore-noninstrumented.mm
@@ -3,7 +3,7 @@
// RUN: %clang_tsan %s -o %t -framework Foundation
// Check that without the flag, there are false positives.
-// RUN: %deflake %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-RACE
+// RUN: %env_tsan_opts=ignore_noninstrumented_modules=0 %deflake %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-RACE
// With ignore_noninstrumented_modules=1, no races are reported.
// RUN: %env_tsan_opts=ignore_noninstrumented_modules=1 %run %t 2>&1 | FileCheck %s
diff --git a/test/tsan/Darwin/ignored-interceptors.mm b/test/tsan/Darwin/ignored-interceptors.mm
index d513142..1105132 100644
--- a/test/tsan/Darwin/ignored-interceptors.mm
+++ b/test/tsan/Darwin/ignored-interceptors.mm
@@ -6,13 +6,13 @@
// RUN: %clang_tsan %s -o %t -framework Foundation
// Check that without the flag, there are false positives.
-// RUN: %deflake %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-RACE
+// RUN: %env_tsan_opts=ignore_noninstrumented_modules=0 %deflake %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-RACE
// With ignore_interceptors_accesses=1, no races are reported.
-// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s
+// RUN: %env_tsan_opts=ignore_noninstrumented_modules=0:ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s
// With ignore_interceptors_accesses=1, races in user's code are still reported.
-// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %deflake %run %t race 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-RACE
+// RUN: %env_tsan_opts=ignore_noninstrumented_modules=0:ignore_interceptors_accesses=1 %deflake %run %t race 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-RACE
#import <Foundation/Foundation.h>
diff --git a/test/tsan/Darwin/libcxx-call-once.mm b/test/tsan/Darwin/libcxx-call-once.mm
index 5388e49..ba4615f 100644
--- a/test/tsan/Darwin/libcxx-call-once.mm
+++ b/test/tsan/Darwin/libcxx-call-once.mm
@@ -1,5 +1,5 @@
// RUN: %clangxx_tsan %s -o %t -framework Foundation -std=c++11
-// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s
+// RUN: %run %t 2>&1 | FileCheck %s
#import <Foundation/Foundation.h>
diff --git a/test/tsan/Darwin/libcxx-future.mm b/test/tsan/Darwin/libcxx-future.mm
index 902f267..720c2e0 100644
--- a/test/tsan/Darwin/libcxx-future.mm
+++ b/test/tsan/Darwin/libcxx-future.mm
@@ -1,5 +1,5 @@
// RUN: %clangxx_tsan %s -o %t
-// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s
+// RUN: %run %t 2>&1 | FileCheck %s
#include <iostream>
#include <future>
diff --git a/test/tsan/Darwin/libcxx-shared-ptr-recursive.mm b/test/tsan/Darwin/libcxx-shared-ptr-recursive.mm
index eea02dc..a9a3a96 100644
--- a/test/tsan/Darwin/libcxx-shared-ptr-recursive.mm
+++ b/test/tsan/Darwin/libcxx-shared-ptr-recursive.mm
@@ -1,5 +1,5 @@
// RUN: %clangxx_tsan %s -o %t -framework Foundation
-// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s
+// RUN: %run %t 2>&1 | FileCheck %s
#import <Foundation/Foundation.h>
diff --git a/test/tsan/Darwin/libcxx-shared-ptr-stress.mm b/test/tsan/Darwin/libcxx-shared-ptr-stress.mm
index 7c36729..e5cd7ed 100644
--- a/test/tsan/Darwin/libcxx-shared-ptr-stress.mm
+++ b/test/tsan/Darwin/libcxx-shared-ptr-stress.mm
@@ -1,5 +1,5 @@
// RUN: %clangxx_tsan %s -o %t -framework Foundation
-// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s
+// RUN: %run %t 2>&1 | FileCheck %s
#import <Foundation/Foundation.h>
diff --git a/test/tsan/Darwin/libcxx-shared-ptr.mm b/test/tsan/Darwin/libcxx-shared-ptr.mm
index 6187c43..057ff22 100644
--- a/test/tsan/Darwin/libcxx-shared-ptr.mm
+++ b/test/tsan/Darwin/libcxx-shared-ptr.mm
@@ -1,5 +1,5 @@
// RUN: %clangxx_tsan %s -o %t -framework Foundation
-// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s
+// RUN: %run %t 2>&1 | FileCheck %s
#import <Foundation/Foundation.h>
diff --git a/test/tsan/Darwin/lit.local.cfg b/test/tsan/Darwin/lit.local.cfg
index a85dfcd..e74e82d 100644
--- a/test/tsan/Darwin/lit.local.cfg
+++ b/test/tsan/Darwin/lit.local.cfg
@@ -7,3 +7,5 @@
if root.host_os not in ['Darwin']:
config.unsupported = True
+
+config.environment['TSAN_OPTIONS'] += ':ignore_noninstrumented_modules=1'
diff --git a/test/tsan/Darwin/main_tid.mm b/test/tsan/Darwin/main_tid.mm
new file mode 100644
index 0000000..af658e4
--- /dev/null
+++ b/test/tsan/Darwin/main_tid.mm
@@ -0,0 +1,46 @@
+// RUN: %clang_tsan %s -o %t -framework Foundation
+// RUN: %deflake %run %t 2>&1 | FileCheck %s
+
+#import <pthread.h>
+#import <stdio.h>
+#import <stdlib.h>
+
+extern "C" {
+void __tsan_on_report(void *report);
+int __tsan_get_report_thread(void *report, unsigned long idx, int *tid,
+ unsigned long *os_id, int *running,
+ const char **name, int *parent_tid, void **trace,
+ unsigned long trace_size);
+}
+
+void __tsan_on_report(void *report) {
+ fprintf(stderr, "__tsan_on_report(%p)\n", report);
+
+ int tid;
+ unsigned long os_id;
+ int running;
+ const char *name;
+ int parent_tid;
+ void *trace[16] = {0};
+ __tsan_get_report_thread(report, 0, &tid, &os_id, &running, &name, &parent_tid, trace, 16);
+ fprintf(stderr, "tid = %d, os_id = %lu\n", tid, os_id);
+}
+
+int main() {
+ fprintf(stderr, "Hello world.\n");
+
+ uint64_t threadid;
+ pthread_threadid_np(NULL, &threadid);
+ fprintf(stderr, "pthread_threadid_np = %llu\n", threadid);
+
+ pthread_mutex_t m;
+ pthread_mutex_init(&m, NULL);
+ pthread_mutex_unlock(&m);
+ fprintf(stderr, "Done.\n");
+}
+
+// CHECK: Hello world.
+// CHECK: pthread_threadid_np = [[ADDR:[0-9]+]]
+// CHECK: WARNING: ThreadSanitizer
+// CHECK: tid = 0, os_id = [[ADDR]]
+// CHECK: Done.
diff --git a/test/tsan/Darwin/norace-objcxx-run-time.mm b/test/tsan/Darwin/norace-objcxx-run-time.mm
index 0cf729e..1de431a 100644
--- a/test/tsan/Darwin/norace-objcxx-run-time.mm
+++ b/test/tsan/Darwin/norace-objcxx-run-time.mm
@@ -1,5 +1,5 @@
// RUN: %clang_tsan %s -lc++ -fobjc-arc -lobjc -o %t -framework Foundation
-// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s
+// RUN: %run %t 2>&1 | FileCheck %s
// Check that we do not report races between:
// - Object retain and initialize
diff --git a/test/tsan/Darwin/objc-double-property.mm b/test/tsan/Darwin/objc-double-property.mm
index 51b10f2..c99151d 100644
--- a/test/tsan/Darwin/objc-double-property.mm
+++ b/test/tsan/Darwin/objc-double-property.mm
@@ -1,7 +1,7 @@
-// RUN: %clangxx_tsan -O0 %s -o %t -framework Foundation && %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s
-// RUN: %clangxx_tsan -O1 %s -o %t -framework Foundation && %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s
-// RUN: %clangxx_tsan -O2 %s -o %t -framework Foundation && %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s
-// RUN: %clangxx_tsan -O3 %s -o %t -framework Foundation && %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s
+// RUN: %clangxx_tsan -O0 %s -o %t -framework Foundation && %run %t 2>&1 | FileCheck %s
+// RUN: %clangxx_tsan -O1 %s -o %t -framework Foundation && %run %t 2>&1 | FileCheck %s
+// RUN: %clangxx_tsan -O2 %s -o %t -framework Foundation && %run %t 2>&1 | FileCheck %s
+// RUN: %clangxx_tsan -O3 %s -o %t -framework Foundation && %run %t 2>&1 | FileCheck %s
#import <Foundation/Foundation.h>
diff --git a/test/tsan/Darwin/objc-simple.mm b/test/tsan/Darwin/objc-simple.mm
index a8fc355..b62d0eb 100644
--- a/test/tsan/Darwin/objc-simple.mm
+++ b/test/tsan/Darwin/objc-simple.mm
@@ -1,7 +1,7 @@
// Test that a simple Obj-C program runs and exits without any warnings.
// RUN: %clang_tsan %s -o %t -framework Foundation
-// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s
+// RUN: %run %t 2>&1 | FileCheck %s
#import <Foundation/Foundation.h>
diff --git a/test/tsan/Darwin/workerthreads.mm b/test/tsan/Darwin/workerthreads.mm
new file mode 100644
index 0000000..18369fd
--- /dev/null
+++ b/test/tsan/Darwin/workerthreads.mm
@@ -0,0 +1,43 @@
+// RUN: %clang_tsan %s -o %t -framework Foundation
+// RUN: %deflake %run %t 2>&1 | FileCheck %s
+
+#import <Foundation/Foundation.h>
+
+#import "../test.h"
+
+long global;
+
+int main() {
+ fprintf(stderr, "Hello world.\n");
+ print_address("addr=", 1, &global);
+ barrier_init(&barrier, 2);
+
+ global = 42;
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+ global = 43;
+ barrier_wait(&barrier);
+ });
+
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+ barrier_wait(&barrier);
+ global = 44;
+
+ dispatch_sync(dispatch_get_main_queue(), ^{
+ CFRunLoopStop(CFRunLoopGetCurrent());
+ });
+ });
+
+ CFRunLoopRun();
+ fprintf(stderr, "Done.\n");
+}
+
+// CHECK: Hello world.
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: Write of size 8
+// CHECK: Previous write of size 8
+// CHECK: Location is global
+// CHECK: Thread {{.*}} is a GCD worker thread
+// CHECK-NOT: failed to restore the stack
+// CHECK: Thread {{.*}} is a GCD worker thread
+// CHECK-NOT: failed to restore the stack
+// CHECK: Done.
diff --git a/test/tsan/Darwin/xpc-cancel.mm b/test/tsan/Darwin/xpc-cancel.mm
new file mode 100644
index 0000000..5e326b7
--- /dev/null
+++ b/test/tsan/Darwin/xpc-cancel.mm
@@ -0,0 +1,37 @@
+// RUN: %clang_tsan %s -o %t -framework Foundation
+// RUN: %run %t 2>&1 | FileCheck %s
+
+#import <Foundation/Foundation.h>
+#import <xpc/xpc.h>
+
+long global;
+
+int main(int argc, const char *argv[]) {
+ fprintf(stderr, "Hello world.\n");
+
+ dispatch_queue_t server_q = dispatch_queue_create("server.queue", DISPATCH_QUEUE_CONCURRENT);
+ xpc_connection_t server_conn = xpc_connection_create(NULL, server_q);
+
+ xpc_connection_set_event_handler(server_conn, ^(xpc_object_t client) {
+ if (client == XPC_ERROR_CONNECTION_INTERRUPTED || client == XPC_ERROR_CONNECTION_INVALID) {
+ global = 43;
+
+ dispatch_async(dispatch_get_main_queue(), ^{
+ CFRunLoopStop(CFRunLoopGetCurrent());
+ });
+ }
+ });
+ xpc_connection_resume(server_conn);
+
+ global = 42;
+
+ xpc_connection_cancel(server_conn);
+
+ CFRunLoopRun();
+
+ fprintf(stderr, "Done.\n");
+}
+
+// CHECK: Hello world.
+// CHECK-NOT: WARNING: ThreadSanitizer
+// CHECK: Done.
diff --git a/test/tsan/Darwin/xpc-race.mm b/test/tsan/Darwin/xpc-race.mm
index 9141da4..eaef4e0 100644
--- a/test/tsan/Darwin/xpc-race.mm
+++ b/test/tsan/Darwin/xpc-race.mm
@@ -1,5 +1,5 @@
// RUN: %clang_tsan %s -o %t -framework Foundation
-// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %deflake %run %t 2>&1 | FileCheck %s
+// RUN: %deflake %run %t 2>&1 | FileCheck %s
#import <Foundation/Foundation.h>
#import <xpc/xpc.h>
diff --git a/test/tsan/Darwin/xpc.mm b/test/tsan/Darwin/xpc.mm
index a939b02..2d6de26 100644
--- a/test/tsan/Darwin/xpc.mm
+++ b/test/tsan/Darwin/xpc.mm
@@ -1,5 +1,5 @@
// RUN: %clang_tsan %s -o %t -framework Foundation
-// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s
+// RUN: %run %t 2>&1 | FileCheck %s
#import <Foundation/Foundation.h>
#import <xpc/xpc.h>
diff --git a/test/tsan/Unit/lit.site.cfg.in b/test/tsan/Unit/lit.site.cfg.in
index 23894a8..40cf096 100644
--- a/test/tsan/Unit/lit.site.cfg.in
+++ b/test/tsan/Unit/lit.site.cfg.in
@@ -11,3 +11,13 @@
# FIXME: De-hardcode this path.
config.test_exec_root = "@COMPILER_RT_BINARY_DIR@/lib/tsan/tests"
config.test_source_root = config.test_exec_root
+
+if config.host_os == 'Darwin':
+ config.parallelism_group = config.darwin_sanitizer_parallelism_group_func
+
+ # On Darwin, we default to ignore_noninstrumented_modules=1, which also
+ # suppresses some races the tests are supposed to find. See tsan/lit.cfg.
+ if 'TSAN_OPTIONS' in config.environment:
+ config.environment['TSAN_OPTIONS'] += ':ignore_noninstrumented_modules=0'
+ else:
+ config.environment['TSAN_OPTIONS'] = 'ignore_noninstrumented_modules=0'
diff --git a/test/tsan/debug_external.cc b/test/tsan/debug_external.cc
new file mode 100644
index 0000000..1b340b2
--- /dev/null
+++ b/test/tsan/debug_external.cc
@@ -0,0 +1,66 @@
+// RUN: %clangxx_tsan -O1 %s -o %t
+// RUN: %deflake %run %t 2>&1 | FileCheck %s
+
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "test.h"
+
+extern "C" {
+void __tsan_on_report(void *report);
+int __tsan_get_report_loc(void *report, unsigned long idx, const char **type,
+ void **addr, void **start,
+ unsigned long *size, int *tid, int *fd,
+ int *suppressable, void **trace,
+ unsigned long trace_size);
+int __tsan_get_report_loc_object_type(void *report, unsigned long idx,
+ const char **object_type);
+void *__tsan_external_register_tag(const char *object_type);
+void __tsan_external_assign_tag(void *addr, void *tag);
+}
+
+void *Thread(void *arg) {
+ barrier_wait(&barrier);
+ *((long *)arg) = 42;
+ return NULL;
+}
+
+int main() {
+ barrier_init(&barrier, 2);
+ void *tag = __tsan_external_register_tag("MyObject");
+ long *obj = (long *)malloc(sizeof(long));
+ fprintf(stderr, "obj = %p\n", obj);
+ // CHECK: obj = [[ADDR:0x[0-9a-f]+]]
+ __tsan_external_assign_tag(obj, tag);
+
+ pthread_t t;
+ pthread_create(&t, 0, Thread, obj);
+ *obj = 41;
+ barrier_wait(&barrier);
+ pthread_join(t, 0);
+ fprintf(stderr, "Done.\n");
+ return 0;
+}
+
+void __tsan_on_report(void *report) {
+ const char *type;
+ void *addr;
+ void *start;
+ unsigned long size;
+ int tid, fd, suppressable;
+ void *trace[16] = {0};
+ __tsan_get_report_loc(report, 0, &type, &addr, &start, &size, &tid, &fd,
+ &suppressable, trace, 16);
+ fprintf(stderr, "type = %s, start = %p, size = %ld\n", type, start, size);
+ // CHECK: type = heap, start = [[ADDR]], size = 8
+
+ const char *object_type;
+ __tsan_get_report_loc_object_type(report, 0, &object_type);
+ fprintf(stderr, "object_type = %s\n", object_type);
+ // CHECK: object_type = MyObject
+}
+
+// CHECK: Done.
+// CHECK: ThreadSanitizer: reported 1 warnings
diff --git a/test/tsan/lit.cfg b/test/tsan/lit.cfg
index 5d82cc9..3c98d1f 100644
--- a/test/tsan/lit.cfg
+++ b/test/tsan/lit.cfg
@@ -24,6 +24,10 @@
# On Darwin, we default to `abort_on_error=1`, which would make tests run
# much slower. Let's override this and run lit tests with 'abort_on_error=0'.
default_tsan_opts += ':abort_on_error=0'
+ # On Darwin, we default to ignore_noninstrumented_modules=1, which also
+ # suppresses some races the tests are supposed to find. Let's run without this
+ # setting, but turn it back on for Darwin tests (see Darwin/lit.local.cfg).
+ default_tsan_opts += ':ignore_noninstrumented_modules=0'
# Platform-specific default TSAN_OPTIONS for lit tests.
if default_tsan_opts:
@@ -83,3 +87,6 @@
# because the test hangs.
if config.target_arch != 'aarch64':
config.available_features.add('stable-runtime')
+
+if config.host_os == 'Darwin' and config.target_arch in ["x86_64", "x86_64h"]:
+ config.parallelism_group = "darwin-64bit-sanitizer"
diff --git a/test/ubsan/TestCases/Float/cast-overflow.cpp b/test/ubsan/TestCases/Float/cast-overflow.cpp
index 5f51553..85c5049 100644
--- a/test/ubsan/TestCases/Float/cast-overflow.cpp
+++ b/test/ubsan/TestCases/Float/cast-overflow.cpp
@@ -86,42 +86,42 @@
case '0': {
// Note that values between 0x7ffffe00 and 0x80000000 may or may not
// successfully round-trip, depending on the rounding mode.
- // CHECK-0: {{.*}}cast-overflow.cpp:[[@LINE+1]]:27: runtime error: value 2.14748{{.*}} is outside the range of representable values of type 'int'
+ // CHECK-0: {{.*}}cast-overflow.cpp:[[@LINE+1]]:27: runtime error: 2.14748{{.*}} is outside the range of representable values of type 'int'
static int test_int = MaxFloatRepresentableAsInt + 0x80;
// CHECK-0: SUMMARY: {{.*}}Sanitizer: float-cast-overflow {{.*}}cast-overflow.cpp:[[@LINE-1]]
return 0;
}
case '1': {
- // CHECK-1: {{.*}}cast-overflow.cpp:[[@LINE+1]]:27: runtime error: value -2.14748{{.*}} is outside the range of representable values of type 'int'
+ // CHECK-1: {{.*}}cast-overflow.cpp:[[@LINE+1]]:27: runtime error: -2.14748{{.*}} is outside the range of representable values of type 'int'
static int test_int = MinFloatRepresentableAsInt - 0x100;
return 0;
}
case '2': {
- // CHECK-2: {{.*}}cast-overflow.cpp:[[@LINE+2]]:37: runtime error: value -1 is outside the range of representable values of type 'unsigned int'
+ // CHECK-2: {{.*}}cast-overflow.cpp:[[@LINE+2]]:37: runtime error: -1 is outside the range of representable values of type 'unsigned int'
volatile float f = -1.0;
volatile unsigned u = (unsigned)f;
return 0;
}
case '3': {
- // CHECK-3: {{.*}}cast-overflow.cpp:[[@LINE+1]]:37: runtime error: value 4.2949{{.*}} is outside the range of representable values of type 'unsigned int'
+ // CHECK-3: {{.*}}cast-overflow.cpp:[[@LINE+1]]:37: runtime error: 4.2949{{.*}} is outside the range of representable values of type 'unsigned int'
static int test_int = (unsigned)(MaxFloatRepresentableAsUInt + 0x100);
return 0;
}
case '4': {
- // CHECK-4: {{.*}}cast-overflow.cpp:[[@LINE+1]]:27: runtime error: value {{.*}} is outside the range of representable values of type 'int'
+ // CHECK-4: {{.*}}cast-overflow.cpp:[[@LINE+1]]:27: runtime error: {{.*}} is outside the range of representable values of type 'int'
static int test_int = Inf;
return 0;
}
case '5': {
- // CHECK-5: {{.*}}cast-overflow.cpp:[[@LINE+1]]:27: runtime error: value {{.*}} is outside the range of representable values of type 'int'
+ // CHECK-5: {{.*}}cast-overflow.cpp:[[@LINE+1]]:27: runtime error: {{.*}} is outside the range of representable values of type 'int'
static int test_int = NaN;
return 0;
}
// Integer -> floating point overflow.
case '6': {
- // CHECK-6: cast-overflow.cpp:[[@LINE+2]]:{{34: runtime error: value 0xffffff00000000000000000000000001 is outside the range of representable values of type 'float'| __int128 not supported}}
+ // CHECK-6: cast-overflow.cpp:[[@LINE+2]]:{{34: runtime error: 0xffffff00000000000000000000000001 is outside the range of representable values of type 'float'| __int128 not supported}}
#if defined(__SIZEOF_INT128__) && !defined(_WIN32)
static int test_int = (float)(FloatMaxAsUInt128 + 1);
return 0;
@@ -135,16 +135,16 @@
// FIXME: The backend cannot lower __fp16 operations on x86 yet.
//case '7':
// (__fp16)65504; // ok
- // // CHECK-7: runtime error: value 65505 is outside the range of representable values of type '__fp16'
+ // // CHECK-7: runtime error: 65505 is outside the range of representable values of type '__fp16'
// return (__fp16)65505;
// Floating point -> floating point overflow.
case '8':
- // CHECK-8: {{.*}}cast-overflow.cpp:[[@LINE+1]]:19: runtime error: value 1e+39 is outside the range of representable values of type 'float'
+ // CHECK-8: {{.*}}cast-overflow.cpp:[[@LINE+1]]:19: runtime error: 1e+39 is outside the range of representable values of type 'float'
return (float)1e39;
case '9':
volatile long double ld = 300.0;
- // CHECK-9: {{.*}}cast-overflow.cpp:[[@LINE+1]]:14: runtime error: value 300 is outside the range of representable values of type 'char'
+ // CHECK-9: {{.*}}cast-overflow.cpp:[[@LINE+1]]:14: runtime error: 300 is outside the range of representable values of type 'char'
char c = ld;
return c;
}
diff --git a/test/ubsan/TestCases/Integer/summary.cpp b/test/ubsan/TestCases/Integer/summary.cpp
index e687afa..8726c14 100644
--- a/test/ubsan/TestCases/Integer/summary.cpp
+++ b/test/ubsan/TestCases/Integer/summary.cpp
@@ -7,7 +7,7 @@
int main() {
(void)(uint64_t(10000000000000000000ull) + uint64_t(9000000000000000000ull));
- // CHECK-NOTYPE: SUMMARY: AddressSanitizer: undefined-behavior {{.*}}summary.cpp:[[@LINE-1]]:44
- // CHECK-TYPE: SUMMARY: AddressSanitizer: unsigned-integer-overflow {{.*}}summary.cpp:[[@LINE-2]]:44
+ // CHECK-NOTYPE: SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior {{.*}}summary.cpp:[[@LINE-1]]:44
+ // CHECK-TYPE: SUMMARY: UndefinedBehaviorSanitizer: unsigned-integer-overflow {{.*}}summary.cpp:[[@LINE-2]]:44
return 0;
}
diff --git a/test/ubsan/TestCases/Misc/bool.m b/test/ubsan/TestCases/Misc/bool.m
new file mode 100644
index 0000000..0430b45
--- /dev/null
+++ b/test/ubsan/TestCases/Misc/bool.m
@@ -0,0 +1,14 @@
+// RUN: %clang -fsanitize=bool %s -O3 -o %t
+// RUN: not %run %t 2>&1 | FileCheck %s
+// RUN: %env_ubsan_opts=print_summary=1:report_error_type=1 not %run %t 2>&1 | FileCheck %s --check-prefix=SUMMARY
+
+typedef char BOOL;
+unsigned char NotABool = 123;
+
+int main(int argc, char **argv) {
+ BOOL *p = (BOOL*)&NotABool;
+
+ // CHECK: bool.m:[[@LINE+1]]:10: runtime error: load of value 123, which is not a valid value for type 'BOOL'
+ return *p;
+ // SUMMARY: SUMMARY: {{.*}}Sanitizer: invalid-bool-load {{.*}}bool.m:[[@LINE-1]]
+}
diff --git a/test/ubsan/TestCases/Misc/log-path_test.cc b/test/ubsan/TestCases/Misc/log-path_test.cc
index 5b45f0b..40bb35a 100644
--- a/test/ubsan/TestCases/Misc/log-path_test.cc
+++ b/test/ubsan/TestCases/Misc/log-path_test.cc
@@ -32,5 +32,5 @@
return 0;
}
-// CHECK-ERROR: runtime error: value -4 is outside the range of representable values of type 'unsigned int'
+// CHECK-ERROR: runtime error: -4 is outside the range of representable values of type 'unsigned int'
diff --git a/test/ubsan/TestCases/Misc/missing_return.cpp b/test/ubsan/TestCases/Misc/missing_return.cpp
index 6808227..7b56b97 100644
--- a/test/ubsan/TestCases/Misc/missing_return.cpp
+++ b/test/ubsan/TestCases/Misc/missing_return.cpp
@@ -1,13 +1,10 @@
// RUN: %clangxx -fsanitize=return -g %s -O3 -o %t
// RUN: not %run %t 2>&1 | FileCheck %s
-// RUN: %env_ubsan_opts=print_stacktrace=1 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%os-STACKTRACE
+// RUN: %env_ubsan_opts=print_stacktrace=1 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-STACKTRACE
// CHECK: missing_return.cpp:[[@LINE+1]]:5: runtime error: execution reached the end of a value-returning function without returning a value
int f() {
-// Slow stack unwinding is not available on Darwin for now, see
-// https://code.google.com/p/address-sanitizer/issues/detail?id=137
-// CHECK-Linux-STACKTRACE: #0 {{.*}}f(){{.*}}missing_return.cpp:[[@LINE-3]]
-// CHECK-FreeBSD-STACKTRACE: #0 {{.*}}f(void){{.*}}missing_return.cpp:[[@LINE-4]]
+// CHECK-STACKTRACE: #0 {{.*}}f{{.*}}missing_return.cpp:[[@LINE-1]]
}
int main(int, char **argv) {
diff --git a/test/ubsan/TestCases/Misc/nullability.c b/test/ubsan/TestCases/Misc/nullability.c
new file mode 100644
index 0000000..37b8ddc
--- /dev/null
+++ b/test/ubsan/TestCases/Misc/nullability.c
@@ -0,0 +1,64 @@
+// RUN: %clang -w -fsanitize=nullability-arg,nullability-assign,nullability-return %s -O3 -o %t
+// RUN: %run %t foo 2>&1 | count 0
+// RUN: %run %t 2>&1 | FileCheck %s
+
+// CHECK: nullability.c:[[@LINE+2]]:51: runtime error: null pointer returned from function declared to never return null
+// CHECK-NEXT: nullability.c:[[@LINE+1]]:6: note: _Nonnull return type annotation specified here
+int *_Nonnull nonnull_retval1(int *p) { return p; }
+
+// CHECK: nullability.c:1001:19: runtime error: null pointer passed as argument 1, which is declared to never be null
+// CHECK-NEXT: nullability.c:[[@LINE+3]]:36: note: _Nonnull type annotation specified here
+// CHECK: nullability.c:1001:22: runtime error: null pointer passed as argument 2, which is declared to never be null
+// CHECK-NEXT: nullability.c:[[@LINE+1]]:56: note: _Nonnull type annotation specified here
+int *_Nonnull nonnull_retval2(int *_Nonnull arg1, int *_Nonnull arg2,
+ int *_Nullable arg3, int *arg4, int arg5, ...) {
+ return arg1;
+}
+
+// CHECK: nullability.c:1002:15: runtime error: null pointer passed as argument 1, which is declared to never be null
+// CHECK-NEXT: nullability.c:[[@LINE+1]]:23: note: _Nonnull type annotation specified here
+void nonnull_arg(int *_Nonnull p) {}
+
+void nonnull_assign1(int *p) {
+ int *_Nonnull local;
+// CHECK: nullability.c:[[@LINE+1]]:9: runtime error: _Nonnull binding to null pointer of type 'int * _Nonnull'
+ local = p;
+}
+
+void nonnull_assign2(int *p) {
+ int *_Nonnull arr[1];
+ // CHECK: nullability.c:[[@LINE+1]]:10: runtime error: _Nonnull binding to null pointer of type 'int * _Nonnull'
+ arr[0] = p;
+}
+
+struct S1 {
+ int *_Nonnull mptr;
+};
+
+void nonnull_assign3(int *p) {
+ struct S1 s;
+ // CHECK: nullability.c:[[@LINE+1]]:10: runtime error: _Nonnull binding to null pointer of type 'int * _Nonnull'
+ s.mptr = p;
+}
+
+// CHECK: nullability.c:[[@LINE+1]]:52: runtime error: _Nonnull binding to null pointer of type 'int * _Nonnull'
+void nonnull_init1(int *p) { int *_Nonnull local = p; }
+
+// CHECK: nullability.c:[[@LINE+2]]:53: runtime error: _Nonnull binding to null pointer of type 'int * _Nonnull'
+// CHECK: nullability.c:[[@LINE+1]]:56: runtime error: _Nonnull binding to null pointer of type 'int * _Nonnull'
+void nonnull_init2(int *p) { int *_Nonnull arr[] = {p, p}; }
+
+int main(int argc, char **argv) {
+ int *p = (argc > 1) ? &argc : ((int *)0);
+
+#line 1000
+ nonnull_retval1(p);
+ nonnull_retval2(p, p, p, p, 0, 0, 0, 0);
+ nonnull_arg(p);
+ nonnull_assign1(p);
+ nonnull_assign2(p);
+ nonnull_assign3(p);
+ nonnull_init1(p);
+ nonnull_init2(p);
+ return 0;
+}
diff --git a/test/ubsan/TestCases/TypeCheck/misaligned.cpp b/test/ubsan/TestCases/TypeCheck/misaligned.cpp
index 35b1ec3..b3ff358 100644
--- a/test/ubsan/TestCases/TypeCheck/misaligned.cpp
+++ b/test/ubsan/TestCases/TypeCheck/misaligned.cpp
@@ -11,7 +11,7 @@
// RUN: %run %t f1 2>&1 | FileCheck %s --check-prefix=CHECK-MEMFUN
// RUN: %run %t n1 2>&1 | FileCheck %s --check-prefix=CHECK-NEW
// RUN: %run %t u1 2>&1 | FileCheck %s --check-prefix=CHECK-UPCAST
-// RUN: %env_ubsan_opts=print_stacktrace=1 %run %t l1 2>&1 | FileCheck %s --check-prefix=CHECK-LOAD --check-prefix=CHECK-%os-STACK-LOAD
+// RUN: %env_ubsan_opts=print_stacktrace=1 %run %t l1 2>&1 | FileCheck %s --check-prefix=CHECK-LOAD --check-prefix=CHECK-STACK-LOAD
// RUN: %clangxx -fsanitize=alignment -fno-sanitize-recover=alignment %s -O3 -o %t
// RUN: not %run %t w1 2>&1 | FileCheck %s --check-prefix=CHECK-WILD
@@ -47,11 +47,7 @@
// CHECK-LOAD-NEXT: {{^ 00 00 00 01 02 03 04 05}}
// CHECK-LOAD-NEXT: {{^ \^}}
return *p && 0;
- // Slow stack unwinding is disabled on Darwin for now, see
- // https://code.google.com/p/address-sanitizer/issues/detail?id=137
- // CHECK-Linux-STACK-LOAD: #0 {{.*}}main{{.*}}misaligned.cpp
- // Check for the already checked line to avoid lit error reports.
- // CHECK-Darwin-STACK-LOAD: {{ }}
+ // CHECK-STACK-LOAD: #0 {{.*}}main{{.*}}misaligned.cpp
case 's':
// CHECK-STORE: misaligned.cpp:[[@LINE+4]]{{(:5)?}}: runtime error: store to misaligned address [[PTR:0x[0-9a-f]*]] for type 'int', which requires 4 byte alignment
diff --git a/unittests/lit.common.unit.cfg b/unittests/lit.common.unit.cfg
index 2bd8f37..475b22d 100644
--- a/unittests/lit.common.unit.cfg
+++ b/unittests/lit.common.unit.cfg
@@ -28,3 +28,13 @@
config.environment['TMP'] = os.environ['TMP']
if 'TEMP' in os.environ:
config.environment['TEMP'] = os.environ['TEMP']
+
+if config.host_os == 'Darwin':
+ # Only run up to 3 64-bit sanitized processes simultaneously on Darwin.
+ # Using more scales badly and hogs the system due to inefficient handling
+ # of large mmap'd regions (terabytes) by the kernel.
+ lit_config.parallelism_groups["darwin-64bit-sanitizer"] = 3
+
+ def darwin_sanitizer_parallelism_group_func(test):
+ return "darwin-64bit-sanitizer" if "x86_64" in test.file_path else ""
+ config.darwin_sanitizer_parallelism_group_func = darwin_sanitizer_parallelism_group_func