Skip to content

Commit 5652986

Browse files
Lukasadanliew-apple
authored andcommitted
[Sanitizers] Add Scudo support (#28538)
LLVM ships a hardened memory allocator called Scudo: https://llvm.org/docs/ScudoHardenedAllocator.html. This allocator provides additional mitigations against heap-based vulnerabilities, but retains sufficient performance to be safely run in production applications. While ideal Swift applications are obviously written in pure Swift, in practice most applications contain some amount of code written in less-safe languages. Additionally, plenty of Swift programs themselves contain unsafe code, particularly when attempting to implement high-performance data structures. These sources of unsafety introduce the risk of memory issues, and having the option to use the Scudo allocator is a useful defense-in-depth tool. This patch enables `-sanitize=scudo` as an extra `swiftc` flag. This sanitizer is only supported on Linux, so no further work is required to enable it on Windows or Apple platforms. As this "sanitizer" is only a runtime component, we do not require any wider changes to instrument code. This is similar to clang's `-fsanitize=scudo` flag. The Swift driver rejects platforms that don't support Scudo using an existing mechanism in the Driver that is not part of this patch. This mechanism is in swift::parseSanitizerArgValues(...) (lib/Option/SanitizerOptions.cpp). The mechanism determines if a sanitizer is supported by checking for the existence of the corresponding sanitizer runtime library in the compiler's resource directory. The Scudo runtime library currently only exists in the Linux compiler resource directory. This results in the driver only allowing Scudo when targeting Linux.
1 parent 2ec583f commit 5652986

File tree

5 files changed

+81
-0
lines changed

5 files changed

+81
-0
lines changed

include/swift/Basic/Sanitizers.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,6 @@ SANITIZER(0, Address, address, asan)
2424
SANITIZER(1, Thread, thread, tsan)
2525
SANITIZER(2, Undefined, undefined, ubsan)
2626
SANITIZER(3, Fuzzer, fuzzer, fuzzer)
27+
SANITIZER(4, Scudo, scudo, scudo)
2728

2829
#undef SANITIZER

lib/Option/SanitizerOptions.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,36 @@ OptionSet<SanitizerKind> swift::parseSanitizerArgValues(
182182
+ toStringRef(SanitizerKind::Thread)).toStringRef(b2));
183183
}
184184

185+
// Scudo can only be run with ubsan.
186+
if (sanitizerSet & SanitizerKind::Scudo) {
187+
OptionSet<SanitizerKind> allowedSet;
188+
allowedSet |= SanitizerKind::Scudo;
189+
allowedSet |= SanitizerKind::Undefined;
190+
191+
auto forbiddenOptions = sanitizerSet - allowedSet;
192+
193+
if (forbiddenOptions) {
194+
SanitizerKind forbidden;
195+
196+
if (forbiddenOptions & SanitizerKind::Address) {
197+
forbidden = SanitizerKind::Address;
198+
} else if (forbiddenOptions & SanitizerKind::Thread) {
199+
forbidden = SanitizerKind::Thread;
200+
} else {
201+
assert(forbiddenOptions & SanitizerKind::Fuzzer);
202+
forbidden = SanitizerKind::Fuzzer;
203+
}
204+
205+
SmallString<128> b1;
206+
SmallString<128> b2;
207+
Diags.diagnose(SourceLoc(), diag::error_argument_not_allowed_with,
208+
(A->getOption().getPrefixedName()
209+
+ toStringRef(SanitizerKind::Scudo)).toStringRef(b1),
210+
(A->getOption().getPrefixedName()
211+
+ toStringRef(forbidden)).toStringRef(b2));
212+
}
213+
}
214+
185215
return sanitizerSet;
186216
}
187217

test/Driver/Inputs/fake-resource-dir/lib/swift/clang/lib/linux/libclang_rt.scudo-x86_64.a

Whitespace-only changes.

test/Driver/sanitize_scudo.swift

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// RUN: %swiftc_driver -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=scudo -target x86_64-unknown-linux-gnu %s | %FileCheck -check-prefix=SCUDO_LINUX %s
2+
// RUN: not %swiftc_driver -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=scudo -target arm64-apple-ios7.1 %s 2>&1 | %FileCheck -check-prefix=SCUDO_IOS %s
3+
// RUN: not %swiftc_driver -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=scudo -target arm64-apple-tvos9.0 %s 2>&1 | %FileCheck -check-prefix=SCUDO_tvOS %s
4+
// RUN: not %swiftc_driver -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=scudo -target armv7k-apple-watchos2.0 %s 2>&1 | %FileCheck -check-prefix=SCUDO_watchOS %s
5+
// RUN: not %swiftc_driver -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=scudo -target i386-apple-watchos2.0 %s 2>&1 | %FileCheck -check-prefix=SCUDO_watchOS_SIM %s
6+
// RUN: not %swiftc_driver -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=scudo -target i386-apple-macosx10.9 %s 2>&1 | %FileCheck -check-prefix=SCUDO_OSX_32 %s
7+
// RUN: not %swiftc_driver -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=scudo -target x86_64-apple-ios7.1 %s 2>&1 | %FileCheck -check-prefix=SCUDO_IOSSIM %s
8+
// RUN: not %swiftc_driver -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=scudo -target x86_64-apple-macosx10.9 %s 2>&1 | %FileCheck -check-prefix=SCUDO_OSX_64 %s
9+
// RUN: not %swiftc_driver -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=scudo -target x86_64-apple-tvos9.0 %s 2>&1 | %FileCheck -check-prefix=SCUDO_tvOS_SIM %s
10+
// RUN: not %swiftc_driver -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=scudo -target x86_64-unknown-windows-msvc %s 2>&1 | %FileCheck -check-prefix=SCUDO_WINDOWS %s
11+
12+
// RUN: not %swiftc_driver -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=scudo,address -target x86_64-unknown-linux-gnu %s 2>&1 | %FileCheck -check-prefix=SCUDO_ASAN %s
13+
// RUN: not %swiftc_driver -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=scudo,thread -target x86_64-unknown-linux-gnu %s 2>&1 | %FileCheck -check-prefix=SCUDO_TSAN %s
14+
// RUN: %swiftc_driver -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=scudo,undefined -target x86_64-unknown-linux-gnu %s 2>&1 | %FileCheck -check-prefix=SCUDO_UBSAN_LINUX %s
15+
16+
17+
/*
18+
* Make sure we don't accidentally add the sanitizer library path when building libraries or modules
19+
*/
20+
// RUN: %swiftc_driver -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -emit-library -sanitize=scudo -target x86_64-unknown-linux-gnu %s 2>&1 | %FileCheck -check-prefix=SCUDO_LIBRARY_LINUX %s
21+
22+
// SCUDO_LINUX: bin/clang
23+
// SCUDO_LINUX-SAME: -pie
24+
// SCUDO_LINUX-SAME: -fsanitize=scudo
25+
// SCUDO_OSX_32: unsupported option '-sanitize=scudo' for target 'i386-apple-macosx10.9'
26+
// SCUDO_OSX_64: unsupported option '-sanitize=scudo' for target 'x86_64-apple-macosx10.9'
27+
// SCUDO_IOSSIM: unsupported option '-sanitize=scudo' for target 'x86_64-apple-ios7.1'
28+
// SCUDO_IOS: unsupported option '-sanitize=scudo' for target 'arm64-apple-ios7.1'
29+
// SCUDO_tvOS_SIM: unsupported option '-sanitize=scudo' for target 'x86_64-apple-tvos9.0'
30+
// SCUDO_tvOS: unsupported option '-sanitize=scudo' for target 'arm64-apple-tvos9.0'
31+
// SCUDO_watchOS_SIM: unsupported option '-sanitize=scudo' for target 'i386-apple-watchos2.0'
32+
// SCUDO_watchOS: unsupported option '-sanitize=scudo' for target 'armv7k-apple-watchos2.0'
33+
// SCUDO_WINDOWS: unsupported option '-sanitize=scudo' for target 'x86_64-unknown-windows-msvc'
34+
35+
// SCUDO_ASAN: argument '-sanitize=scudo' is not allowed with '-sanitize=address'
36+
// SCUDO_TSAN: argument '-sanitize=scudo' is not allowed with '-sanitize=thread'
37+
// SCUDO_UBSAN_LINUX: -fsanitize=undefined,scudo
38+
// SCUDO_LIBRARY_LINUX: bin/clang
39+
// SCUDO_LIBRARY_LINUX-NOT: -fsanitize=scudo
40+

test/Sanitizers/scudo.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// RUN: %target-swiftc_driver %s -target %sanitizers-target-triple -g -sanitize=scudo -o %t_scudo-binary
2+
// RUN: not %target-run %t_scudo-binary 2>&1 | %FileCheck %s
3+
// REQUIRES: executable_test
4+
// REQUIRES: OS=linux-gnu
5+
6+
let allocated = UnsafeMutableRawPointer.allocate(byteCount: 128, alignment: 1)
7+
allocated.deallocate()
8+
allocated.deallocate()
9+
10+
// CHECK: ERROR: invalid chunk state

0 commit comments

Comments
 (0)