Skip to content

Commit 10ab127

Browse files
authored
Avoid using String while holding an internal libobjc lock. (#938)
`String` can inadvertently touch libobjc when we implicitly cast it to a C string (when calling `getsectiondata()`), which can result in a deadlock if that operation needs to acquire one of libobjc's internal locks. Switch to `StaticString` which we can guarantee never touches libobjc. Resolves rdar://144093524. ### Checklist: - [x] Code and documentation should follow the style of the [Style Guide](https://github.com/apple/swift-testing/blob/main/Documentation/StyleGuide.md). - [x] If public symbols are renamed or modified, DocC references should be updated.
1 parent 6df23a4 commit 10ab127

File tree

1 file changed

+23
-5
lines changed

1 file changed

+23
-5
lines changed

Sources/Testing/Discovery+Platform.swift

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,25 @@ struct SectionBounds: Sendable {
4747
#if SWT_TARGET_OS_APPLE
4848
// MARK: - Apple implementation
4949

50+
extension SectionBounds.Kind {
51+
/// The Mach-O segment and section name for this instance as a pair of
52+
/// null-terminated UTF-8 C strings and pass them to a function.
53+
///
54+
/// The values of this property within this function are instances of
55+
/// `StaticString` rather than `String` because the latter's inner storage is
56+
/// sometimes Objective-C-backed and touching it here can cause a recursive
57+
/// access to an internal libobjc lock, whereas `StaticString`'s internal
58+
/// storage is immediately available.
59+
fileprivate var segmentAndSectionName: (segmentName: StaticString, sectionName: StaticString) {
60+
switch self {
61+
case .testContent:
62+
("__DATA_CONST", "__swift5_tests")
63+
case .typeMetadata:
64+
("__TEXT", "__swift5_types")
65+
}
66+
}
67+
}
68+
5069
/// An array containing all of the test content section bounds known to the
5170
/// testing library.
5271
private let _sectionBounds = Locked<[SectionBounds.Kind: [SectionBounds]]>()
@@ -77,18 +96,17 @@ private let _startCollectingSectionBounds: Void = {
7796

7897
// If this image contains the Swift section(s) we need, acquire the lock and
7998
// store the section's bounds.
80-
func findSectionBounds(forSectionNamed segmentName: String, _ sectionName: String, ofKind kind: SectionBounds.Kind) {
99+
for kind in SectionBounds.Kind.allCases {
100+
let (segmentName, sectionName) = kind.segmentAndSectionName
81101
var size = CUnsignedLong(0)
82-
if let start = getsectiondata(mh, segmentName, sectionName, &size), size > 0 {
102+
if let start = getsectiondata(mh, segmentName.utf8Start, sectionName.utf8Start, &size), size > 0 {
83103
let buffer = UnsafeRawBufferPointer(start: start, count: Int(clamping: size))
84104
let sb = SectionBounds(imageAddress: mh, buffer: buffer)
85105
_sectionBounds.withLock { sectionBounds in
86106
sectionBounds[kind]!.append(sb)
87107
}
88108
}
89109
}
90-
findSectionBounds(forSectionNamed: "__DATA_CONST", "__swift5_tests", ofKind: .testContent)
91-
findSectionBounds(forSectionNamed: "__TEXT", "__swift5_types", ofKind: .typeMetadata)
92110
}
93111

94112
#if _runtime(_ObjC)
@@ -97,7 +115,7 @@ private let _startCollectingSectionBounds: Void = {
97115
}
98116
#else
99117
_dyld_register_func_for_add_image { mh, _ in
100-
addSectionBounds(from: mh)
118+
addSectionBounds(from: mh!)
101119
}
102120
#endif
103121
}()

0 commit comments

Comments
 (0)