Skip to content

Commit 64c5a33

Browse files
committed
Add support for the Scudo sanitizer.
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.
1 parent 528da51 commit 64c5a33

File tree

11 files changed

+114
-1
lines changed

11 files changed

+114
-1
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// swift-tools-version:5.1
2+
3+
import PackageDescription
4+
5+
let package = Package(
6+
name: "double-free",
7+
targets: [
8+
.target(
9+
name: "lib",
10+
dependencies: []),
11+
.target(
12+
name: "exec",
13+
dependencies: ["lib"]),
14+
.testTarget(
15+
name: "libTests",
16+
dependencies: ["lib"]),
17+
]
18+
)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# double-free
2+
3+
A description of this package.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import lib
2+
3+
executeDoubleFree()
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
public func executeDoubleFree() {
2+
let size = 512
3+
4+
let buffer = UnsafeMutableRawBufferPointer.allocate(byteCount: size, alignment: 1)
5+
buffer[0] = 0
6+
buffer.deallocate()
7+
buffer.deallocate()
8+
print(buffer[0])
9+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import XCTest
2+
3+
import libTests
4+
5+
var tests = [XCTestCaseEntry]()
6+
tests += libTests.__allTests()
7+
8+
XCTMain(tests)
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import XCTest
2+
3+
extension libTests {
4+
static let __allTests = [
5+
("testDoubleFree", testDoubleFree),
6+
]
7+
}
8+
9+
#if !os(macOS)
10+
public func __allTests() -> [XCTestCaseEntry] {
11+
return [
12+
testCase(libTests.__allTests),
13+
]
14+
}
15+
#endif
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import XCTest
2+
import lib
3+
4+
final class libTests: XCTestCase {
5+
func testDoubleFree() {
6+
executeDoubleFree()
7+
}
8+
}

Sources/Build/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/CommandsTests/RunToolTests.swift

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,4 +127,27 @@ final class RunToolTests: XCTestCase {
127127
}
128128
#endif
129129
}
130+
131+
func testSanitizeScudo() throws {
132+
// Scudo is only supported on Linux.
133+
#if os(Linux)
134+
fixture(name: "Miscellaneous/DoubleFree") { path in
135+
// Ensure that we don't abort() when we find the race. This avoids
136+
// generating the crash report on macOS.
137+
let cmdline = {
138+
try SwiftPMProduct.SwiftRun.execute(
139+
["--sanitize=scudo"], packagePath: path)
140+
}
141+
XCTAssertThrows(try cmdline()) { (error: SwiftPMProductError) in
142+
switch error {
143+
case .executionFailure(_, _, let error):
144+
XCTAssertMatch(error, .contains("invalid chunk state"))
145+
return true
146+
default:
147+
return false
148+
}
149+
}
150+
}
151+
#endif
152+
}
130153
}

Tests/CommandsTests/TestToolTests.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,4 +80,25 @@ final class TestToolTests: XCTestCase {
8080
}
8181
#endif
8282
}
83+
84+
func testSanitizeScudo() throws {
85+
// This test only runs on Linux because Scudo only runs on Linux
86+
#if os(Linux)
87+
fixture(name: "Miscellaneous/ThreadRace") { path in
88+
let cmdline = {
89+
try SwiftPMProduct.SwiftTest.execute(
90+
["--sanitize=scudo"], packagePath: path)
91+
}
92+
XCTAssertThrows(try cmdline()) { (error: SwiftPMProductError) in
93+
switch error {
94+
case .executionFailure(_, _, let error):
95+
XCTAssertMatch(error, .contains("invalid chunk state"))
96+
return true
97+
default:
98+
return false
99+
}
100+
}
101+
}
102+
#endif
103+
}
83104
}

Tests/CommandsTests/XCTestManifests.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ extension RunToolTests {
6161
("testFileDeprecation", testFileDeprecation),
6262
("testMultipleExecutableAndExplicitExecutable", testMultipleExecutableAndExplicitExecutable),
6363
("testMutualExclusiveFlags", testMutualExclusiveFlags),
64+
("testSanitizeScudo", testSanitizeScudo),
6465
("testSanitizeThread", testSanitizeThread),
6566
("testSeeAlso", testSeeAlso),
6667
("testUnkownProductAndArgumentPassing", testUnkownProductAndArgumentPassing),
@@ -77,6 +78,7 @@ extension TestToolTests {
7778
static let __allTests__TestToolTests = [
7879
("testNumWorkersParallelRequeriment", testNumWorkersParallelRequeriment),
7980
("testNumWorkersValue", testNumWorkersValue),
81+
("testSanitizeScudo", testSanitizeScudo),
8082
("testSanitizeThread", testSanitizeThread),
8183
("testSeeAlso", testSeeAlso),
8284
("testUsage", testUsage),

0 commit comments

Comments
 (0)