Skip to content

Conversation

@melekr
Copy link
Contributor

@melekr melekr commented Oct 8, 2025

Summary

When Unity full stack traces include native frames that end after the module (for example: 0x00007ffad7723088 (UnityPlayer)) the SDK crashes with ArgumentOutOfRangeException.
This PR makes native frame parsing safe, ensures all string operations are bounds-checked, and adds unit tests to confirm the fix.

Changes

ConvertStackFrames

  • Added validation to ensure appears before before parsing.
  • Skips malformed frames safely instead of continuing and throwing.

SetNativeStackTraceInformation : Added parsing logic to handle:

  • Frames with or without symbols.
  • Missing or malformed library delimiters.
  • Optional [file:line] suffixes.
  • Adding checks to avoid index errors.
  • Normalizing known wrapper prefixes.

Tests Added NativeParserTests:

  • Parses frames with and without symbols.
  • Returns raw text for unknown formats.
  • Verifies no exceptions are thrown.

ref: BT-5953

Add bounds checks in SetNativeStackTraceInformation
Add NativeParserTests to validate safe native frame parsing
@melekr melekr self-assigned this Oct 8, 2025
@melekr melekr marked this pull request as draft October 8, 2025 05:56
@melekr melekr requested a review from konraddysput October 8, 2025 05:56
{
private static BacktraceStackFrame Parse(string frame)
{
var instance = new BacktraceUnhandledException("msg", "");
Copy link
Collaborator

Choose a reason for hiding this comment

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

this is not a valid way to test it. Please follow the parser tests that we already have in the tests.

var mi = typeof(BacktraceUnhandledException)
.GetMethod("SetNativeStackTraceInformation",
BindingFlags.NonPublic | BindingFlags.Instance);
Assert.IsNotNull(mi);
Copy link
Collaborator

Choose a reason for hiding this comment

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

what it tests?

private static BacktraceStackFrame Parse(string frame)
{
var instance = new BacktraceUnhandledException("msg", "");
var mi = typeof(BacktraceUnhandledException)
Copy link
Collaborator

Choose a reason for hiding this comment

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

what is mi?

[Test]
public void SymbolLessModuleLine_ParsesAddressAndLibrary_NoThrow()
{
var f = Parse("0x00007ffad7723088 (UnityPlayer)");
Copy link
Collaborator

Choose a reason for hiding this comment

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

what is f?

targetPlatform: ${{ matrix.targetPlatform }}
projectPath: ${{ matrix.projectPath }}/
allowDirtyBuild: true
allowDirtyBuild: true
Copy link
Collaborator

Choose a reason for hiding this comment

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

broken yaml

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yup allowDirtyBuild & testMode cannot co-exist. also allowDirtyBuild shouldn't have been used here.

using System.Text;
using UnityEngine.TestTools;

namespace Backtrace.Unity.Tests.Runtime{
Copy link
Collaborator

Choose a reason for hiding this comment

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

this file is not formatted

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's a draft to review the native parsing logic;
Formatting, var names and other cosmetics will be fixed later.
I only need your thoughts on Parsing for now ~

using UnityEngine.TestTools;

namespace Backtrace.Unity.Tests.Runtime{
public class NativeParserTests
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we be more specific? I would call it NativeStackTraceParser instead

Copy link
Contributor Author

Choose a reason for hiding this comment

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

sure

//methodname index should be greater than 0 AND '(' should be before ')'
if (methodNameEndIndex < 1 && frameString[methodNameEndIndex - 1] != '(')
// we require a '(' that appears before this ')'
int openParenIndex = frameString.LastIndexOf('(', methodNameEndIndex);
Copy link
Collaborator

Choose a reason for hiding this comment

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

typo paren

/// Try to convert native stack frame
/// Try to safely convert a native stack frame string into a Backtrace stack frame.
/// Handles frames with or without symbols (e.g. "0xADDR (Module)" or "0xADDR (Module) Symbol").
/// Prevents ArgumentOutOfRangeException by validating index ranges and allowing empty symbols.
Copy link
Collaborator

Choose a reason for hiding this comment

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

whats the point of adding this info to the summary?

Comment on lines +285 to +286
FunctionName = string.Empty,
Line = 0
Copy link
Collaborator

Choose a reason for hiding this comment

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

Are you sure you need to set them?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, needed to guarantee consistent defaults for native frames (especially when the parser exits early). It avoids nulls and keeps the frame type marked as Native even for empty input.

Copy link
Collaborator

Choose a reason for hiding this comment

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

yeah but why we should avoid nulls? I would say null in this case gives us important information - do not set entry in the json field. Are you sure symbolication process later will handle it properly?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Solid point, exactly what I needed!
I’ll keep StackFrameType = Native but stop forcing FunctionName = "" and Line = 0.
For frames we can’t parse I’ll leave FunctionName null and only set Line when found.

@melekr melekr requested a review from konraddysput October 8, 2025 19:58
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.

2 participants