Skip to content

Commit 109f689

Browse files
fix: Truncation of Swift crash messages (#5036)
With GH-1596, we introduced caching binary images to avoid calling async unsafe methods when writing crash reports. This PR removed getting the crash info from the binary images when crashing. This is fixed now by calling getCrashInfo while writing the binary images for a crash report. Fixes GH-3905 Co-authored-by: Philip Niedertscheider <phil.niedertscheider@sentry.io>
1 parent 7eded42 commit 109f689

File tree

9 files changed

+159
-43
lines changed

9 files changed

+159
-43
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
- Crash in setMeasurement when name is nil (#5064)
88
- Make setMeasurement thread safe (#5067, #5078)
9+
- Truncation of Swift crash messages (#5036)
910

1011
## 8.49.0
1112

Samples/iOS-Swift/iOS-Swift/Base.lproj/Main.storyboard

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="23504" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="5CD-RQ-aBU">
2+
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="23727" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="5CD-RQ-aBU">
33
<device id="retina4_0" orientation="portrait" appearance="light"/>
44
<dependencies>
55
<deployment identifier="iOS"/>
6-
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23506"/>
6+
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23721"/>
77
<capability name="Image references" minToolsVersion="12.0"/>
88
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
99
<capability name="System colors in document resources" minToolsVersion="11.0"/>
@@ -751,13 +751,13 @@
751751
</constraints>
752752
</view>
753753
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="40A-vG-dFA">
754-
<rect key="frame" x="0.0" y="134" width="320" height="246.5"/>
754+
<rect key="frame" x="0.0" y="134" width="320" height="268"/>
755755
<subviews>
756756
<stackView opaque="NO" contentMode="scaleToFill" distribution="fillEqually" translatesAutoresizingMaskIntoConstraints="NO" id="fn9-mQ-2PY">
757-
<rect key="frame" x="0.0" y="0.0" width="320" height="118.5"/>
757+
<rect key="frame" x="0.0" y="0.0" width="320" height="140"/>
758758
<subviews>
759759
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="equalSpacing" translatesAutoresizingMaskIntoConstraints="NO" id="Q2k-6Y-RnD">
760-
<rect key="frame" x="0.0" y="0.0" width="160" height="118.5"/>
760+
<rect key="frame" x="0.0" y="0.0" width="160" height="140"/>
761761
<subviews>
762762
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="NFC-V3-lgW">
763763
<rect key="frame" x="0.0" y="0.0" width="160" height="28"/>
@@ -768,23 +768,31 @@
768768
</connections>
769769
</button>
770770
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Llv-Yz-cwF">
771-
<rect key="frame" x="0.0" y="30" width="160" height="28"/>
771+
<rect key="frame" x="0.0" y="28" width="160" height="28"/>
772772
<fontDescription key="fontDescription" type="system" pointSize="13"/>
773773
<state key="normal" title="Capture NSException"/>
774774
<connections>
775775
<action selector="captureNSException:" destination="QmU-DD-itF" eventType="touchUpInside" id="Gdl-wk-dUU"/>
776776
</connections>
777777
</button>
778778
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="wJ4-gS-64G" userLabel="fatalError">
779-
<rect key="frame" x="0.0" y="60.5" width="160" height="28"/>
779+
<rect key="frame" x="0.0" y="56" width="160" height="28"/>
780780
<fontDescription key="fontDescription" type="system" pointSize="13"/>
781781
<state key="normal" title="Throw FatalError"/>
782782
<connections>
783783
<action selector="captureFatalError:" destination="QmU-DD-itF" eventType="touchUpInside" id="dM5-xq-lrm"/>
784784
</connections>
785785
</button>
786+
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="ulu-26-dyP" userLabel="fatalDuplicateKeyError">
787+
<rect key="frame" x="0.0" y="84" width="160" height="28"/>
788+
<fontDescription key="fontDescription" type="system" pointSize="13"/>
789+
<state key="normal" title="Fatal Duplicate Key Error"/>
790+
<connections>
791+
<action selector="throwFatalDuplicateKeyError:" destination="QmU-DD-itF" eventType="touchUpInside" id="kze-Yw-k8f"/>
792+
</connections>
793+
</button>
786794
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="NYx-6R-0bb">
787-
<rect key="frame" x="0.0" y="90.5" width="160" height="28"/>
795+
<rect key="frame" x="0.0" y="112" width="160" height="28"/>
788796
<fontDescription key="fontDescription" type="system" pointSize="13"/>
789797
<state key="normal" title="OOM crash"/>
790798
<connections>
@@ -794,7 +802,7 @@
794802
</subviews>
795803
</stackView>
796804
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="equalSpacing" translatesAutoresizingMaskIntoConstraints="NO" id="2CV-VI-MDY">
797-
<rect key="frame" x="160" y="0.0" width="160" height="118.5"/>
805+
<rect key="frame" x="160" y="0.0" width="160" height="140"/>
798806
<subviews>
799807
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Zbo-2T-1Zm">
800808
<rect key="frame" x="0.0" y="0.0" width="160" height="28"/>
@@ -806,15 +814,15 @@
806814
</connections>
807815
</button>
808816
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="s6w-E3-5yE" userLabel="DiskWriteException">
809-
<rect key="frame" x="0.0" y="28" width="160" height="28"/>
817+
<rect key="frame" x="0.0" y="35" width="160" height="28"/>
810818
<fontDescription key="fontDescription" type="system" pointSize="13"/>
811819
<state key="normal" title="DiskWriteException"/>
812820
<connections>
813821
<action selector="diskWriteException:" destination="QmU-DD-itF" eventType="touchUpInside" id="p74-qC-kH7"/>
814822
</connections>
815823
</button>
816824
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="NNd-Ec-zXw">
817-
<rect key="frame" x="0.0" y="56" width="160" height="28"/>
825+
<rect key="frame" x="0.0" y="70.5" width="160" height="28"/>
818826
<accessibility key="accessibilityConfiguration" label="crashTheApp"/>
819827
<fontDescription key="fontDescription" type="system" pointSize="13"/>
820828
<state key="normal" title="Crash the app"/>
@@ -823,7 +831,7 @@
823831
</connections>
824832
</button>
825833
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="wJs-Av-cg2">
826-
<rect key="frame" x="0.0" y="84" width="160" height="34.5"/>
834+
<rect key="frame" x="0.0" y="105.5" width="160" height="34.5"/>
827835
<state key="normal" title="Button"/>
828836
<buttonConfiguration key="configuration" style="plain" title="Use-after-free"/>
829837
<connections>
@@ -835,7 +843,7 @@
835843
</subviews>
836844
</stackView>
837845
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="1zm-ho-TCr">
838-
<rect key="frame" x="0.0" y="118.5" width="320" height="128"/>
846+
<rect key="frame" x="0.0" y="140" width="320" height="128"/>
839847
<constraints>
840848
<constraint firstAttribute="height" constant="128" id="lc6-97-p3W"/>
841849
</constraints>
@@ -1574,13 +1582,13 @@
15741582
<color white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
15751583
</systemColor>
15761584
<systemColor name="secondarySystemBackgroundColor">
1577-
<color red="0.94901960784313721" green="0.94901960784313721" blue="0.96862745098039216" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
1585+
<color red="0.94901960780000005" green="0.94901960780000005" blue="0.96862745100000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
15781586
</systemColor>
15791587
<systemColor name="systemBackgroundColor">
15801588
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
15811589
</systemColor>
15821590
<systemColor name="systemGray5Color">
1583-
<color red="0.89803921568627454" green="0.89803921568627454" blue="0.91764705882352937" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
1591+
<color red="0.8980392157" green="0.8980392157" blue="0.91764705879999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
15841592
</systemColor>
15851593
</resources>
15861594
</document>

Samples/iOS-Swift/iOS-Swift/ErrorsViewController.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,16 @@ class ErrorsViewController: UIViewController {
8484
fatalError("This is a fatal error. Oh no 😬.")
8585
}
8686

87+
@IBAction func throwFatalDuplicateKeyError(_ sender: Any) {
88+
// Triggers: Fatal error: Duplicate keys of type 'Something' were found in a Dictionary.
89+
var dict = [HashableViolation(): "value"]
90+
91+
// Add plenty of items to the dictionary so it uses both == and hash methods, which will cause the crash.
92+
for i in 0..<1_000_000 {
93+
dict[HashableViolation()] = "value \(i)"
94+
}
95+
}
96+
8797
@IBAction func oomCrash(_ sender: UIButton) {
8898
highlightButton(sender)
8999
DispatchQueue.main.async {
@@ -101,3 +111,20 @@ class ErrorsViewController: UIViewController {
101111
}
102112
}
103113
}
114+
115+
/// When using this class with a dictionary in Swift, it will cause a crash due to the violation of the Hashable contract.
116+
/// The Swift dict sees multiple keys that are equal but have different hashes, which it can’t resolve safely. When this
117+
/// happens, the Swift runtime will crash with the error: "Fatal error: Duplicate keys of type 'HashableViolation' were
118+
/// found in a Dictionary."
119+
class HashableViolation: Hashable {
120+
121+
// always return true, which means every instance of Something is considered equal.
122+
static func == (lhs: HashableViolation, rhs: HashableViolation) -> Bool {
123+
return true
124+
}
125+
126+
// Always return a different hash value for each instance so we're violating the Hashable contract.
127+
func hash(into hasher: inout Hasher) {
128+
hasher.combine(ObjectIdentifier(self))
129+
}
130+
}

Samples/macOS-SwiftUI/macOS-SwiftUI/ContentView.swift

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,19 @@ struct ContentView: View {
1515
Button(action: reportNSException) {
1616
Text("Report NSException")
1717
}
18-
18+
19+
Button(action: {
20+
// Triggers: Fatal error: Duplicate keys of type 'Something' were found in a Dictionary.
21+
var dict = [HashableViolation(): "value"]
22+
23+
// Add plenty of items to the dictionary so it uses both == and hash methods, which will cause the crash.
24+
for i in 0..<1_000_000 {
25+
dict[HashableViolation()] = "value \(i)"
26+
}
27+
}) {
28+
Text("Fatal Duplicate Key Error")
29+
}
30+
1931
Button(action: crash) {
2032
Text("Crash")
2133
}
@@ -45,6 +57,23 @@ struct ContentView: View {
4557
}
4658
}
4759

60+
/// When using this class with a dictionary in Swift, it will cause a crash due to the violation of the Hashable contract.
61+
/// The Swift dict sees multiple keys that are equal but have different hashes, which it can’t resolve safely. When this
62+
/// happens, the Swift runtime will crash with the error: "Fatal error: Duplicate keys of type 'HashableViolation' were
63+
/// found in a Dictionary."
64+
class HashableViolation: Hashable {
65+
66+
// always return true, which means every instance of Something is considered equal.
67+
static func == (lhs: HashableViolation, rhs: HashableViolation) -> Bool {
68+
return true
69+
}
70+
71+
// Always return a different hash value for each instance so we're violating the Hashable contract.
72+
func hash(into hasher: inout Hasher) {
73+
hasher.combine(ObjectIdentifier(self))
74+
}
75+
}
76+
4877
#Preview {
4978
ContentView()
5079
}

Sources/Sentry/SentryCrashReportConverter.m

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -477,32 +477,6 @@ - (void)enhanceValueFromNotableAddresses:(SentryException *)exception
477477
}
478478
}
479479

480-
/**
481-
* Get the message of fatalError, assert, and precondition to set it as the exception value if the
482-
* crashInfo contains the message.
483-
*
484-
* Swift puts the messages of fatalError, assert, and precondition into the @c crashInfo of the
485-
* @c libswiftCore.dylib. We found somewhat proof that the swift runtime uses @c __crash_info:
486-
* fatalError (1) calls @c swift_reportError (2) calls @c reportOnCrash (3) which uses (4) the
487-
* @c __crash_info (5). The documentation of Apple and Swift doesn't mention anything about where
488-
* the @c __crash_info ends up. Trying fatalError, assert, and precondition on iPhone, iPhone
489-
* simulator, and macOS all showed that the message ends up in the crashInfo of the
490-
* @c libswiftCore.dylib. For example, on the simulator, other binary images also contain a
491-
* @c crash_info_message with information about the stacktrace. We only care about the message of
492-
* fatalError, assert, or precondition, and we already get the stacktrace from the threads,
493-
* retrieving it from @c libswiftCore.dylib seems to be the most reliable option.
494-
*
495-
* @seealso
496-
* https://github.com/apple/swift/blob/d1bb98b11ede375a1cee739f964b7d23b6657aaf/stdlib/public/runtime/Errors.cpp#L365-L377
497-
* @seealso
498-
* https://github.com/apple/swift/blob/d1bb98b11ede375a1cee739f964b7d23b6657aaf/stdlib/public/runtime/Errors.cpp#L361
499-
* @seealso
500-
* https://github.com/apple/swift/blob/d1bb98b11ede375a1cee739f964b7d23b6657aaf/stdlib/public/runtime/Errors.cpp#L269-L293
501-
* @seealso
502-
* https://github.com/apple/swift/blob/d1bb98b11ede375a1cee739f964b7d23b6657aaf/stdlib/public/runtime/Errors.cpp#L264-L293
503-
* @seealso
504-
* https://github.com/apple/swift/blob/d1bb98b11ede375a1cee739f964b7d23b6657aaf/include/swift/Runtime/Debug.h#L29-L58
505-
*/
506480
- (void)enhanceValueFromCrashInfoMessage:(SentryException *)exception
507481
{
508482
NSMutableArray<NSString *> *crashInfoMessages = [NSMutableArray new];

Sources/SentryCrash/Recording/SentryCrashReport.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1185,6 +1185,10 @@ static void
11851185
binaryImagesIteratorCallback(SentryCrashBinaryImage *image, void *context)
11861186
{
11871187
SentryCrashReportWriter *writer = (SentryCrashReportWriter *)context;
1188+
// We can only retrieve the crash info after a crash occurred. So we need to
1189+
// fetch it when writing the crash report.
1190+
// Swift puts its fatalErrors into the crash info message.
1191+
sentrycrashdl_getCrashInfo(image->address, image);
11881192
writeBinaryImage(writer, NULL, image);
11891193
}
11901194

0 commit comments

Comments
 (0)