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

Pass NSException to the NSException handler with no extra processing #1387

Merged
merged 1 commit into from
May 24, 2022

Conversation

kstenerud
Copy link
Contributor

Goal

NSException is actually implemented using the C++ exception ABI. The Bugsnag C++ handler was doing some preprocessing of the NSException before passing it to the default C++ handler (which then calls the NSException handler). After this, the NSException handler would do the same processing over again.

Since both the C++ and NSException handlers call bsg_kscrashsentry_beginHandlingCrash, and this function checks for re-entry to detect a crash in the crash handler, it gets confused into thinking that a crash has happened in the crash handler when there's actually nothing amiss. The resulting report on the dashboard is still correct, but the messaging on the device is misleading, and the crash handler is doing unnecessary extra work.

Design

Don't do any processing in the C++ handler if it has caught an NSException. Simply pass it along and let the NSException handler do everything (basically behaving the same way it would if C++ exception capture were disabled).

Testing

Tested manually on real iphone, watch, macos, and simulator tvos.

@github-actions
Copy link

Bugsnag.framework binary size did not change - 820,608 bytes

    FILE SIZE        VM SIZE    
 --------------  -------------- 
  +0.5%     +60  +0.5%     +60    [__TEXT]
  -0.0%     -60  -0.0%     -60    __TEXT,__text
  [ = ]       0  [ = ]       0    TOTAL

Generated by 🚫 Danger

bsg_kscrashsentry_endHandlingCrash();
if (bsg_g_originalTerminateHandler != NULL) {
BSG_KSLOG_DEBUG("Detected NSException. Passing to the current NSException handler.");
bsg_g_originalTerminateHandler();
Copy link
Contributor

Choose a reason for hiding this comment

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

How do NSExceptions now get captured?

Does bsg_g_originalTerminateHandler end up calling bsg_ksnsexc_i_handleException via NSUncaughtExceptionHandler?

Copy link
Contributor Author

@kstenerud kstenerud May 23, 2022

Choose a reason for hiding this comment

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

The ObjC runtime sets its own custom C++ exception handler that checks for NSException and does processing if it is (calling whatever NSUncaughtExceptionHandler is registered, or calling abort() if none is).

We tap into both the C++ exception handler and the NSException handler, but since we don't want to process twice, we check in C++ land for NSException and just pass the exception along for the ObjC runtime to find. The runtime then completes the loop back to our NSException handler.

Copy link
Contributor

Choose a reason for hiding this comment

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

What would happen if bsg_g_originalTerminateHandler is (for example) Crashlytics's NSUncaughtExceptionHandler?

Will this PR mean that crashes we previously reported in apps that use both would no longer be detected?

Copy link
Contributor Author

@kstenerud kstenerud May 24, 2022

Choose a reason for hiding this comment

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

Hmm actually, if someone else injects themselves into the callback chain AND forgets to pass it along, we'd get an abort signal instead (if you hook the c++ exception callback and don't pass it along to the next handler, the ObjC runtime will never see it).

So, also capturing information at the C++ level would give an extra defence against poorly implemented third party software (but only if they hook the handler BEFORE we do; if they hook after us and forget to pass it on, all we'll see is https://app.bugsnag.com/bugsnag-test-projects/karl-watchos/errors/628c812aa8150d0009d7c56f?filters[event.since]=30d&filters[error.status]=open&event_id=628c812a00946907a7480000 ).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nope, my initial testing was flawed, and further testing showed that this C++ code is not capturing NSException data after all. So we're better off going with the approach in this PR to ensure that bsg_kscrashsentry_beginHandlingCrash never gets called more than once for a single crash.

@kstenerud
Copy link
Contributor Author

Closed in favour of #1389

@kstenerud kstenerud closed this May 24, 2022
@kstenerud
Copy link
Contributor Author

Reopening because the other approach leaves open a concurrency risk.

@kstenerud kstenerud reopened this May 24, 2022
@kstenerud kstenerud requested a review from nickdowell May 24, 2022 08:49
@kstenerud kstenerud merged commit 831d5fb into next May 24, 2022
@kstenerud kstenerud deleted the PLAT-8488-crash-in-handler branch May 24, 2022 10:00
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