Skip to content

Commit 3d8532d

Browse files
fix: edge case crashes with profiled traces (#3365)
Co-authored-by: Philipp Hofmann <[email protected]>
1 parent 76fc36e commit 3d8532d

File tree

8 files changed

+37
-19
lines changed

8 files changed

+37
-19
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
- Stop sending empty thread names (#3361)
1313
- Work around edge case with a thread info kernel call sometimes returning invalid data, leading to a crash (#3364)
1414
- Thread sanitizer data race warnings in ANR tracker, network tracker and span finish (#3303)
15+
- Crashes when trace ID is externally modified or profiler fails to initialize (#3365)
1516

1617
## 8.14.2
1718

Sources/Sentry/Profiling/SentryProfiledTracerConcurrency.mm

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
/* number of in-flight tracers */ NSNumber *> *_gProfilersToTracers;
2626

2727
/** provided for fast access to a profiler given a tracer */
28-
static NSMutableDictionary</* SentryTracer.tracerId */ NSString *, SentryProfiler *>
28+
static NSMutableDictionary</* SentryTracer.internalTraceId */ NSString *, SentryProfiler *>
2929
*_gTracersToProfilers;
3030

3131
namespace {
@@ -54,12 +54,12 @@
5454
std::mutex _gStateLock;
5555

5656
void
57-
trackProfilerForTracer(SentryProfiler *profiler, SentryId *traceId)
57+
trackProfilerForTracer(SentryProfiler *profiler, SentryId *internalTraceId)
5858
{
5959
std::lock_guard<std::mutex> l(_gStateLock);
6060

6161
const auto profilerKey = profiler.profilerId.sentryIdString;
62-
const auto tracerKey = traceId.sentryIdString;
62+
const auto tracerKey = internalTraceId.sentryIdString;
6363

6464
SENTRY_LOG_DEBUG(
6565
@"Tracking relationship between profiler id %@ and tracer id %@", profilerKey, tracerKey);
@@ -73,7 +73,7 @@
7373
/* number of in-flight tracers */ NSNumber *>
7474
dictionary];
7575
_gTracersToProfilers =
76-
[NSMutableDictionary</* SentryTracer.tracerId */ NSString *, SentryProfiler *>
76+
[NSMutableDictionary</* SentryTracer.internalTraceId */ NSString *, SentryProfiler *>
7777
dictionary];
7878
}
7979

@@ -82,14 +82,14 @@
8282
}
8383

8484
void
85-
discardProfilerForTracer(SentryId *traceId)
85+
discardProfilerForTracer(SentryId *internalTraceId)
8686
{
8787
std::lock_guard<std::mutex> l(_gStateLock);
8888

8989
SENTRY_CASSERT(_gTracersToProfilers != nil && _gProfilersToTracers != nil,
9090
@"Structures should have already been initialized by the time they are being queried");
9191

92-
const auto tracerKey = traceId.sentryIdString;
92+
const auto tracerKey = internalTraceId.sentryIdString;
9393
const auto profiler = _gTracersToProfilers[tracerKey];
9494

9595
if (profiler == nil) {
@@ -105,14 +105,14 @@
105105
# endif // SENTRY_HAS_UIKIT
106106
}
107107

108-
SentryProfiler *_Nullable profilerForFinishedTracer(SentryId *traceId)
108+
SentryProfiler *_Nullable profilerForFinishedTracer(SentryId *internalTraceId)
109109
{
110110
std::lock_guard<std::mutex> l(_gStateLock);
111111

112112
SENTRY_CASSERT(_gTracersToProfilers != nil && _gProfilersToTracers != nil,
113113
@"Structures should have already been initialized by the time they are being queried");
114114

115-
const auto tracerKey = traceId.sentryIdString;
115+
const auto tracerKey = internalTraceId.sentryIdString;
116116
const auto profiler = _gTracersToProfilers[tracerKey];
117117

118118
if (!SENTRY_CASSERT_RETURN(profiler != nil,

Sources/Sentry/SentryProfiler.mm

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,7 @@ - (void)scheduleTimeoutTimer
336336

337337
# pragma mark - Public
338338

339-
+ (void)startWithTracer:(SentryId *)traceId
339+
+ (BOOL)startWithTracer:(SentryId *)traceId
340340
{
341341
std::lock_guard<std::mutex> l(_gProfilerLock);
342342

@@ -345,16 +345,17 @@ + (void)startWithTracer:(SentryId *)traceId
345345
trackProfilerForTracer(_gCurrentProfiler, traceId);
346346
// record a new metric sample for every concurrent span start
347347
[_gCurrentProfiler->_metricProfiler recordMetrics];
348-
return;
348+
return YES;
349349
}
350350

351351
_gCurrentProfiler = [[SentryProfiler alloc] init];
352352
if (_gCurrentProfiler == nil) {
353353
SENTRY_LOG_WARN(@"Profiler was not initialized, will not proceed.");
354-
return;
354+
return NO;
355355
}
356356

357357
trackProfilerForTracer(_gCurrentProfiler, traceId);
358+
return YES;
358359
}
359360

360361
+ (BOOL)isCurrentlyProfiling
@@ -377,7 +378,7 @@ + (nullable SentryEnvelopeItem *)createProfilingEnvelopeItemForTransaction:
377378
{
378379
const auto payload = [self collectProfileBetween:transaction.startSystemTime
379380
and:transaction.endSystemTime
380-
forTrace:transaction.trace.traceId
381+
forTrace:transaction.trace.internalID
381382
onHub:transaction.trace.hub];
382383
if (payload == nil) {
383384
return nil;

Sources/Sentry/SentryTracer.m

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -163,9 +163,9 @@ - (instancetype)initWithTransactionContext:(SentryTransactionContext *)transacti
163163

164164
#if SENTRY_TARGET_PROFILING_SUPPORTED
165165
if (_configuration.profilesSamplerDecision.decision == kSentrySampleDecisionYes) {
166-
_isProfiling = YES;
167166
_startSystemTime = SentryDependencyContainer.sharedInstance.dateProvider.systemTime;
168-
[SentryProfiler startWithTracer:self.traceId];
167+
_internalID = [[SentryId alloc] init];
168+
_isProfiling = [SentryProfiler startWithTracer:_internalID];
169169
}
170170
#endif // SENTRY_TARGET_PROFILING_SUPPORTED
171171

@@ -176,7 +176,7 @@ - (void)dealloc
176176
{
177177
#if SENTRY_TARGET_PROFILING_SUPPORTED
178178
if (self.isProfiling) {
179-
discardProfilerForTracer(self.traceId);
179+
discardProfilerForTracer(self.internalID);
180180
}
181181
#endif // SENTRY_TARGET_PROFILING_SUPPORTED
182182
}

Sources/Sentry/include/SentryProfiledTracerConcurrency.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,21 @@ SENTRY_EXTERN_C_BEGIN
1515
* Associate the provided profiler and tracer so that profiling data may be retrieved by the tracer
1616
* when it is ready to transmit its envelope.
1717
*/
18-
void trackProfilerForTracer(SentryProfiler *profiler, SentryId *traceId);
18+
void trackProfilerForTracer(SentryProfiler *profiler, SentryId *internalTraceId);
1919

2020
/**
2121
* For transactions that will be discarded, clean up the bookkeeping state associated with them to
2222
* reclaim the memory they're using.
2323
*/
24-
void discardProfilerForTracer(SentryId *traceId);
24+
void discardProfilerForTracer(SentryId *internalTraceId);
2525

2626
/**
2727
* Return the profiler instance associated with the tracer. If it was the last tracer for the
2828
* associated profiler, stop that profiler. Copy any recorded @c SentryScreenFrames data into the
2929
* profiler instance, and if this is the last profiler being tracked, reset the
3030
* @c SentryFramesTracker data.
3131
*/
32-
SentryProfiler *_Nullable profilerForFinishedTracer(SentryId *traceId);
32+
SentryProfiler *_Nullable profilerForFinishedTracer(SentryId *internalTraceId);
3333

3434
# if defined(TEST) || defined(TESTCI)
3535
void resetConcurrencyTracking(void);

Sources/Sentry/include/SentryProfiler.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ SENTRY_EXTERN_C_END
5050
/**
5151
* Start a profiler, if one isn't already running.
5252
*/
53-
+ (void)startWithTracer:(SentryId *)traceId;
53+
+ (BOOL)startWithTracer:(SentryId *)traceId;
5454

5555
/**
5656
* Stop the profiler if it is running.

Sources/Sentry/include/SentryTracer+Private.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,11 @@ SentryTracer ()
55

66
@property (nonatomic, strong) SentryHub *hub;
77

8+
/**
9+
* We need an immutable identifier to e.g. track concurrent tracers against a static profiler where
10+
* we can use the same ID as a key in the concurrent bookkeeping. @c SentryTracer.traceId can be
11+
* changed by consumers so is unfit for this purpose.
12+
*/
13+
@property (nonatomic, strong, readonly) SentryId *internalID;
14+
815
@end

Tests/SentryProfilerTests/SentryProfilerSwiftTests.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,15 @@ class SentryProfilerSwiftTests: XCTestCase {
263263
try self.assertMetricsPayload(expectedUsageReadings: fixture.mockUsageReadingsPerBatch + 2) // including one sample at the start and the end
264264
}
265265

266+
func testTransactionWithMutatedTracerID() throws {
267+
let span = try fixture.newTransaction()
268+
addMockSamples()
269+
self.fixture.currentDateProvider.advanceBy(nanoseconds: 1.toNanoSeconds())
270+
span.traceId = SentryId()
271+
span.finish()
272+
try self.assertValidProfileData()
273+
}
274+
266275
func testConcurrentProfilingTransactions() throws {
267276
let numberOfTransactions = 10
268277
var spans = [Span]()

0 commit comments

Comments
 (0)