Skip to content

Commit 5d34f66

Browse files
committed
Merge pull request #2076 from apple/asan
Add experimental support for Thread Sanitizer
2 parents 709951e + 246bc7a commit 5d34f66

File tree

12 files changed

+116
-34
lines changed

12 files changed

+116
-34
lines changed

include/swift/AST/DiagnosticsFrontend.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ ERROR(error_unsupported_target_arch, none,
5555
ERROR(error_unsupported_opt_for_target, none,
5656
"unsupported option '%0' for target '%1'", (StringRef, StringRef))
5757

58+
ERROR(error_argument_not_allowed_with, none,
59+
"argument '%0' is not allowed with '%1'", (StringRef, StringRef))
60+
5861
ERROR(cannot_open_file,none,
5962
"cannot open file '%0' (%1)", (StringRef, StringRef))
6063
ERROR(cannot_open_serialized_file,none,

include/swift/Basic/Sanitizers.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ namespace swift {
1818
enum class SanitizerKind : unsigned {
1919
None = 0,
2020
Address,
21+
Thread,
2122
};
2223

2324
} // end namespace swift

include/swift/Runtime/Concurrent.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,7 @@ template <class EntryTy> class ConcurrentMap {
297297

298298
// Try to set the edge to the new node.
299299
if (std::atomic_compare_exchange_strong_explicit(edge, &node, newNode,
300-
std::memory_order_release,
300+
std::memory_order_acq_rel,
301301
std::memory_order_acquire)) {
302302
// If that succeeded, cache and report that we created a new node.
303303
LastSearch.store(newNode, std::memory_order_release);

lib/Driver/ToolChains.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1031,6 +1031,9 @@ toolchains::Darwin::constructInvocation(const LinkJobAction &job,
10311031
if (context.OI.SelectedSanitizer == SanitizerKind::Address)
10321032
addLinkSanitizerLibArgsForDarwin(context.Args, Arguments, "asan", *this);
10331033

1034+
if (context.OI.SelectedSanitizer == SanitizerKind::Thread)
1035+
addLinkSanitizerLibArgsForDarwin(context.Args, Arguments, "tsan", *this);
1036+
10341037
if (context.Args.hasArg(options::OPT_embed_bitcode,
10351038
options::OPT_embed_bitcode_marker)) {
10361039
Arguments.push_back("-bitcode_bundle");

lib/IRGen/GenMeta.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1085,6 +1085,10 @@ void irgen::emitLazyCacheAccessFunction(IRGenModule &IGM,
10851085
// FIXME: Technically should be "consume", but that introduces barriers in the
10861086
// current LLVM ARM backend.
10871087
auto load = IGF.Builder.CreateLoad(cache);
1088+
// Make this barrier explicit when building for TSan to avoid false positives.
1089+
if (IGM.Opts.Sanitize == SanitizerKind::Thread)
1090+
load->setOrdering(llvm::AtomicOrdering::Acquire);
1091+
10881092

10891093
// Compare the load result against null.
10901094
auto isNullBB = IGF.createBasicBlock("cacheIsNull");

lib/IRGen/IRGen.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,11 @@ static void addAddressSanitizerPasses(const PassManagerBuilder &Builder,
9090
PM.add(createAddressSanitizerModulePass());
9191
}
9292

93+
static void addThreadSanitizerPass(const PassManagerBuilder &Builder,
94+
legacy::PassManagerBase &PM) {
95+
PM.add(createThreadSanitizerPass());
96+
}
97+
9398
std::tuple<llvm::TargetOptions, std::string, std::vector<std::string>>
9499
swift::getIRTargetOptions(IRGenOptions &Opts, ASTContext &Ctx) {
95100
// Things that maybe we should collect from the command line:
@@ -152,6 +157,13 @@ void swift::performLLVMOptimizations(IRGenOptions &Opts, llvm::Module *Module,
152157
addAddressSanitizerPasses);
153158
}
154159

160+
if (Opts.Sanitize == SanitizerKind::Thread) {
161+
PMBuilder.addExtension(PassManagerBuilder::EP_OptimizerLast,
162+
addThreadSanitizerPass);
163+
PMBuilder.addExtension(PassManagerBuilder::EP_EnabledOnOptLevel0,
164+
addThreadSanitizerPass);
165+
}
166+
155167
// Configure the function passes.
156168
legacy::FunctionPassManager FunctionPasses(Module);
157169
FunctionPasses.add(createTargetTransformInfoWrapperPass(

lib/IRGen/IRGenFunction.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ IRGenFunction::IRGenFunction(IRGenModule &IGM,
5353
// file or via annotations.
5454
if (IGM.Opts.Sanitize == SanitizerKind::Address)
5555
Fn->addFnAttr(llvm::Attribute::SanitizeAddress);
56+
if (IGM.Opts.Sanitize == SanitizerKind::Thread)
57+
Fn->addFnAttr(llvm::Attribute::SanitizeThread);
5658

5759
emitPrologue();
5860
}

lib/Option/SanitizerOptions.cpp

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,18 @@
1616
//===----------------------------------------------------------------------===//
1717

1818
#include "swift/Option/SanitizerOptions.h"
19+
#include "swift/Basic/Platform.h"
1920
#include "swift/AST/DiagnosticEngine.h"
2021
#include "swift/AST/DiagnosticsFrontend.h"
22+
#include "llvm/ADT/StringSwitch.h"
2123
using namespace swift;
2224

2325
static StringRef toStringRef(const SanitizerKind kind) {
2426
switch (kind) {
2527
case SanitizerKind::Address:
2628
return "address";
29+
case SanitizerKind::Thread:
30+
return "thread";
2731
case SanitizerKind::None:
2832
llvm_unreachable("Getting a name for SanitizerKind::None");
2933
}
@@ -36,23 +40,50 @@ SanitizerKind swift::parseSanitizerArgValues(const llvm::opt::Arg *A,
3640
SanitizerKind kind = SanitizerKind::None;
3741

3842
// Find the sanitizer kind.
39-
// TODO: Add support for dealing with multiple sanitizers.
43+
SanitizerKind pKind = SanitizerKind::None;
4044
for (int i = 0, n = A->getNumValues(); i != n; ++i) {
41-
const char *Value = A->getValue(i);
42-
if (StringRef(Value).equals("address")) {
43-
kind = SanitizerKind::Address;
44-
} else {
45+
kind =
46+
llvm::StringSwitch<SanitizerKind>(A->getValue(i))
47+
.Case("address", SanitizerKind::Address)
48+
.Case("thread", SanitizerKind::Thread)
49+
.Default(SanitizerKind::None);
50+
51+
if (kind == SanitizerKind::None) {
4552
Diags.diagnose(SourceLoc(), diag::error_unsupported_option_argument,
46-
A->getOption().getName(), A->getValue(i));
53+
A->getOption().getPrefixedName(), A->getValue(i));
4754
return kind;
4855
}
56+
57+
// Currently, more than one sanitizer cannot be enabled at the same time.
58+
if (pKind != SanitizerKind::None && pKind != kind) {
59+
SmallString<128> pb;
60+
SmallString<128> b;
61+
Diags.diagnose(SourceLoc(), diag::error_argument_not_allowed_with,
62+
(A->getOption().getPrefixedName() + toStringRef(pKind)).toStringRef(pb),
63+
(A->getOption().getPrefixedName() + toStringRef(kind)).toStringRef(b));
64+
}
65+
pKind = kind;
4966
}
5067

68+
if (kind == SanitizerKind::None)
69+
return kind;
70+
5171
// Check if the target is supported for this sanitizer.
52-
if (!Triple.isOSDarwin() && kind != SanitizerKind::None) {
53-
SmallVector<char, 128> buffer;
72+
// None of the sanitizers work on Linux right now.
73+
if (!Triple.isOSDarwin()) {
74+
SmallString<128> b;
75+
Diags.diagnose(SourceLoc(), diag::error_unsupported_opt_for_target,
76+
(A->getOption().getPrefixedName() + toStringRef(kind)).toStringRef(b),
77+
Triple.getTriple());
78+
}
79+
// Thread Sanitizer only works on OS X and the simulators. It's only supported
80+
// on 64 bit arcitectures.
81+
if (kind == SanitizerKind::Thread &&
82+
(!(Triple.isMacOSX() || tripleIsAnySimulator(Triple)) ||
83+
!Triple.isArch64Bit())) {
84+
SmallString<128> b;
5485
Diags.diagnose(SourceLoc(), diag::error_unsupported_opt_for_target,
55-
(A->getOption().getName() + toStringRef(kind)).toStringRef(buffer),
86+
(A->getOption().getPrefixedName() + toStringRef(kind)).toStringRef(b),
5687
Triple.getTriple());
5788
}
5889

stdlib/public/core/Arrays.swift.gyb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -539,8 +539,10 @@ extension ${Self} : RangeReplaceableCollection, _ArrayProtocol {
539539
// unnecessary uniqueness check. We disable inlining here to curb code
540540
// growth.
541541
_buffer = ${Self}._allocateBufferUninitialized(minimumCapacity: count)
542+
_buffer.count = count
542543
}
543-
_buffer.count = count
544+
// Can’t store count here because the buffer might be pointing to the
545+
// shared empty array.
544546
}
545547

546548
/// Entry point for `Array` literal construction; builds and returns

stdlib/public/runtime/ProtocolConformance.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -560,10 +560,9 @@ swift::swift_conformsToProtocol(const Metadata *type,
560560
return FoundConformance.first;
561561
}
562562

563-
unsigned failedGeneration = ConformanceCacheGeneration;
564-
565563
// If we didn't have an up-to-date cache entry, scan the conformance records.
566564
C.SectionsToScanLock.lock();
565+
unsigned failedGeneration = ConformanceCacheGeneration;
567566

568567
// If we have no new information to pull in (and nobody else pulled in
569568
// new information while we waited on the lock), we're done.

test/Driver/sanitizers.swift

Lines changed: 44 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,54 @@
1-
// RUN: %swiftc_driver -driver-print-jobs -sanitize=address -target x86_64-apple-macosx10.9 %s | FileCheck -check-prefix=ASAN -check-prefix=OSX %s
2-
3-
// RUN: %swiftc_driver -driver-print-jobs -sanitize=address -target x86_64-apple-ios7.1 %s | FileCheck -check-prefix=ASAN -check-prefix=IOSSIM %s
4-
// RUN: %swiftc_driver -driver-print-jobs -sanitize=address -target arm64-apple-ios7.1 %s | FileCheck -check-prefix=ASAN -check-prefix=IOS %s
5-
6-
// RUN: %swiftc_driver -driver-print-jobs -sanitize=address -target x86_64-apple-tvos9.0 %s | FileCheck -check-prefix=ASAN -check-prefix=tvOS_SIM %s
7-
// RUN: %swiftc_driver -driver-print-jobs -sanitize=address -target arm64-apple-tvos9.0 %s | FileCheck -check-prefix=ASAN -check-prefix=tvOS %s
8-
9-
// RUN: %swiftc_driver -driver-print-jobs -sanitize=address -target i386-apple-watchos2.0 %s | FileCheck -check-prefix=ASAN -check-prefix=watchOS_SIM %s
10-
// RUN: %swiftc_driver -driver-print-jobs -sanitize=address -target armv7k-apple-watchos2.0 %s | FileCheck -check-prefix=ASAN -check-prefix=watchOS %s
11-
12-
// RUN: not %swiftc_driver -driver-print-jobs -sanitize=address -target x86_64-unknown-linux-gnu %s 2>&1 | FileCheck -check-prefix=LINUX %s
1+
// RUN: %swiftc_driver -driver-print-jobs -sanitize=address -target x86_64-apple-macosx10.9 %s | FileCheck -check-prefix=ASAN -check-prefix=ASAN_OSX %s
2+
// RUN: %swiftc_driver -driver-print-jobs -sanitize=address -target x86_64-apple-ios7.1 %s | FileCheck -check-prefix=ASAN -check-prefix=ASAN_IOSSIM %s
3+
// RUN: %swiftc_driver -driver-print-jobs -sanitize=address -target arm64-apple-ios7.1 %s | FileCheck -check-prefix=ASAN -check-prefix=ASAN_IOS %s
4+
// RUN: %swiftc_driver -driver-print-jobs -sanitize=address -target x86_64-apple-tvos9.0 %s | FileCheck -check-prefix=ASAN -check-prefix=ASAN_tvOS_SIM %s
5+
// RUN: %swiftc_driver -driver-print-jobs -sanitize=address -target arm64-apple-tvos9.0 %s | FileCheck -check-prefix=ASAN -check-prefix=ASAN_tvOS %s
6+
// RUN: %swiftc_driver -driver-print-jobs -sanitize=address -target i386-apple-watchos2.0 %s | FileCheck -check-prefix=ASAN -check-prefix=ASAN_watchOS_SIM %s
7+
// RUN: %swiftc_driver -driver-print-jobs -sanitize=address -target armv7k-apple-watchos2.0 %s | FileCheck -check-prefix=ASAN -check-prefix=ASAN_watchOS %s
8+
// RUN: not %swiftc_driver -driver-print-jobs -sanitize=address -target x86_64-unknown-linux-gnu %s 2>&1 | FileCheck -check-prefix=ASAN_LINUX %s
9+
10+
// RUN: %swiftc_driver -driver-print-jobs -sanitize=thread -target x86_64-apple-macosx10.9 %s | FileCheck -check-prefix=TSAN -check-prefix=TSAN_OSX %s
11+
// RUN: not %swiftc_driver -driver-print-jobs -sanitize=thread -target x86-apple-macosx10.9 %s 2>&1 | FileCheck -check-prefix=TSAN_OSX_32 %s
12+
// RUN: %swiftc_driver -driver-print-jobs -sanitize=thread -target x86_64-apple-ios7.1 %s | FileCheck -check-prefix=TSAN -check-prefix=TSAN_IOSSIM %s
13+
// RUN: not %swiftc_driver -driver-print-jobs -sanitize=thread -target arm64-apple-ios7.1 %s 2>&1 | FileCheck -check-prefix=TSAN_IOS %s
14+
// RUN: %swiftc_driver -driver-print-jobs -sanitize=thread -target x86_64-apple-tvos9.0 %s | FileCheck -check-prefix=TSAN -check-prefix=TSAN_tvOS_SIM %s
15+
// RUN: not %swiftc_driver -driver-print-jobs -sanitize=thread -target arm64-apple-tvos9.0 %s 2>&1 | FileCheck -check-prefix=TSAN_tvOS %s
16+
// RUN: not %swiftc_driver -driver-print-jobs -sanitize=thread -target i386-apple-watchos2.0 %s 2>&1 | FileCheck -check-prefix=TSAN_watchOS_SIM %s
17+
// RUN: not %swiftc_driver -driver-print-jobs -sanitize=thread -target armv7k-apple-watchos2.0 %s 2>&1 | FileCheck -check-prefix=TSAN_watchOS %s
18+
// RUN: not %swiftc_driver -driver-print-jobs -sanitize=thread -target x86_64-unknown-linux-gnu %s 2>&1 | FileCheck -check-prefix=TSAN_LINUX %s
1319

1420
// RUN: not %swiftc_driver -driver-print-jobs -sanitize=address,unknown %s 2>&1 | FileCheck -check-prefix=BADARG %s
1521
// RUN: not %swiftc_driver -driver-print-jobs -sanitize=address -sanitize=unknown %s 2>&1 | FileCheck -check-prefix=BADARG %s
22+
// RUN: not %swiftc_driver -driver-print-jobs -sanitize=address,thread %s 2>&1 | FileCheck -check-prefix=INCOMPATIBLESANITIZERS %s
1623

1724
// ASAN: swift
1825
// ASAN: -sanitize=address
1926

20-
// OSX: lib/swift/clang/lib/darwin/libclang_rt.asan_osx_dynamic.dylib
21-
// IOS: lib/swift/clang/lib/darwin/libclang_rt.asan_ios_dynamic.dylib
22-
// IOSSIM: lib/swift/clang/lib/darwin/libclang_rt.asan_iossim_dynamic.dylib
23-
// tvOS: lib/swift/clang/lib/darwin/libclang_rt.asan_tvos_dynamic.dylib
24-
// tvOS_SIM: lib/swift/clang/lib/darwin/libclang_rt.asan_tvossim_dynamic.dylib
25-
// watchOS: lib/swift/clang/lib/darwin/libclang_rt.asan_watchos_dynamic.dylib
26-
// watchOS_SIM: lib/swift/clang/lib/darwin/libclang_rt.asan_watchossim_dynamic.dylib
27-
// LINUX: unsupported option 'sanitize=address' for target 'x86_64-unknown-linux-gnu'
27+
// ASAN_OSX: lib/swift/clang/lib/darwin/libclang_rt.asan_osx_dynamic.dylib
28+
// ASAN_IOSSIM: lib/swift/clang/lib/darwin/libclang_rt.asan_iossim_dynamic.dylib
29+
// ASAN_IOS: lib/swift/clang/lib/darwin/libclang_rt.asan_ios_dynamic.dylib
30+
// ASAN_tvOS_SIM: lib/swift/clang/lib/darwin/libclang_rt.asan_tvossim_dynamic.dylib
31+
// ASAN_tvOS: lib/swift/clang/lib/darwin/libclang_rt.asan_tvos_dynamic.dylib
32+
// ASAN_watchOS_SIM: lib/swift/clang/lib/darwin/libclang_rt.asan_watchossim_dynamic.dylib
33+
// ASAN_watchOS: lib/swift/clang/lib/darwin/libclang_rt.asan_watchos_dynamic.dylib
34+
// ASAN_LINUX: unsupported option '-sanitize=address' for target 'x86_64-unknown-linux-gnu'
2835

2936
// ASAN: -rpath @executable_path
3037

31-
// BADARG: unsupported argument 'unknown' to option 'sanitize='
38+
// TSAN: swift
39+
// TSAN: -sanitize=thread
40+
41+
// TSAN_OSX: lib/swift/clang/lib/darwin/libclang_rt.tsan_osx_dynamic.dylib
42+
// TSAN_OSX_32: unsupported option '-sanitize=thread' for target 'x86-apple-macosx10.9'
43+
// TSAN_IOSSIM: lib/swift/clang/lib/darwin/libclang_rt.tsan_iossim_dynamic.dylib
44+
// TSAN_IOS: unsupported option '-sanitize=thread' for target 'arm64-apple-ios7.1'
45+
// TSAN_tvOS_SIM: lib/swift/clang/lib/darwin/libclang_rt.tsan_tvossim_dynamic.dylib
46+
// TSAN_tvOS: unsupported option '-sanitize=thread' for target 'arm64-apple-tvos9.0'
47+
// TSAN_watchOS_SIM: unsupported option '-sanitize=thread' for target 'i386-apple-watchos2.0'
48+
// TSAN_watchOS: unsupported option '-sanitize=thread' for target 'armv7k-apple-watchos2.0'
49+
// TSAN_LINUX: unsupported option '-sanitize=thread' for target 'x86_64-unknown-linux-gnu'
50+
51+
// TSAN: -rpath @executable_path
52+
53+
// BADARG: unsupported argument 'unknown' to option '-sanitize='
54+
// INCOMPATIBLESANITIZERS: argument '-sanitize=address' is not allowed with '-sanitize=thread'

test/IRGen/sanitizer-attributes.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
// This test verifies that we add the function attributes used by sanitizers.
22

33
// RUN: %target-swift-frontend -emit-ir -sanitize=address %s | FileCheck %s -check-prefix=ASAN
4+
// RUN: %target-swift-frontend -emit-ir -target x86_64-apple-macosx10.9 -sanitize=thread %s | FileCheck %s -check-prefix=TSAN
45

56
// XFAIL: linux
67

78
func test() {
89
}
910

1011
// ASAN: Function Attrs: sanitize_address
12+
// TSAN: Function Attrs: sanitize_thread

0 commit comments

Comments
 (0)