Skip to content

Commit

Permalink
[runtime] Detect recursion when handling unhandled Objective-C except…
Browse files Browse the repository at this point in the history
…ions. Fixes #14796. (#20276)

Detect recursion when handling unhandled Objective-C exceptions, which can happen if:

* We install a handler for unhandled Objective-C exceptions.
* An unhandled Objective-C exception is caught.
* We convert the unhandled Objective-C exception to a managed exception, and throw that.
* Nobody handles the managed exception either, so we convert it to an Objective-C exception and throw that.
* We re-enter the unhandled Objective-C exception handler, and the cycle continues.
* Eventually the process crashes due to a stack overflow.

Fixes #14796.
  • Loading branch information
rolfbjarne authored Mar 13, 2024
1 parent 2a1300e commit 9b8869b
Showing 1 changed file with 17 additions and 3 deletions.
20 changes: 17 additions & 3 deletions runtime/runtime.m
Original file line number Diff line number Diff line change
Expand Up @@ -1133,18 +1133,24 @@ -(void) xamarinSetFlags: (enum XamarinGCHandleFlags) flags;
abort ();
}

extern "C" {
static thread_local int xamarin_handling_unhandled_exceptions = 0;
}

static void
exception_handler (NSException *exc)
{
// COOP: We won't get here in coop-mode, because we don't set the uncaught objc exception handler in that case.
LOG (PRODUCT ": Received unhandled ObjectiveC exception: %@ %@", [exc name], [exc reason]);

if (xamarin_is_gc_coop) {
PRINT ("Uncaught Objective-C exception: %@", exc);
assert (false); // Re-throwing the Objective-C exception will probably just end up with infinite recursion
if (xamarin_handling_unhandled_exceptions == 1) {
PRINT ("Detected recursion when handling uncaught Objective-C exception: %@", exc);
abort ();
}

xamarin_handling_unhandled_exceptions = 1;
xamarin_throw_ns_exception (exc);
xamarin_handling_unhandled_exceptions = 0;
}

#if defined (DEBUG)
Expand Down Expand Up @@ -2369,6 +2375,14 @@ -(void) xamarinSetFlags: (enum XamarinGCHandleFlags) flags;
xamarin_gchandle_free (exception_gchandle);
}

// If we end up here as part of an unhandled Objective-C exception, we're also trying to detect an infinite loop by
// setting the xamarin_handling_unhandled_exceptions variable to 1 before processing the unhandled Objective-C exception,
// and clearing it afterwards. However, the clearing of the variable will never happen if Mono unwinds native
// stack frames, and then on the next unhandled Objective-C exception we'll think we're recursing when we're really not.
// (FWIW this is yet another reason why letting Mono unhandled native frames is a really bad idea).
// So here we work around that by clearing the variable before letting Mono unwind native frames.
xamarin_handling_unhandled_exceptions = 0;

mono_raise_exception ((MonoException *) exception);
#endif
break;
Expand Down

8 comments on commit 9b8869b

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

Please sign in to comment.