From 99e0f95318d9bbbe951bf96998cf7599ac5fdece Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Wed, 12 May 2021 01:03:08 +0200 Subject: [PATCH] Fix crash on exit introduced in ac265aa (#10042) ControlCore::AttachUiaEngine receives a IRenderEngine as a raw pointer, which TermControl owns. We must ensure that we first destroy the ControlCore before the UiaEngine instance (both owned by TermControl). Otherwise a deallocated IRenderEngine is accessed when ControlCore calls Renderer::TriggerTeardown. This crash was introduced in #10031. * [x] I work here * [x] Tests added/passed * Run accevent.exe to cause a UiaEngine to be attached to a TermControl. * Close the current tab * Ensured no crashes occur (cherry picked from commit 43040ef9d02e2fd667c6bb3049f41aa33a8d1e8f) (cherry picked from commit ad45139bb4aa17f71c6eb9ad283e13da65edea40) --- src/cascadia/TerminalControl/TermControl.cpp | 22 ++------------------ src/cascadia/TerminalControl/TermControl.h | 7 ++++++- 2 files changed, 8 insertions(+), 21 deletions(-) diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 19dbeae2c50..7378b71de54 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -641,27 +641,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation { Close(); - // Before destroying this instance we must ensure that we destroy the _renderer - // before the _renderEngine, as well as calling _renderer->TriggerTeardown(). - // _renderEngine will be destroyed naturally after this ~destructor() returns. - - decltype(_renderer) renderer; - { - // GH#8734: - // We lock the terminal here to make sure it isn't still being - // used in the connection thread before we destroy the renderer. - // However, we must unlock it again prior to triggering the - // teardown, to avoid the render thread being deadlocked. The - // renderer may be waiting to acquire the terminal lock, while - // we're waiting for the renderer to finish. - auto lock = _terminal->LockForWriting(); - - _renderer.swap(renderer); - } - - if (renderer) + if (_renderer) { - renderer->TriggerTeardown(); + _renderer->TriggerTeardown(); } } diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index f38d8592eeb..bd4d69b0498 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -130,9 +130,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation std::unique_ptr<::Microsoft::Terminal::Core::Terminal> _terminal; - std::unique_ptr<::Microsoft::Console::Render::Renderer> _renderer; + // NOTE: All render engines must be ordered before _renderer. + // + // As _renderer has a dependency on the render engine (through a raw pointer) + // we must ensure the _renderer is deallocated first. + // (C++ class members are destroyed in reverse order.) std::unique_ptr<::Microsoft::Console::Render::DxEngine> _renderEngine; std::unique_ptr<::Microsoft::Console::Render::UiaEngine> _uiaEngine; + std::unique_ptr<::Microsoft::Console::Render::Renderer> _renderer; IControlSettings _settings; bool _focused;