Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions Sentry.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@
6304360B1EC0595B00C4D3FA /* SentryNSDataUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 630436091EC0595B00C4D3FA /* SentryNSDataUtils.m */; };
630436101EC0600A00C4D3FA /* SentrySerializable.h in Headers */ = {isa = PBXBuildFile; fileRef = 6304360F1EC0600A00C4D3FA /* SentrySerializable.h */; settings = {ATTRIBUTES = (Public, ); }; };
630436161EC0AD3100C4D3FA /* SentryNSDataCompressionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 630436151EC0AD3100C4D3FA /* SentryNSDataCompressionTests.m */; };
630C01941EC3402C00C52CEF /* SentryKSCrashReportConverterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 630C01931EC3402C00C52CEF /* SentryKSCrashReportConverterTests.m */; };
630C01941EC3402C00C52CEF /* SentryCrashReportConverterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 630C01931EC3402C00C52CEF /* SentryCrashReportConverterTests.m */; };
630C01961EC341D600C52CEF /* Resources in Resources */ = {isa = PBXBuildFile; fileRef = 630C01951EC341D600C52CEF /* Resources */; };
631501BB1EE6F30B00512C5B /* SentrySwizzleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 631501BA1EE6F30B00512C5B /* SentrySwizzleTests.m */; };
631E6D331EBC679C00712345 /* SentryQueueableRequestManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 631E6D311EBC679C00712345 /* SentryQueueableRequestManager.h */; };
Expand Down Expand Up @@ -1395,7 +1395,7 @@
6304360D1EC05CEF00C4D3FA /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk/usr/lib/libz.tbd; sourceTree = DEVELOPER_DIR; };
6304360F1EC0600A00C4D3FA /* SentrySerializable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SentrySerializable.h; path = Public/SentrySerializable.h; sourceTree = "<group>"; };
630436151EC0AD3100C4D3FA /* SentryNSDataCompressionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SentryNSDataCompressionTests.m; sourceTree = "<group>"; };
630C01931EC3402C00C52CEF /* SentryKSCrashReportConverterTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SentryKSCrashReportConverterTests.m; sourceTree = "<group>"; };
630C01931EC3402C00C52CEF /* SentryCrashReportConverterTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SentryCrashReportConverterTests.m; sourceTree = "<group>"; };
630C01951EC341D600C52CEF /* Resources */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Resources; sourceTree = "<group>"; };
631501BA1EE6F30B00512C5B /* SentrySwizzleTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SentrySwizzleTests.m; sourceTree = "<group>"; };
631E6D311EBC679C00712345 /* SentryQueueableRequestManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SentryQueueableRequestManager.h; path = include/SentryQueueableRequestManager.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2995,7 +2995,7 @@
8431D4572BE175A1009EAEC1 /* SentryContinuousProfiler+Test.h */,
639889D21EDF06C100EA7442 /* SentryTests-Bridging-Header.h */,
63B819131EC352A7002FDF4C /* SentryInterfacesTests.m */,
630C01931EC3402C00C52CEF /* SentryKSCrashReportConverterTests.m */,
630C01931EC3402C00C52CEF /* SentryCrashReportConverterTests.m */,
630436151EC0AD3100C4D3FA /* SentryNSDataCompressionTests.m */,
63EED6C22237989300E02400 /* SentryOptionsTest.m */,
7B569DFF2590EEF600B653FC /* SentryScope+Equality.m */,
Expand Down Expand Up @@ -6165,7 +6165,7 @@
7BBD18A2244EE2FD00427C76 /* TestResponseFactory.swift in Sources */,
628B89022D841D7F004B6F2A /* SentryDateUtilsTests.swift in Sources */,
D808FB8B281BCE96009A2A33 /* TestSentrySwizzleWrapper.swift in Sources */,
630C01941EC3402C00C52CEF /* SentryKSCrashReportConverterTests.m in Sources */,
630C01941EC3402C00C52CEF /* SentryCrashReportConverterTests.m in Sources */,
7B59398424AB481B0003AAD2 /* NotificationCenterTestCase.swift in Sources */,
7B0A542E2521C62400A71716 /* SentryFrameRemoverTests.swift in Sources */,
7BE912B12721C76000E49E62 /* SentryPerformanceTrackingIntegrationTests.swift in Sources */,
Expand Down
10 changes: 6 additions & 4 deletions Sources/Sentry/Public/SentryThread.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ NS_ASSUME_NONNULL_BEGIN
SENTRY_NO_INIT

/**
* Number of the thread
* Number of the thread.
*
* Can be nil for threads in recrash reports where the thread index information is not available.
*/
@property (nonatomic, copy) NSNumber *threadId;
@property (nullable, nonatomic, copy) NSNumber *threadId;

/**
* Name (if available) of the thread
Expand Down Expand Up @@ -52,10 +54,10 @@ SENTRY_NO_INIT

/**
* Initializes a SentryThread with its id
* @param threadId NSNumber
* @param threadId NSNumber or nil if thread index is not available (e.g., recrash reports)
* @return SentryThread
*/
- (instancetype)initWithThreadId:(NSNumber *)threadId;
- (instancetype)initWithThreadId:(nullable NSNumber *)threadId;

@end

Expand Down
4 changes: 3 additions & 1 deletion Sources/Sentry/SentryANRTrackingIntegration.m
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#import "SentryException.h"
#import "SentryFileManager.h"
#import "SentryHub+Private.h"
#import "SentryInternalDefines.h"
#import "SentryLogC.h"
#import "SentryMechanism.h"
#import "SentrySDK+Private.h"
Expand Down Expand Up @@ -156,7 +157,8 @@ - (void)anrDetectedWithType:(enum SentryANRType)type
// recover the debug images. The client would also attach the debug images when directly
// capturing the app hang event. Still, we attach them already now to ensure all app hang events
// have debug images cause it's easy to mess this up in the future.
event.debugMeta = [self.debugImageProvider getDebugImagesFromCacheForThreads:event.threads];
event.debugMeta = [self.debugImageProvider
getDebugImagesFromCacheForThreads:SENTRY_UNWRAP_NULLABLE(NSArray, event.threads)];

#if SENTRY_HAS_UIKIT
# if SDK_V9
Expand Down
5 changes: 3 additions & 2 deletions Sources/Sentry/SentryClient.m
Original file line number Diff line number Diff line change
Expand Up @@ -767,8 +767,9 @@ - (SentryEvent *_Nullable)prepareEvent:(SentryEvent *)event
BOOL debugMetaNotAttached = !(nil != event.debugMeta && event.debugMeta.count > 0);
if (!isFatalEvent && shouldAttachStacktrace && debugMetaNotAttached
&& event.threads != nil) {
event.debugMeta =
[self.debugImageProvider getDebugImagesFromCacheForThreads:event.threads];
event.debugMeta = [self.debugImageProvider
getDebugImagesFromCacheForThreads:SENTRY_UNWRAP_NULLABLE(
NSArray<SentryThread *>, event.threads)];
}
}

Expand Down
83 changes: 58 additions & 25 deletions Sources/Sentry/SentryCrashReportConverter.m
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ - (instancetype)initWithReport:(NSDictionary *)report inAppLogic:(SentryInAppLog
// here. For more details please check out SentryCrashScopeObserver.
NSMutableDictionary *userContextMerged =
[[NSMutableDictionary alloc] initWithDictionary:userContextUnMerged];
[userContextMerged addEntriesFromDictionary:report[@"sentry_sdk_scope"]];
[userContextMerged addEntriesFromDictionary:report[@"sentry_sdk_scope"] ?: @{}];
[userContextMerged removeObjectForKey:@"sentry_sdk_scope"];
self.userContext = userContextMerged;

Expand Down Expand Up @@ -108,8 +108,9 @@ - (SentryEvent *_Nullable)convertReportToEvent
if ([self.report[@"report"][@"timestamp"] isKindOfClass:NSNumber.class]) {
event.timestamp = [NSDate
dateWithTimeIntervalSince1970:[self.report[@"report"][@"timestamp"] integerValue]];
} else {
event.timestamp = sentry_fromIso8601String(self.report[@"report"][@"timestamp"]);
} else if ([self.report[@"report"][@"timestamp"] isKindOfClass:NSString.class]) {
event.timestamp = sentry_fromIso8601String(
SENTRY_UNWRAP_NULLABLE(NSString, self.report[@"report"][@"timestamp"]));
}
event.threads = [self convertThreads];
event.debugMeta = [self debugMetaForThreads:event.threads];
Expand All @@ -119,7 +120,7 @@ - (SentryEvent *_Nullable)convertReportToEvent
event.environment = self.userContext[@"environment"];

NSMutableDictionary *mutableContext =
[[NSMutableDictionary alloc] initWithDictionary:self.userContext[@"context"]];
[[NSMutableDictionary alloc] initWithDictionary:self.userContext[@"context"] ?: @{}];
if (self.userContext[@"traceContext"]) {
mutableContext[@"trace"] = self.userContext[@"traceContext"];
}
Expand Down Expand Up @@ -186,13 +187,19 @@ - (SentryUser *_Nullable)convertUser
if (nil != self.userContext[@"breadcrumbs"]) {
NSArray *storedBreadcrumbs = self.userContext[@"breadcrumbs"];
for (NSDictionary *storedCrumb in storedBreadcrumbs) {
if (!storedCrumb[@"category"]) {
continue;
}
SentryBreadcrumb *crumb = [[SentryBreadcrumb alloc]
initWithLevel:[self sentryLevelFromString:storedCrumb[@"level"]]
category:storedCrumb[@"category"]];
category:SENTRY_UNWRAP_NULLABLE(NSString, storedCrumb[@"category"])];
crumb.message = storedCrumb[@"message"];
crumb.type = storedCrumb[@"type"];
crumb.origin = storedCrumb[@"origin"];
crumb.timestamp = sentry_fromIso8601String(storedCrumb[@"timestamp"]);
if ([storedCrumb[@"timestamp"] isKindOfClass:NSString.class]) {
crumb.timestamp = sentry_fromIso8601String(
SENTRY_UNWRAP_NULLABLE(NSString, storedCrumb[@"timestamp"]));
}
crumb.data = storedCrumb[@"data"];
[breadcrumbs addObject:crumb];
}
Expand Down Expand Up @@ -248,14 +255,31 @@ - (NSDictionary *)binaryImageForAddress:(uintptr_t)address
return result;
}

/**
* Creates a SentryThread from crash report thread data at the specified index.
*
* This method includes defensive null handling to prevent crashes when processing
* malformed crash reports. The original bug was that invalid thread index types
* could cause crashes when accessing threadId.intValue for isMain calculation.
*/
- (SentryThread *_Nullable)threadAtIndex:(NSInteger)threadIndex
{
if (threadIndex >= [self.threads count]) {
return nil;
}
NSDictionary *threadDictionary = self.threads[threadIndex];

SentryThread *thread = [[SentryThread alloc] initWithThreadId:threadDictionary[@"index"]];
// Thread index validation: We must support nil/missing indexes for backward compatibility
// with recrash reports (see testRecrashReport_WithThreadIsStringInsteadOfDict), but we
// must reject invalid types when present to prevent crashes from calling .intValue on
// non-NSNumber objects. This fixes a bug where malformed crash reports could cause
// crashes in the converter itself when accessing threadId.intValue for isMain calculation.
id threadIndexObj = threadDictionary[@"index"];
if (threadIndexObj != nil && ![threadIndexObj isKindOfClass:[NSNumber class]]) {
SENTRY_LOG_ERROR(@"Thread index is not a number: %@", threadIndexObj);
return nil;
}
SentryThread *thread = [[SentryThread alloc] initWithThreadId:threadIndexObj];
// We only want to add the stacktrace if this thread hasn't crashed
thread.stacktrace = [self stackTraceForThreadIndex:threadIndex];
if (thread.stacktrace.frames.count == 0) {
Expand All @@ -266,7 +290,10 @@ - (SentryThread *_Nullable)threadAtIndex:(NSInteger)threadIndex
thread.current = threadDictionary[@"current_thread"];
thread.name = threadDictionary[@"name"];
// We don't have access to the MachineContextWrapper but we know first thread is always the main
thread.isMain = [NSNumber numberWithBool:thread.threadId.intValue == 0];
// Use null-safe check: threadIndexObj can be nil for recrash reports, and calling intValue on
// a nil NSNumber would return 0, incorrectly marking threads without indexes as main threads.
thread.isMain =
[NSNumber numberWithBool:threadIndexObj != nil && [threadIndexObj intValue] == 0];
if (nil == thread.name) {
thread.name = threadDictionary[@"dispatch_queue"];
}
Expand Down Expand Up @@ -357,9 +384,11 @@ - (SentryDebugMeta *)debugMetaFromBinaryImageDictionary:(NSDictionary *)sourceIm

for (SentryThread *thread in threads) {
for (SentryFrame *frame in thread.stacktrace.frames) {
if (frame.imageAddress && ![imageNames containsObject:frame.imageAddress]) {
[imageNames addObject:frame.imageAddress];
NSString *_Nullable nullableImageAddress = frame.imageAddress;
if (nullableImageAddress == nil) {
continue;
}
[imageNames addObject:SENTRY_UNWRAP_NULLABLE(NSString, nullableImageAddress)];
}
}

Expand Down Expand Up @@ -399,19 +428,20 @@ - (SentryDebugMeta *)debugMetaFromBinaryImageDictionary:(NSDictionary *)sourceIm
self.exceptionContext[@"mach"][@"exception"],
self.exceptionContext[@"mach"][@"code"],
self.exceptionContext[@"mach"][@"subcode"]]
type:self.exceptionContext[@"mach"][@"exception_name"]];
type:self.exceptionContext[@"mach"][@"exception_name"] ?: @"Mach Exception"];
} else if ([exceptionType isEqualToString:@"signal"]) {
exception =
[[SentryException alloc] initWithValue:[NSString stringWithFormat:@"Signal %@, Code %@",
self.exceptionContext[@"signal"][@"signal"],
self.exceptionContext[@"signal"][@"code"]]
type:self.exceptionContext[@"signal"][@"name"]];
exception = [[SentryException alloc]
initWithValue:[NSString stringWithFormat:@"Signal %@, Code %@",
self.exceptionContext[@"signal"][@"signal"],
self.exceptionContext[@"signal"][@"code"]]
type:self.exceptionContext[@"signal"][@"name"] ?: @"Signal Exception"];
} else if ([exceptionType isEqualToString:@"user"]) {
NSString *exceptionReason =
[NSString stringWithFormat:@"%@", self.exceptionContext[@"reason"]];
exception = [[SentryException alloc]
initWithValue:exceptionReason
type:self.exceptionContext[@"user_reported"][@"name"]];
exception =
[[SentryException alloc] initWithValue:exceptionReason
type:self.exceptionContext[@"user_reported"][@"name"]
?: @"User Reported Exception"];

NSRange match = [exceptionReason rangeOfString:@":"];
if (match.location != NSNotFound) {
Expand Down Expand Up @@ -453,8 +483,9 @@ - (SentryException *)parseNSException
reason = self.exceptionContext[@"reason"];
}

return [[SentryException alloc] initWithValue:[NSString stringWithFormat:@"%@", reason]
type:self.exceptionContext[@"nsexception"][@"name"]];
return [[SentryException alloc]
initWithValue:[NSString stringWithFormat:@"%@", reason]
type:self.exceptionContext[@"nsexception"][@"name"] ?: @"NSException"];
}

- (void)enhanceValueFromNotableAddresses:(SentryException *)exception
Expand All @@ -464,15 +495,15 @@ - (void)enhanceValueFromNotableAddresses:(SentryException *)exception
return;
}
NSDictionary *crashedThread = self.threads[self.crashedThreadIndex];
NSDictionary *notableAddresses = crashedThread[@"notable_addresses"];
NSDictionary *_Nullable notableAddresses = crashedThread[@"notable_addresses"];
NSMutableOrderedSet *reasons = [[NSMutableOrderedSet alloc] init];
if (nil != notableAddresses) {
for (id key in notableAddresses) {
NSDictionary *content = notableAddresses[key];
if ([content[@"type"] isEqualToString:@"string"] && nil != content[@"value"]) {
// if there are less than 3 slashes it shouldn't be a filepath
if ([[content[@"value"] componentsSeparatedByString:@"/"] count] < 3) {
[reasons addObject:content[@"value"]];
[reasons addObject:SENTRY_UNWRAP_NULLABLE(NSString, content[@"value"])];
}
}
}
Expand All @@ -497,11 +528,13 @@ - (void)enhanceValueFromCrashInfoMessage:(SentryException *)exception

for (NSDictionary *binaryImage in libSwiftCoreBinaryImages) {
if (binaryImage[@"crash_info_message"] != nil) {
[crashInfoMessages addObject:binaryImage[@"crash_info_message"]];
[crashInfoMessages
addObject:SENTRY_UNWRAP_NULLABLE(NSString, binaryImage[@"crash_info_message"])];
}

if (binaryImage[@"crash_info_message2"] != nil) {
[crashInfoMessages addObject:binaryImage[@"crash_info_message2"]];
[crashInfoMessages
addObject:SENTRY_UNWRAP_NULLABLE(NSString, binaryImage[@"crash_info_message2"])];
}
}

Expand Down
2 changes: 1 addition & 1 deletion Sources/Sentry/SentryThread.mm
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

@implementation SentryThread

- (instancetype)initWithThreadId:(NSNumber *)threadId
- (instancetype)initWithThreadId:(nullable NSNumber *)threadId
{
self = [super init];
if (self) {
Expand Down
Loading
Loading