diff --git a/core/debuggercontroller.cpp b/core/debuggercontroller.cpp index 1713fe19..629ea127 100644 --- a/core/debuggercontroller.cpp +++ b/core/debuggercontroller.cpp @@ -1986,6 +1986,12 @@ bool DebuggerController::RemoveEventCallbackInternal(size_t index) void DebuggerController::PostDebuggerEvent(const DebuggerEvent& event) { + // During conditional breakpoint auto-resume, suppress the ResumeEventType that adapters + // post inside Go(). The target is already considered running by the UI, and posting this + // event from the dispatcher thread would trigger a re-entrant warning. + if (m_suppressResumeEvent && event.type == ResumeEventType) + return; + auto pending = std::make_shared(); pending->event = event; std::future future = pending->done.get_future(); @@ -2059,13 +2065,19 @@ void DebuggerController::DebuggerMainThread() if (uint64_t ip = m_state->IP(); !isStepOperation && m_state->GetBreakpoints()->ContainsAbsolute(ip)) { - if (!EvaluateBreakpointCondition(ip)) + if (!EvaluateBreakpointCondition(ip) && !m_userRequestedBreak) { m_lastAdapterStopEventConsumed = true; current->done.set_value(); - // using m_adapter->Go() directly instead of Go() to avoid mutex deadlock - // since we're already inside ExecuteAdapterAndWait's event processing + // Using m_adapter->Go() directly instead of Go() to avoid mutex deadlock + // since we're already inside ExecuteAdapterAndWait's event processing. + // Suppress the ResumeEventType that some adapters post synchronously inside + // Go() — the UI already considers the target running, and posting from the + // dispatcher thread would be unexpected. + m_suppressResumeEvent = true; m_adapter->Go(); + m_suppressResumeEvent = false; + m_state->SetExecutionStatus(DebugAdapterRunningStatus); continue; } } diff --git a/core/debuggercontroller.h b/core/debuggercontroller.h index a7df4703..f298d902 100644 --- a/core/debuggercontroller.h +++ b/core/debuggercontroller.h @@ -121,6 +121,10 @@ namespace BinaryNinjaDebugger { bool m_lastAdapterStopEventConsumed = true; + // When true, ResumeEventType events are suppressed in PostDebuggerEvent. + // Used during conditional breakpoint auto-resume to avoid posting events from the dispatcher thread. + bool m_suppressResumeEvent = false; + bool m_inputFileLoaded = false; bool m_initialBreakpointSeen = false;