Skip to content

Commit 4e7c3e4

Browse files
Suppress Native SIGSEGV signal errors on Android (#3903)
1 parent e3425c9 commit 4e7c3e4

File tree

5 files changed

+157
-5
lines changed

5 files changed

+157
-5
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
### Fixes
66

7+
- Native SIGSEGV errors resulting from managed NullReferenceExceptions are now suppressed on Android ([#3903](https://github.com/getsentry/sentry-dotnet/pull/3903))
78
- OTel activities that are marked as not recorded are no longer sent to Sentry ([#3890](https://github.com/getsentry/sentry-dotnet/pull/3890))
89
- Fixed envelopes with oversized attachments getting stuck in __processing ([#3938](https://github.com/getsentry/sentry-dotnet/pull/3938))
910
- Unknown stack frames in profiles on .NET 8+ ([#3942](https://github.com/getsentry/sentry-dotnet/pull/3942))
@@ -12,7 +13,6 @@
1213
## 5.1.0
1314

1415
### Significant change in behavior
15-
1616
- The User.IpAddress is now only set to `{{auto}}` when `SendDefaultPii` is enabled. This change gives you control over IP address collection directly on the client ([#3893](https://github.com/getsentry/sentry-dotnet/pull/3893))
1717

1818
### Features

src/Sentry/Platforms/Android/AndroidOptions.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,21 @@ public class AndroidOptions
2525
/// </summary>
2626
/// <seealso cref="LogCatIntegration" />
2727
public int LogCatMaxLines { get; set; } = 1000;
28+
29+
/// <summary>
30+
/// <para>
31+
/// Whether to suppress capturing SIGSEGV (Segfault) errors in the Native SDK.
32+
/// </para>
33+
/// <para>
34+
/// When managed code results in a NullReferenceException, this also causes a SIGSEGV (Segfault). Duplicate
35+
/// events (one managed and one native) can be prevented by suppressing native Segfaults, which may be
36+
/// convenient.
37+
/// </para>
38+
/// <para>
39+
/// Enabling this may prevent the capture of Segfault originating from native (not managed) code... so it may
40+
/// prevent the capture of genuine native Segfault errors.
41+
/// </para>
42+
/// </summary>
43+
public bool SuppressSegfaults { get; set; } = false;
2844
}
2945
}

src/Sentry/Platforms/Android/BindableAndroidSentryOptions.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,13 @@ public class AndroidOptions
1414
{
1515
public LogCatIntegrationType? LogCatIntegration { get; set; }
1616
public int? LogCatMaxLines { get; set; }
17+
public bool? SuppressSegfaults { get; set; }
1718

1819
public void ApplyTo(SentryOptions.AndroidOptions options)
1920
{
2021
options.LogCatIntegration = LogCatIntegration ?? options.LogCatIntegration;
2122
options.LogCatMaxLines = LogCatMaxLines ?? options.LogCatMaxLines;
23+
options.SuppressSegfaults = SuppressSegfaults ?? options.SuppressSegfaults;
2224
}
2325
}
2426
}

src/Sentry/Platforms/Android/SentrySdk.cs

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using Sentry.Android;
44
using Sentry.Android.Callbacks;
55
using Sentry.Android.Extensions;
6+
using Sentry.Extensibility;
67
using Sentry.JavaSdk.Android.Core;
78

89
// Don't let the Sentry Android SDK auto-init, as we do that manually in SentrySdk.Init
@@ -99,10 +100,7 @@ private static void InitSentryAndroidSdk(SentryOptions options)
99100
}
100101
}
101102

102-
if (options.Native.EnableBeforeSend && options.BeforeSendInternal is { } beforeSend)
103-
{
104-
o.BeforeSend = new BeforeSendCallback(beforeSend, options, o);
105-
}
103+
o.BeforeSend = new BeforeSendCallback(BeforeSendWrapper(options), options, o);
106104

107105
// These options are from SentryAndroidOptions
108106
o.AttachScreenshot = options.Native.AttachScreenshot;
@@ -172,6 +170,26 @@ private static void InitSentryAndroidSdk(SentryOptions options)
172170
// TODO: Pause/Resume
173171
}
174172

173+
internal static Func<SentryEvent, SentryHint, SentryEvent?> BeforeSendWrapper(SentryOptions options)
174+
{
175+
return (evt, hint) =>
176+
{
177+
// Suppress SIGSEGV errors.
178+
// See: https://github.com/getsentry/sentry-dotnet/pull/3903
179+
if (options.Android.SuppressSegfaults
180+
&& evt.SentryExceptions?.FirstOrDefault() is { Type: "SIGSEGV", Value: "Segfault" })
181+
{
182+
options.LogDebug("Suppressing SIGSEGV (this will be thrown as a managed exception instead)");
183+
return null;
184+
}
185+
186+
// Call the user defined BeforeSend callback, if it's defined - otherwise return the event as-is
187+
return (options.Native.EnableBeforeSend && options.BeforeSendInternal is { } beforeSend)
188+
? beforeSend(evt, hint)
189+
: evt;
190+
};
191+
}
192+
175193
private static void AndroidEnvironment_UnhandledExceptionRaiser(object? _, RaiseThrowableEventArgs e)
176194
{
177195
var description = "This exception was caught by the Android global error handler.";
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
#if ANDROID
2+
namespace Sentry.Tests.Platforms.Android;
3+
4+
public class SentrySdkTests
5+
{
6+
[Fact]
7+
public void BeforeSendWrapper_Suppress_SIGSEGV_ReturnsNull()
8+
{
9+
// Arrange
10+
var options = new SentryOptions();
11+
options.Android.SuppressSegfaults = true;
12+
var evt = new SentryEvent
13+
{
14+
SentryExceptions = new[]
15+
{
16+
new SentryException
17+
{
18+
Type = "SIGSEGV",
19+
Value = "Segfault"
20+
}
21+
}
22+
};
23+
var hint = new SentryHint();
24+
25+
// Act
26+
var result = SentrySdk.BeforeSendWrapper(options).Invoke(evt, hint);
27+
28+
// Assert
29+
result.Should().BeNull();
30+
}
31+
32+
[Fact]
33+
public void BeforeSendWrapper_DontSupress_SIGSEGV_ReturnsEvent()
34+
{
35+
// Arrange
36+
var options = new SentryOptions();
37+
options.Android.SuppressSegfaults = false;
38+
var evt = new SentryEvent
39+
{
40+
SentryExceptions = new[]
41+
{
42+
new SentryException
43+
{
44+
Type = "SIGSEGV",
45+
Value = "Segfault"
46+
}
47+
}
48+
};
49+
var hint = new SentryHint();
50+
51+
// Act
52+
var result = SentrySdk.BeforeSendWrapper(options).Invoke(evt, hint);
53+
54+
// Assert
55+
result.Should().Be(evt);
56+
}
57+
58+
[Fact]
59+
public void BeforeSendWrapper_BeforeSendCallbackDefined_CallsBeforeSend()
60+
{
61+
// Arrange
62+
var beforeSend = Substitute.For<Func<SentryEvent, SentryHint, SentryEvent>>();
63+
beforeSend.Invoke(Arg.Any<SentryEvent>(), Arg.Any<SentryHint>()).Returns(callInfo => callInfo.Arg<SentryEvent>());
64+
65+
var options = new SentryOptions();
66+
options.Native.EnableBeforeSend = true;
67+
options.SetBeforeSend(beforeSend);
68+
var evt = new SentryEvent();
69+
var hint = new SentryHint();
70+
71+
// Act
72+
var result = SentrySdk.BeforeSendWrapper(options).Invoke(evt, hint);
73+
74+
// Assert
75+
beforeSend.Received(1).Invoke(Arg.Any<SentryEvent>(), Arg.Any<SentryHint>());
76+
result.Should().Be(evt);
77+
}
78+
79+
[Fact]
80+
public void BeforeSendWrapper_NoBeforeSendCallback_ReturnsEvent()
81+
{
82+
// Arrange
83+
var options = new SentryOptions();
84+
options.Native.EnableBeforeSend = true;
85+
var evt = new SentryEvent();
86+
var hint = new SentryHint();
87+
88+
// Act
89+
var result = SentrySdk.BeforeSendWrapper(options).Invoke(evt, hint);
90+
91+
// Assert
92+
result.Should().Be(evt);
93+
}
94+
95+
[Fact]
96+
public void BeforeSendWrapper_NativeBeforeSendDisabled_ReturnsEvent()
97+
{
98+
// Arrange
99+
var beforeSend = Substitute.For<Func<SentryEvent, SentryHint, SentryEvent>>();
100+
beforeSend.Invoke(Arg.Any<SentryEvent>(), Arg.Any<SentryHint>()).Returns(_ => null);
101+
102+
var options = new SentryOptions();
103+
options.SetBeforeSend(beforeSend);
104+
options.Native.EnableBeforeSend = false;
105+
var evt = new SentryEvent();
106+
var hint = new SentryHint();
107+
108+
// Act
109+
var result = SentrySdk.BeforeSendWrapper(options).Invoke(evt, hint);
110+
111+
// Assert
112+
beforeSend.DidNotReceive().Invoke(Arg.Any<SentryEvent>(), Arg.Any<SentryHint>());
113+
result.Should().Be(evt);
114+
}
115+
}
116+
#endif

0 commit comments

Comments
 (0)