Skip to content

Commit

Permalink
Merge pull request #253 from bugsnag/PLAT-11750-null-url-crash
Browse files Browse the repository at this point in the history
[PLAT-11750] Handle edge case when capturing a URL request with a null URL before Bugsnag is initialized
  • Loading branch information
kstenerud committed Mar 15, 2024
2 parents 1c5f456 + b3b7011 commit 968eb48
Show file tree
Hide file tree
Showing 8 changed files with 121 additions and 7 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ Changelog
* Add package BugsnagPerformanceSwift, and deprecate package BugsnagPerformanceSwiftUI.
[251](https://github.com/bugsnag/bugsnag-cocoa-performance/pull/251)

### Bug fixes

* Guard against an edge case where an auto-captured URL request with a nil URL can crash the library if it's sent before Bugsnag is initialized.
[253](https://github.com/bugsnag/bugsnag-cocoa-performance/pull/253)

## 1.4.1 (2024-02-28)

### Bug fixes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,12 @@ - (void)URLSession:(__unused NSURLSession *)session task:(NSURLSessionTask *)tas
SpanOptions options;
options.makeCurrentContext = false;
auto span = tracer_->startNetworkSpan(task.originalRequest.URL, task.originalRequest.HTTPMethod, options);
objc_setAssociatedObject(task, associatedNetworkSpanKey, span,
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
if (isEarlySpansPhase_) {
markEarlySpan(span);
if (span != nil) {
objc_setAssociatedObject(task, associatedNetworkSpanKey, span,
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
if (isEarlySpansPhase_) {
markEarlySpan(span);
}
}
})
{}
Expand Down
22 changes: 22 additions & 0 deletions features/default/automatic_spans.feature
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,28 @@ Feature: Automatic instrumentation spans
# Only the initial command request should be captured.
Then I wait for 1 span

Scenario: Automatically start a network span that has a null URL
Given I run "AutoInstrumentNetworkNullURLScenario"
And I wait for 1 span
Then the trace "Content-Type" header equals "application/json"
* the trace "Bugsnag-Sent-At" header matches the regex "^\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\d\.\d\d\dZ$"
* every span field "parentSpanId" does not exist
* a span field "name" equals "[HTTP/GET]"
* a span string attribute "http.flavor" exists
* a span string attribute "http.url" matches the regex "http://.*:9[0-9]{3}/reflect\?status=200"
* a span string attribute "http.method" equals "GET"
* a span integer attribute "http.status_code" is greater than 0
* a span integer attribute "http.response_content_length" is greater than 0
* a span string attribute "net.host.connection.type" equals "wifi"
* every span field "spanId" matches the regex "^[A-Fa-f0-9]{16}$"
* every span field "traceId" matches the regex "^[A-Fa-f0-9]{32}$"
* every span field "kind" equals 1
* every span field "startTimeUnixNano" matches the regex "^[0-9]+$"
* every span field "endTimeUnixNano" matches the regex "^[0-9]+$"
* the trace payload field "resourceSpans.0.resource" string attribute "service.name" equals "com.bugsnag.fixtures.PerformanceFixture"
* the trace payload field "resourceSpans.0.resource" string attribute "telemetry.sdk.name" equals "bugsnag.performance.cocoa"
* the trace payload field "resourceSpans.0.resource" string attribute "telemetry.sdk.version" matches the regex "[0-9]\.[0-9]\.[0-9]"

Scenario: ComplexViewScenario
Given I run "ComplexViewScenario"
And I wait for 27 spans
Expand Down
16 changes: 13 additions & 3 deletions features/fixtures/ios/Fixture.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,10 @@
0983A17B2B14BB2000DDF4FF /* BugsnagPerformanceSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 0983A17A2B14BB2000DDF4FF /* BugsnagPerformanceSwift */; };
098808E02B10A6E400DC1677 /* ManualViewLoadPhaseScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 098808DF2B10A6E400DC1677 /* ManualViewLoadPhaseScenario.swift */; };
09DA59A52A6E866B00A06EEE /* ManualNetworkCallbackScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DA59A42A6E866B00A06EEE /* ManualNetworkCallbackScenario.swift */; };
96284DCE2B626B6F00025070 /* AutoInstrumentPreLoadedViewLoadScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96284DCD2B626B6F00025070 /* AutoInstrumentPreLoadedViewLoadScenario.swift */; };
09F025072BA08804007D9F73 /* ObjCURLSession.m in Sources */ = {isa = PBXBuildFile; fileRef = 09F025062BA08804007D9F73 /* ObjCURLSession.m */; };
09F025092BA08817007D9F73 /* AutoInstrumentNetworkNullURLScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09F025082BA08817007D9F73 /* AutoInstrumentNetworkNullURLScenario.swift */; };
960EECE92B2316E1009FAA11 /* AutoInstrumentGenericViewLoadScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 960EECE82B2316E1009FAA11 /* AutoInstrumentGenericViewLoadScenario.swift */; };
96284DCE2B626B6F00025070 /* AutoInstrumentPreLoadedViewLoadScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96284DCD2B626B6F00025070 /* AutoInstrumentPreLoadedViewLoadScenario.swift */; };
9657A8992A3CF75B001CEF5D /* AutoInstrumentTabViewLoadScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9657A8982A3CF75B001CEF5D /* AutoInstrumentTabViewLoadScenario.swift */; };
9657A89B2A3D06EB001CEF5D /* AutoInstrumentNavigationViewLoadScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9657A89A2A3D06EB001CEF5D /* AutoInstrumentNavigationViewLoadScenario.swift */; };
967F6F1A29C4AD300054EED8 /* ReleaseStageNotEnabledScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 967F6F1929C4AD300054EED8 /* ReleaseStageNotEnabledScenario.swift */; };
Expand Down Expand Up @@ -91,8 +93,11 @@
0983A1782B14B20C00DDF4FF /* AutoInstrumentSwiftUIScenario.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoInstrumentSwiftUIScenario.swift; sourceTree = "<group>"; };
098808DF2B10A6E400DC1677 /* ManualViewLoadPhaseScenario.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManualViewLoadPhaseScenario.swift; sourceTree = "<group>"; };
09DA59A42A6E866B00A06EEE /* ManualNetworkCallbackScenario.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManualNetworkCallbackScenario.swift; sourceTree = "<group>"; };
96284DCD2B626B6F00025070 /* AutoInstrumentPreLoadedViewLoadScenario.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoInstrumentPreLoadedViewLoadScenario.swift; sourceTree = "<group>"; };
09F025052BA08804007D9F73 /* ObjCURLSession.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ObjCURLSession.h; sourceTree = "<group>"; };
09F025062BA08804007D9F73 /* ObjCURLSession.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ObjCURLSession.m; sourceTree = "<group>"; };
09F025082BA08817007D9F73 /* AutoInstrumentNetworkNullURLScenario.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoInstrumentNetworkNullURLScenario.swift; sourceTree = "<group>"; };
960EECE82B2316E1009FAA11 /* AutoInstrumentGenericViewLoadScenario.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoInstrumentGenericViewLoadScenario.swift; sourceTree = "<group>"; };
96284DCD2B626B6F00025070 /* AutoInstrumentPreLoadedViewLoadScenario.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoInstrumentPreLoadedViewLoadScenario.swift; sourceTree = "<group>"; };
9657A8982A3CF75B001CEF5D /* AutoInstrumentTabViewLoadScenario.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoInstrumentTabViewLoadScenario.swift; sourceTree = "<group>"; };
9657A89A2A3D06EB001CEF5D /* AutoInstrumentNavigationViewLoadScenario.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoInstrumentNavigationViewLoadScenario.swift; sourceTree = "<group>"; };
967F6F1929C4AD300054EED8 /* ReleaseStageNotEnabledScenario.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReleaseStageNotEnabledScenario.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -200,7 +205,9 @@
0921F02D2A69262300C764EB /* AutoInstrumentNetworkCallbackScenario.swift */,
CBEC89222A458BA70088A3CE /* AutoInstrumentNetworkMultiple.swift */,
CBE0872A29F81BBB007455F2 /* AutoInstrumentNetworkNoParentScenario.swift */,
09F025082BA08817007D9F73 /* AutoInstrumentNetworkNullURLScenario.swift */,
CB3477172901481F0033759C /* AutoInstrumentNetworkWithParentScenario.swift */,
96284DCD2B626B6F00025070 /* AutoInstrumentPreLoadedViewLoadScenario.swift */,
CBC90CE329D1BDE400280884 /* AutoInstrumentSubViewLoadScenario.swift */,
096EE5EE2B3C2B3E006059CE /* AutoInstrumentSwiftUIDeferredScenario.swift */,
0983A1782B14B20C00DDF4FF /* AutoInstrumentSwiftUIScenario.swift */,
Expand All @@ -223,13 +230,14 @@
098808DF2B10A6E400DC1677 /* ManualViewLoadPhaseScenario.swift */,
01E3F99028F46B6700003F44 /* ManualViewLoadScenario.swift */,
CBEC89442A4ED0590088A3CE /* MaxPayloadSizeScenario.swift */,
09F025052BA08804007D9F73 /* ObjCURLSession.h */,
09F025062BA08804007D9F73 /* ObjCURLSession.m */,
CBE615F629A4C1F0000E72D8 /* ParentSpanScenario.swift */,
CBE43A9829A8EFA3000B4205 /* ProbabilityExpiryScenario.swift */,
967F6F1929C4AD300054EED8 /* ReleaseStageNotEnabledScenario.swift */,
CBF62108291A4F47004BEE0B /* RetryScenario.swift */,
01A58C1429096AF4006E4DF7 /* SamplingProbabilityZeroScenario.swift */,
01FE4DC428E1AF9600D1F239 /* Scenario.swift */,
96284DCD2B626B6F00025070 /* AutoInstrumentPreLoadedViewLoadScenario.swift */,
);
path = Scenarios;
sourceTree = "<group>";
Expand Down Expand Up @@ -327,6 +335,7 @@
CBE43A9929A8EFA3000B4205 /* ProbabilityExpiryScenario.swift in Sources */,
CBE0872B29F81BBB007455F2 /* AutoInstrumentNetworkNoParentScenario.swift in Sources */,
09637A432B0617FE00F4F776 /* MazeRunnerCommand.swift in Sources */,
09F025092BA08817007D9F73 /* AutoInstrumentNetworkNullURLScenario.swift in Sources */,
0185C47228F6C983006F9BDC /* AutoInstrumentViewLoadScenario.swift in Sources */,
01FE4DAD28E1AEBD00D1F239 /* ViewController.swift in Sources */,
CBEC89232A458BA70088A3CE /* AutoInstrumentNetworkMultiple.swift in Sources */,
Expand Down Expand Up @@ -363,6 +372,7 @@
01FE4DC728E1D5A400D1F239 /* Fixture.swift in Sources */,
09637A412B060E7C00F4F776 /* CommandReaderThread.swift in Sources */,
CBC90CE029CDD02800280884 /* FirstClassYesScenario.swift in Sources */,
09F025072BA08804007D9F73 /* ObjCURLSession.m in Sources */,
CBC90CDE29CDCFF700280884 /* FirstClassNoScenario.swift in Sources */,
CBC90C4329C466BD00280884 /* ForceUBSan.m in Sources */,
09637A452B0B883B00F4F776 /* FixtureConfig.swift in Sources */,
Expand Down
1 change: 1 addition & 0 deletions features/fixtures/ios/Fixture/Fixture-Bridging-Header.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@
#import <Foundation/Foundation.h>
#import "BugsnagPerformanceConfiguration+Private.h"
#import "Logging.h"
#import "ObjCURLSession.h"
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//
// AutoInstrumentNetworkNullURLScenario.swift
// Fixture
//
// Created by Karl Stenerud on 12.03.24.
//

import Foundation

@objcMembers
class AutoInstrumentNetworkNullURLScenario: Scenario {

override func configure() {
// Early phase span. Make sure it doesn't crash or generate a span
ObjCURLSession.dataTask(with: nil).resume()

super.configure()
config.autoInstrumentNetworkRequests = true
config.networkRequestCallback = { (info: BugsnagPerformanceNetworkRequestInfo) -> BugsnagPerformanceNetworkRequestInfo in
super.ignoreInternalRequests(info: info)
let testUrl = info.url
if (testUrl == nil) {
return info
}
if (self.isMazeRunnerAdministrationURL(url: testUrl!)) {
info.url = nil
}
return info
}
}

func query(string: String) {
let url = URL(string: string, relativeTo: fixtureConfig.reflectURL)!
URLSession.shared.dataTask(with: url).resume()
}

override func run() {
// Force the automatic spans to be sent in a separate trace that we will discard
waitForCurrentBatch()
// Send an actual request to be captured
query(string: "?status=200")
}
}
15 changes: 15 additions & 0 deletions features/fixtures/ios/Scenarios/ObjCURLSession.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//
// ObjCURLSession.h
// Fixture
//
// Created by Karl Stenerud on 12.03.24.
//

#import <Foundation/Foundation.h>

// Wrapper so that we can pass in nil arguments
@interface ObjCURLSession : NSObject

+ (NSURLSessionDataTask *)dataTaskWithURL:(NSURL *)url;

@end
16 changes: 16 additions & 0 deletions features/fixtures/ios/Scenarios/ObjCURLSession.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// ObjCURLSession.m
// Fixture
//
// Created by Karl Stenerud on 12.03.24.
//

#import "ObjCURLSession.h"

@implementation ObjCURLSession

+ (NSURLSessionDataTask *)dataTaskWithURL:(NSURL *)url {
return [[NSURLSession sharedSession] dataTaskWithURL:url];
}

@end

0 comments on commit 968eb48

Please sign in to comment.