Skip to content

Commit 3b65599

Browse files
authored
Add support for the Scudo sanitizer. (#2717)
* Add support for the Scudo sanitizer. + Test sanitizers with build plans The Swift PR swiftlang/swift#28538 adds support for the Scudo hardened allocator to swiftc. This patch adds support for it to Swift Package Manager, allowing the Package Manager to correctly plumb Scudo through the entire build pipeline. See the aforementioned PR for more discussion on the value of supporting Scudo. (cherry picked from commit 594ccaa) * Test sanitizers with build plans. (#2720) The current tests for sanitizers in SwiftPM are essentially "live": they build a package that will do something to trigger a sanitizer, and then run it and confirm it fails. This is a pretty fragile way to test, and it exposes SwiftPM to a number of possible test failures that don't have anything to do with the core mission (exposing the sanitizer). This patch replaces those tests with build plan tests that validate that SwiftPM correctly invokes swiftc and clang. These tests are acceptable because for now SwiftPM doesn't have any smarts with the sanitizers: it just passes them all through to swiftc and clang. This patch also deletes the current live tests, as they are no longer necessary. (cherry picked from commit 984618b)
1 parent 021eea9 commit 3b65599

File tree

12 files changed

+71
-127
lines changed

12 files changed

+71
-127
lines changed

Fixtures/Miscellaneous/ThreadRace/.gitignore

Lines changed: 0 additions & 4 deletions
This file was deleted.

Fixtures/Miscellaneous/ThreadRace/Package.swift

Lines changed: 0 additions & 18 deletions
This file was deleted.

Fixtures/Miscellaneous/ThreadRace/README.md

Lines changed: 0 additions & 3 deletions
This file was deleted.

Fixtures/Miscellaneous/ThreadRace/Sources/exec/main.swift

Lines changed: 0 additions & 3 deletions
This file was deleted.

Fixtures/Miscellaneous/ThreadRace/Sources/lib/lib.swift

Lines changed: 0 additions & 16 deletions
This file was deleted.

Fixtures/Miscellaneous/ThreadRace/Tests/LinuxMain.swift

Lines changed: 0 additions & 8 deletions
This file was deleted.

Fixtures/Miscellaneous/ThreadRace/Tests/libTests/XCTestManifests.swift

Lines changed: 0 additions & 15 deletions
This file was deleted.

Fixtures/Miscellaneous/ThreadRace/Tests/libTests/libTests.swift

Lines changed: 0 additions & 8 deletions
This file was deleted.

Sources/SPMBuildCore/Sanitizers.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,15 @@ public enum Sanitizer: String, Encodable {
1616
case address
1717
case thread
1818
case undefined
19+
case scudo
1920

2021
/// Return an established short name for a sanitizer, e.g. "asan".
2122
public var shortName: String {
2223
switch self {
2324
case .address: return "asan"
2425
case .thread: return "tsan"
2526
case .undefined: return "ubsan"
27+
case .scudo: return "scudo"
2628
}
2729
}
2830
}
@@ -72,6 +74,7 @@ extension Sanitizer: StringEnumArgument {
7274
public static let completion: ShellCompletion = .values([
7375
(address.rawValue, "enable Address sanitizer"),
7476
(thread.rawValue, "enable Thread sanitizer"),
75-
(undefined.rawValue, "enable Undefined Behavior sanitizer")
77+
(undefined.rawValue, "enable Undefined Behavior sanitizer"),
78+
(scudo.rawValue, "enable Scudo hardened allocator")
7679
])
7780
}

Tests/BuildTests/BuildPlanTests.swift

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2342,6 +2342,73 @@ final class BuildPlanTests: XCTestCase {
23422342
XCTAssertMatch(clibraryLinkArguments, [.anySequence, "-L", "/path/to/build/debug", .anySequence])
23432343
XCTAssertMatch(clibraryLinkArguments, ["-lStaticLibrary"])
23442344
}
2345+
2346+
func testAddressSanitizer() throws {
2347+
try sanitizerTest(.address, expectedName: "address")
2348+
}
2349+
2350+
func testThreadSanitizer() throws {
2351+
try sanitizerTest(.thread, expectedName: "thread")
2352+
}
2353+
2354+
func testUndefinedSanitizer() throws {
2355+
try sanitizerTest(.undefined, expectedName: "undefined")
2356+
}
2357+
2358+
func testScudoSanitizer() throws {
2359+
try sanitizerTest(.scudo, expectedName: "scudo")
2360+
}
2361+
2362+
private func sanitizerTest(_ sanitizer: Sanitizer, expectedName: String) throws {
2363+
let fs = InMemoryFileSystem(emptyFiles:
2364+
"/Pkg/Sources/exe/main.swift",
2365+
"/Pkg/Sources/lib/lib.swift",
2366+
"/Pkg/Sources/clib/clib.c",
2367+
"/Pkg/Sources/clib/include/clib.h"
2368+
)
2369+
2370+
let diagnostics = DiagnosticsEngine()
2371+
let graph = loadPackageGraph(fs: fs, diagnostics: diagnostics,
2372+
manifests: [
2373+
Manifest.createV4Manifest(
2374+
name: "Pkg",
2375+
path: "/Pkg",
2376+
url: "/Pkg",
2377+
packageKind: .root,
2378+
targets: [
2379+
TargetDescription(name: "exe", dependencies: ["lib", "clib"]),
2380+
TargetDescription(name: "lib", dependencies: []),
2381+
TargetDescription(name: "clib", dependencies: []),
2382+
]),
2383+
]
2384+
)
2385+
XCTAssertNoDiagnostics(diagnostics)
2386+
2387+
// Unrealistic: we can't enable all of these at once on all platforms.
2388+
// This test codifies current behaviour, not ideal behaviour, and
2389+
// may need to be amended if we change it.
2390+
var parameters = mockBuildParameters(shouldLinkStaticSwiftStdlib: true)
2391+
parameters.sanitizers = EnabledSanitizers([sanitizer])
2392+
2393+
let result = BuildPlanResult(plan: try BuildPlan(
2394+
buildParameters: parameters,
2395+
graph: graph, diagnostics: diagnostics, fileSystem: fs)
2396+
)
2397+
2398+
result.checkProductsCount(1)
2399+
result.checkTargetsCount(3)
2400+
2401+
let exe = try result.target(for: "exe").swiftTarget().compileArguments()
2402+
XCTAssertTrue(exe.contains("-sanitize=\(expectedName)"))
2403+
2404+
let lib = try result.target(for: "lib").swiftTarget().compileArguments()
2405+
XCTAssertTrue(lib.contains("-sanitize=\(expectedName)"))
2406+
2407+
let clib = try result.target(for: "clib").clangTarget().basicArguments()
2408+
XCTAssertTrue(clib.contains("-fsanitize=\(expectedName)"))
2409+
2410+
XCTAssertTrue(try result.buildProduct(for: "exe").linkArguments().contains("-sanitize=\(expectedName)"))
2411+
}
23452412
}
23462413

23472414
// MARK:- Test Helpers

Tests/CommandsTests/RunToolTests.swift

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -103,29 +103,4 @@ final class RunToolTests: XCTestCase {
103103
}
104104
}
105105
}
106-
107-
// Test that thread sanitizer works.
108-
func testSanitizeThread() throws {
109-
// FIXME: We need to figure out how to test this for linux.
110-
#if os(macOS)
111-
fixture(name: "Miscellaneous/ThreadRace") { path in
112-
// Ensure that we don't abort() when we find the race. This avoids
113-
// generating the crash report on macOS.
114-
let env = ["TSAN_OPTIONS": "abort_on_error=0"]
115-
let cmdline = {
116-
try SwiftPMProduct.SwiftRun.execute(
117-
["--sanitize=thread"], packagePath: path, env: env)
118-
}
119-
XCTAssertThrows(try cmdline()) { (error: SwiftPMProductError) in
120-
switch error {
121-
case .executionFailure(_, _, let error):
122-
XCTAssertMatch(error, .contains("ThreadSanitizer: data race"))
123-
return true
124-
default:
125-
return false
126-
}
127-
}
128-
}
129-
#endif
130-
}
131106
}

Tests/CommandsTests/TestToolTests.swift

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -30,32 +30,6 @@ final class TestToolTests: XCTestCase {
3030
XCTAssert(try execute(["--version"]).stdout.contains("Swift Package Manager"))
3131
}
3232

33-
// Test that thread sanitizer works.
34-
func testSanitizeThread() throws {
35-
// FIXME: We need to figure out how to test this for linux.
36-
// Disabled because of https://bugs.swift.org/browse/SR-7272
37-
#if false
38-
fixture(name: "Miscellaneous/ThreadRace") { path in
39-
// Ensure that we don't abort() when we find the race. This avoids
40-
// generating the crash report on macOS.
41-
let env = ["TSAN_OPTIONS": "abort_on_error=0"]
42-
let cmdline = {
43-
try SwiftPMProduct.SwiftTest.execute(
44-
["--sanitize=thread"], packagePath: path, env: env)
45-
}
46-
XCTAssertThrows(try cmdline()) { (error: SwiftPMProductError) in
47-
switch error {
48-
case .executionFailure(_, _, let error):
49-
XCTAssertMatch(error, .contains("ThreadSanitizer: data race"))
50-
return true
51-
default:
52-
return false
53-
}
54-
}
55-
}
56-
#endif
57-
}
58-
5933
func testNumWorkersParallelRequeriment() throws {
6034
// Running swift-test fixtures on linux is not yet possible.
6135
#if os(macOS)

0 commit comments

Comments
 (0)