Skip to content

bug: Terminal can crash before drop when a dragged tab crosses the content boundary #20514

Description

@zackees

Context

This issue intentionally tracks a distinct user-visible manifestation of the tab-drag crash reported in #20512:

  • The crash occurs while the mouse button is still held.
  • The tab does not need to be dropped, detached, or merged into another window.
  • Crossing from the tab strip into terminal content, or back out of the content, can immediately terminate the whole WindowsTerminal.exe process.
  • To the user, an ordinary reorder gesture or an accidental movement below the tab strip appears to destroy the entire window and all of its tabs.

The fully symbolized dump confirms this manifestation happens specifically while XAML is processing drag-enter/drag-leave boundary transitions. It has the same implementation-level root cause as #20512, but the reproduction and UX are different enough to track independently.

Environment

Windows Terminal package: 1.24.11911.0
WindowsTerminal.exe:       1.24.2607.10001
Windows:                   Windows 10 Pro x64, 10.0.19045
Windows.UI.Xaml.dll:       10.0.19041.6456
Process uptime:            00:55:23
Dump:                      806 MB full user-mode dump

Steps to reproduce

This is timing-sensitive and is easier to reproduce when the terminal is processing sustained output:

  1. Open a Terminal window containing multiple tabs.
  2. Run a workload that produces continuous/high-volume terminal output.
  3. Click and hold a tab as if reordering or detaching it.
  4. Move the pointer down across the boundary between the tab strip and terminal content.
  5. Continue holding the mouse button; do not drop the tab.
  6. Moving into or back out of the terminal content can terminate the entire Terminal process.

Expected behavior

Crossing the terminal-content boundary during an internal tab drag should be harmless. Terminal should continue the tab reorder/tear-out gesture and preserve every tab, regardless of current output volume.

Actual behavior

The Terminal process exits immediately and without a user-facing error. Event Viewer records:

Faulting application: WindowsTerminal.exe 1.24.2607.10001
Faulting module:      Windows.UI.Xaml.dll 10.0.19041.6456
Exception code:       0xc000027b
Fault offset:         0x0000000000481782

Symbolized failure evidence

The important distinction from the representative stack in #20512 is the outer XAML drag state:

Windows_UI_Xaml!CDragDropState::RaiseDragOverOrDropEvents+0xa5
Windows_UI_Xaml!CDragDropState::RaiseDragEnterEvents+0x1f6
Windows_UI_Xaml!CDragDropState::RaiseDragLeaveEvents+0x24d
Windows_UI_Xaml!CDragDropState::RaiseEvents+0x46
Windows_UI_Xaml!ContentRootInput::DragDropProcessor::ProcessWinRtDragDrop+0x16e
Windows_UI_Xaml!DragDrop_RaiseEvent+0x118
Windows_UI_Xaml!DirectUI::DropOperationTarget::RaiseDragDropEventActionAsync+0x206
Windows_UI_Xaml!DirectUI::RaiseDragDropEventAsyncOperation::OnStart+0xa4
Windows_UI_Xaml!DirectUI::DropOperationTarget::RaiseDragDropEventOperationAsync+0x6a
Windows_UI_Xaml!DirectUI::DropOperationTarget::OverAsync+0x22
DataExchange!DropTargetInternal::DragOver+0x7b

That boundary transition raises Terminal's content DragOver handler. The nested portion of the same thread is:

Microsoft_Terminal_Control!
  winrt::Microsoft::Terminal::Control::implementation::
  TermControl::_DragOverHandler+0x3b
Microsoft_Terminal_Control!
  winrt::impl::consume_Windows_UI_Xaml_IDragEventArgs2<...>::DataView+0xaf
Windows_UI_Xaml!DirectUI::DragEventArgsGenerated::get_DataView+0x54
Windows_UI_Xaml!DirectUI::DragEventArgs::get_DataViewImpl+0x51
DataExchange!DropTargetInternal::DragInfo::get_Data+0x42
combase!ObjectStubless
combase!ObjectStublessClient
combase!CClientChannel::SendReceive
combase!ClassicSTAThreadSendReceive
combase!CSyncClientCall::SendReceive2
combase!ClassicSTAThreadWaitForCall
combase!CCliModalLoop::BlockFn
combase!CCliModalLoop::PeekRPCAndDDEMessage
combase!CCliModalLoop::MyPeekMessage
user32!PeekMessageW
win32u!NtUserPeekMessage
ntdll!KiUserCallbackDispatcherContinue
user32!DispatchClientMessage
CoreMessaging!Microsoft::CoreUI::Dispatch::UserAdapter_WindowProc
CoreMessaging!Microsoft::CoreUI::Dispatch::UserAdapter_DoWork
CoreMessaging!Microsoft::CoreUI::Dispatch::UserAdapter::OnUserDispatch
CoreMessaging!Microsoft::CoreUI::Dispatch::EventLoop::Callback_RunCoreLoop
CoreMessaging!Microsoft::CoreUI::Dispatch::TimeoutManager::Callback_OnDispatch
Windows_UI_Xaml!CXcpDispatcher::MessageTimerCallbackStatic
Windows_UI_Xaml!CDeferredInvoke::DispatchQueuedMessage
Windows_UI_Xaml!CXcpDispatcher::WindowProc
Windows_UI_Xaml!CXcpDispatcher::OnReentrancyProtectedWindowMessage
combase!RoFailFastWithErrorContextInternal2
KERNELBASE!RaiseFailFastException  // HRESULT 0x8000ffff, E_UNEXPECTED
ntdll!RtlRaiseNoncontinuableException

Exception record:

ExceptionAddress: Windows_UI_Xaml!
  CXcpDispatcher::OnReentrancyProtectedWindowMessage+0x1fdc5e
ExceptionCode: c000027b

Confirmed root cause and relationship to #20512

This is the same underlying reentrancy defect as #20512:

  1. The drag-enter/drag-leave transition routes DragOver to TermControl.
  2. TermControl::_DragOverHandler+0x3b calls e.DataView() to determine whether the payload contains files or text.
  3. DataView() enters DataExchange!DropTargetInternal::DragInfo::get_Data, which makes a synchronous cross-apartment COM request.
  4. COM pumps the STA message queue while waiting.
  5. The nested message pump dispatches pending XAML work through CDeferredInvoke::DispatchQueuedMessage.
  6. XAML re-enters CXcpDispatcher::OnReentrancyProtectedWindowMessage while the original drag callback is still active.
  7. XAML deliberately fail-fasts with E_UNEXPECTED; WER surfaces it as 0xc000027b at Windows.UI.Xaml.dll+0x481782.

The distinct UX manifestation is the trigger: this dump proves that merely crossing the content boundary, including drag-enter/drag-leave processing, is sufficient. The user does not have to hover for long or complete a drop.

High output appears to amplify the timing window by leaving more deferred XAML render/layout/scroll work available to the nested COM pump. It is not an out-of-memory or renderer failure; neither buffer nor AtlasEngine code appears in the crashing path.

Current main still evaluates e.DataView() up to four times in TermControl::_DragOverHandler. Internal tab drags already have application-owned state and private windowId/pid properties, and should not need to enter the terminal content's external file/text drop inspection path.

Proposal

Fix the shared implementation defect from #20512 while explicitly covering drag-boundary transitions:

  1. Track internal-tab-drag lifetime starting at TabDragStarting and reliably clear it on completion, cancellation, and drop-outside paths.
  2. Suppress terminal-content drop handling for internal tab drags before TermControl::_DragOverHandler calls e.DataView(); temporarily disabling AllowDrop on terminal controls during an internal drag is another possible implementation.
  3. Preserve tab-strip drag/drop handling so reordering, cross-window movement, and tear-out continue working.
  4. For external file/text drags, retrieve and cache DataView() only once, and avoid synchronous payload inspection in high-frequency DragOver where possible.

Throttling is not a complete mitigation because the first getter call can fail. A try/catch around e.DataView() cannot reliably contain a XAML RaiseFailFastException raised from the nested dispatcher.

Acceptance criteria

  • Add focused RED evidence that an internal tab crossing into/out of terminal content exercises the unsafe DataView() path during RaiseDragEnterEvents/RaiseDragLeaveEvents; after the fix, the same reproduction turns GREEN and does not enter that getter.
  • Repeatedly cross a dragged tab over the tab-strip/content boundary, without dropping it, while continuous output is rendering; Terminal must remain alive and produce no 0xc000027b / Windows.UI.Xaml.dll+0x481782 failure.
  • Internal tab drags must not invoke external file/text payload inspection in TermControl::_DragOverHandler.
  • Same-window reorder, cross-window tab movement, tear-out, drag cancellation, and drop-outside behavior must continue working.
  • External file and text drops into terminal content must continue working.
  • The fix must cover drag-enter and drag-leave transitions, not only steady-state DragOver or completed Drop.

Related issues

Dump availability and privacy

The 806 MB full user dump is available privately to maintainers. It should not be attached publicly because Terminal dumps can contain scrollback, commands, environment data, and other sensitive information.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Needs-Tag-FixDoesn't match tag requirementsNeeds-TriageIt's a new issue that the core contributor team needs to triage at the next triage meeting

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions