Description
Debugging a net11.0-ios app on the iOS simulator with the CoreCLR interpreter, the target process crashes when the debugger walks a managed thread's stack — i.e., when stopping at a breakpoint and then stepping (or otherwise retrieving active frames). The fault is a null-pointer dereference in the DAC stack-frame iterator.
Stepping is what reliably triggers it; the debugger requests the call stack via ICorDebug (CordbThread::GetActiveFunctions → ShimStackWalk), which calls into the DAC and segfaults.
Faulting thread (EXC_BAD_ACCESS / SIGSEGV, KERN_INVALID_ADDRESS at 0x0)
libmscordaccore.dylib StackFrameIterator::CheckForSkippedFrames()
libmscordaccore.dylib StackFrameIterator::ResetRegDisp(REGDISPLAY*, bool)
libmscordaccore.dylib DacDbiInterfaceImpl::SetStackWalkCurrentContext(VMPTR_Base<Thread, __DPtr<Thread>>, void**, CorDebugSetContextFlag, DT_CONTEXT*)
libmscordbi.dylib CordbStackWalk::SetContextWorker(CorDebugSetContextFlag, unsigned int, unsigned char*)
libmscordbi.dylib CordbStackWalk::SetContext(CorDebugSetContextFlag, unsigned int, unsigned char*)
libmscordbi.dylib ShimStackWalk::CheckInternalFrame(ICorDebugFrame*, ShimStackWalk::StackWalkInfo*, ICorDebugThread3*, ICorDebugStackWalk*)
libmscordbi.dylib ShimStackWalk::Populate()
libmscordbi.dylib ShimStackWalk::ShimStackWalk(ShimProcess*, ICorDebugThread*)
libmscordbi.dylib ShimProcess::LookupOrCreateShimStackWalk(ICorDebugThread*)
libmscordbi.dylib CordbThread::GetActiveFunctions(unsigned int, unsigned int*, _COR_ACTIVE_FUNCTION*)
(invoked by the debugger via the ICorDebug API)
The thread being walked is executing under the CoreCLR interpreter; at the time of a single-step its stack looks like:
libcoreclr.dylib DebuggerController::DispatchPatchOrSingleStep(Thread*, _CONTEXT*, unsigned char const*, SCAN_TRIGGER)
libcoreclr.dylib DebuggerController::DispatchNativeException(_EXCEPTION_RECORD*, _CONTEXT*, unsigned int, Thread*)
libcoreclr.dylib Debugger::FirstChanceNativeException(_EXCEPTION_RECORD*, _CONTEXT*, unsigned int, Thread*, int)
libcoreclr.dylib InterpExecMethod(InterpreterFrame*, InterpMethodContextFrame*, InterpThreadContext*, ExceptionClauseArgs*)
libcoreclr.dylib ExecuteInterpretedMethod
libcoreclr.dylib InterpreterStubRetI8
StackFrameIterator::CheckForSkippedFrames() appears not to handle interpreter frames (InterpreterFrame / InterpMethodContextFrame) and dereferences null while resetting the register display for the active context.
Secondary issue — crash reporting masks the fault
With crash reporting enabled (default), the above SIGSEGV is not what's observed. CoreCLR's in-proc crash reporter runs from the signal handler and tries to suspend the runtime to enumerate threads, but deadlocks acquiring the ThreadStore lock (the faulting/stepping thread is hijacked in RareDisablePreemptiveGC → WaitSuspendEventsHelper):
libcoreclr.dylib CrstBase::Enter()
libcoreclr.dylib ThreadSuspend::LockThreadStore(ThreadSuspend::SUSPEND_REASON)
libcoreclr.dylib ThreadSuspend::SuspendEE(ThreadSuspend::SUSPEND_REASON)
libcoreclr.dylib CrashReportEnumerateThreads(...)
libcoreclr.dylib InProcCrashReporter::EmitThreads(InProcCrashReportCrashKind, void*)
libcoreclr.dylib InProcCrashReporter::CreateReport(int, void*)
libcoreclr.dylib InProcCrashReportSignalDispatcher(int, void*, void*)
The crash report never completes, so CrashReportWatchdog::Abort() fires and the process exits with SIGABRT (Abort trap: 6) after the watchdog timeout. Setting DOTNET_EnableCrashReport=0 / DOTNET_DbgEnableMiniDump=0 removes the masking and surfaces the underlying SIGSEGV immediately (faulting stack above).
So there are arguably two problems:
- Primary: null deref in
StackFrameIterator::CheckForSkippedFrames during a debugger stack walk over CoreCLR interpreter frames.
- Secondary: the in-proc crash reporter can deadlock in
SuspendEE when the crash originates from a thread already hijacked for debugging, leaving CrashReportWatchdog to abort.
Steps to reproduce
- Create/run a
net11.0-ios app configured to use the CoreCLR interpreter on the iOS simulator.
- Debug it (Pair-to-Mac), hit a breakpoint in a button handler.
- Step (Step Over / Into).
- The target crashes —
SIGABRT via the crash-report watchdog by default, or SIGSEGV (null deref, stack above) with crash reporting disabled.
Notes / observations
- Hitting/landing on a breakpoint works; stepping (which forces the active-frame stack walk mid-interpreter-execution) is the reliable trigger.
- Disabling implicit property/func evaluation in the debugger does not help — pure single-step (
DispatchPatchOrSingleStep) crashes the same way.
Environment
- App:
net11.0-ios, iossimulator-arm64, CoreCLR + interpreter
- Simulator: iPhone 16 Pro, iOS 18.5; host macOS 26.5.1 (25F80)
- iOS SDK:
Microsoft.iOS.Sdk.net11.0_26.5 26.5.11546-net11-p5
- Debugger: Visual Studio 18 (Canary) + Pair-to-Mac, remote CoreCLR debugging
Crash reports (.ips) captured for all three signatures (2× watchdog SIGABRT, 1× real SIGSEGV with crash reporting disabled) — available on request.
Description
Debugging a
net11.0-iosapp on the iOS simulator with the CoreCLR interpreter, the target process crashes when the debugger walks a managed thread's stack — i.e., when stopping at a breakpoint and then stepping (or otherwise retrieving active frames). The fault is a null-pointer dereference in the DAC stack-frame iterator.Stepping is what reliably triggers it; the debugger requests the call stack via
ICorDebug(CordbThread::GetActiveFunctions→ShimStackWalk), which calls into the DAC and segfaults.Faulting thread (
EXC_BAD_ACCESS/SIGSEGV,KERN_INVALID_ADDRESS at 0x0)The thread being walked is executing under the CoreCLR interpreter; at the time of a single-step its stack looks like:
StackFrameIterator::CheckForSkippedFrames()appears not to handle interpreter frames (InterpreterFrame/InterpMethodContextFrame) and dereferences null while resetting the register display for the active context.Secondary issue — crash reporting masks the fault
With crash reporting enabled (default), the above
SIGSEGVis not what's observed. CoreCLR's in-proc crash reporter runs from the signal handler and tries to suspend the runtime to enumerate threads, but deadlocks acquiring the ThreadStore lock (the faulting/stepping thread is hijacked inRareDisablePreemptiveGC→WaitSuspendEventsHelper):The crash report never completes, so
CrashReportWatchdog::Abort()fires and the process exits withSIGABRT(Abort trap: 6) after the watchdog timeout. SettingDOTNET_EnableCrashReport=0/DOTNET_DbgEnableMiniDump=0removes the masking and surfaces the underlyingSIGSEGVimmediately (faulting stack above).So there are arguably two problems:
StackFrameIterator::CheckForSkippedFramesduring a debugger stack walk over CoreCLR interpreter frames.SuspendEEwhen the crash originates from a thread already hijacked for debugging, leavingCrashReportWatchdogto abort.Steps to reproduce
net11.0-iosapp configured to use the CoreCLR interpreter on the iOS simulator.SIGABRTvia the crash-report watchdog by default, orSIGSEGV(null deref, stack above) with crash reporting disabled.Notes / observations
DispatchPatchOrSingleStep) crashes the same way.Environment
net11.0-ios,iossimulator-arm64, CoreCLR + interpreterMicrosoft.iOS.Sdk.net11.0_26.526.5.11546-net11-p5