Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RUM-3183 fix: Fix crash on accessing request.allHTTPHeaderFields #1843

Merged

Conversation

ncreated
Copy link
Member

@ncreated ncreated commented May 17, 2024

What and why?

📦 🧯 Fixes #1638

As we observed in #1638, accessing request.allHTTPHeaderFields is not safe and leads to crashes with undefined root cause. To avoid this, instead we use request.value(forHTTPHeaderField:) to only read headers known by the SDK.

How?

Removed usage of .allHTTPHeaderFields by replacing it with higher-level .value(forHTTPHeaderField:) API.

I also added linter rule to avoid using allHTTPHeaderFields if possible.

Suspected problem with .allHTTPHeaderFields safety

I failed to reproduce the crash, although by exploring swift-corelibs-foundation code, I suspect it to relate to the Obj-c → Swift bridging, somewhere in this code. Symbols ran during the bridging correspond to some reported in #1638 stack traces. This may invalidate our earlier assumption on the problem being caused by lack of thread safety in .allHTTPHeaderFields implementation.

Review checklist

  • Feature or bugfix MUST have appropriate tests (unit, integration)
  • Make sure each commit and the PR mention the Issue number or JIRA reference
  • Add CHANGELOG entry for user facing changes

Custom CI job configuration (optional)

  • Run unit tests for Core, RUM, Trace, Logs, CR and WVT
  • Run unit tests for Session Replay
  • Run integration tests
  • Run smoke tests
  • Run tests for tools/

@ncreated ncreated self-assigned this May 17, 2024
As observed in #1638, accessing `request.allHTTPHeaderFields` is not
safe and leads to crashes with undefined root cause. To avoid this, instead we use `request.value(forHTTPHeaderField:)`
to only read headers known by the SDK.
@ncreated ncreated force-pushed the ncreated/RUM-3183/fix-crash-on-accessing-allHTTPHeaderFields branch from cdaf482 to 1c252a0 Compare May 17, 2024 09:42
Comment on lines 32 to 33
var knownHTTPHeaderFields: [String: String] = [:]
addHeaderIfExists(request: request, field: TracingHTTPHeaders.originField, to: &knownHTTPHeaderFields)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 We could consider adding other known headers here, notably the ones related to distributed tracing (x-datadog-*, W3C and B3). Not doing this as these headers are not required for upstream code. The only reason for allHTTPHeaderFields existence, was accessing the x-datadog-origin header which is added here.

Alternatively, we could replace the entire knownHTTPHeaderFields prop with datadogOriginField. This way we will move even further away from the unsafe consideration of "all headers".

Open for discussion 🧠.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/concerning

This seems risky, by only looking for some headers. We will be blind where knownHTTPHeaderFields is actually used.

Can we copy all headers for instance?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we copy all headers for instance?

I don't see any option without using the problematic allHTTPHeaderFields API.

This seems risky, by only looking for some headers. We will be blind where knownHTTPHeaderFields is actually used.

Makes sense 👍. I changed it to the alternative approach by replacing knownHTTPHeaderFields with ddOriginHeaderValue prop.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is those places where I prefer using assertions (debug only) but not sure how customer can selectively disable them for libraries only else it is too much of noise.

I'm okay.

@ncreated ncreated marked this pull request as ready for review May 17, 2024 09:46
@ncreated ncreated requested review from a team as code owners May 17, 2024 09:46
@datadog-datadog-prod-us1
Copy link

datadog-datadog-prod-us1 bot commented May 17, 2024

Datadog Report

Branch report: ncreated/RUM-3183/fix-crash-on-accessing-allHTTPHeaderFields
Commit report: fa145d1
Test service: dd-sdk-ios

✅ 0 Failed, 3222 Passed, 0 Skipped, 2m 31.53s Total Time
🔻 Test Sessions change in coverage: 9 decreased, 4 increased

🔻 Code Coverage Decreases vs Default Branch (9)

This report shows up to 5 code coverage decreases.

  • test DatadogCrashReportingTests tvOS 27.39% (-0.32%) - Details
  • test DatadogCrashReportingTests iOS 27.36% (-0.3%) - Details
  • test DatadogLogsTests tvOS 46.14% (-0.26%) - Details
  • test DatadogLogsTests iOS 46.09% (-0.26%) - Details
  • test DatadogWebViewTrackingTests iOS 18.31% (-0.21%) - Details

guard let headers = request.allHTTPHeaderFields else {
guard let headers = request.allHTTPHeaderFields else { // swiftlint:disable:this unsafe_all_http_header_fields
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 It is IMO fine to ignore this warning here as URLSessionInterceptor is only used by Alamofire extension, which should be deprecated and removed. We have no crash reports pointing this usage, hence this should be safe.

Alternatively, we could use value(forHTTPHeaderField:) to read (one by one) all headers required by DD, B3 and W3C header readers in following lines.

Copy link
Member

@maxep maxep May 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer the alternative mentioned, it's clearer 👍 thanks for the change :)
sorry, wrong line..

Yes, this is fine 👍

@ncreated ncreated requested a review from ganeshnj May 20, 2024 14:05
maxep
maxep previously approved these changes May 21, 2024
Copy link
Member

@maxep maxep left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤞

guard let headers = request.allHTTPHeaderFields else {
guard let headers = request.allHTTPHeaderFields else { // swiftlint:disable:this unsafe_all_http_header_fields
Copy link
Member

@maxep maxep May 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer the alternative mentioned, it's clearer 👍 thanks for the change :)
sorry, wrong line..

Yes, this is fine 👍

excluded_match_kinds:
- comment
- doccomment
message: "`URLRequest.allHTTPHeaderFields` is considered unsafe. Prefer using `URLRequest.value(forHTTPHeaderField:)`. See full context in https://github.com/DataDog/dd-sdk-ios/pull/1843"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🥇

/// Introduced in response to concerns raised in https://github.com/DataDog/dd-sdk-ios/issues/1638
/// it makes a copy of request attributes, safeguarding against potential thread safety issues arising from concurrent
/// mutations (see more context in https://github.com/DataDog/dd-sdk-ios/pull/1767 ).
public struct ImmutableRequest {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/suggestion I guess this could be merged with URLSessionTaskInterception, or an inner type.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this PR we just move this type to separate file (it was added earlier). While merging types is simple in code, updating tests is harder as it adds more permutations to cover. For the sake of isolated testing, let's keep it this way.

ganeshnj
ganeshnj previously approved these changes May 22, 2024
@ncreated ncreated dismissed stale reviews from ganeshnj and maxep via 8fe0422 May 22, 2024 09:17
@ncreated ncreated requested review from ganeshnj and maxep May 22, 2024 13:05
@ncreated ncreated merged commit 40ce508 into develop May 22, 2024
8 checks passed
@ncreated ncreated deleted the ncreated/RUM-3183/fix-crash-on-accessing-allHTTPHeaderFields branch May 22, 2024 13:27
This was referenced May 29, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Crashes in NetworkInstrumentationFeature.extractTrace and NetworkInstrumentationFeature.intercept
3 participants