Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Misc. elevation crash fixes #12205

Merged
11 commits merged into from
Jan 24, 2022
7 changes: 5 additions & 2 deletions src/cascadia/Remoting/WindowManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -252,8 +252,11 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation

if (_peasant)
{
// Inform the monarch of the time we were last activated
_monarch.HandleActivatePeasant(_peasant.GetLastActivatedArgs());
if (const auto& lastActivated{ _peasant.GetLastActivatedArgs() })
{
// Inform the monarch of the time we were last activated
_monarch.HandleActivatePeasant(lastActivated);
}
}

if (!_isKing)
Expand Down
18 changes: 18 additions & 0 deletions src/cascadia/TerminalApp/AppLogic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1472,6 +1472,24 @@ namespace winrt::TerminalApp::implementation
return _root != nullptr ? _root->ShouldUsePersistedLayout(_settings) : false;
}

bool AppLogic::ShouldImmediatelyHandoffToElevated()
{
if (!_loadedInitialSettings)
{
// Load settings if we haven't already
LoadSettings();
}

return _root != nullptr ? _root->ShouldImmediatelyHandoffToElevated(_settings) : false;
}
void AppLogic::HandoffToElevated()
{
if (_root)
{
_root->HandoffToElevated(_settings);
}
}

void AppLogic::SaveWindowLayoutJsons(const Windows::Foundation::Collections::IVector<hstring>& layouts)
{
std::vector<WindowLayout> converted;
Expand Down
2 changes: 2 additions & 0 deletions src/cascadia/TerminalApp/AppLogic.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ namespace winrt::TerminalApp::implementation
bool AlwaysOnTop() const;

bool ShouldUsePersistedLayout();
bool ShouldImmediatelyHandoffToElevated();
void HandoffToElevated();
hstring GetWindowLayoutJson(Microsoft::Terminal::Settings::Model::LaunchPosition position);
void SaveWindowLayoutJsons(const Windows::Foundation::Collections::IVector<hstring>& layouts);
void IdentifyWindow();
Expand Down
2 changes: 2 additions & 0 deletions src/cascadia/TerminalApp/AppLogic.idl
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ namespace TerminalApp
TaskbarState TaskbarState{ get; };

Boolean ShouldUsePersistedLayout();
Boolean ShouldImmediatelyHandoffToElevated();
void HandoffToElevated();
String GetWindowLayoutJson(Microsoft.Terminal.Settings.Model.LaunchPosition position);
void SaveWindowLayoutJsons(Windows.Foundation.Collections.IVector<String> layouts);

Expand Down
133 changes: 132 additions & 1 deletion src/cascadia/TerminalApp/TerminalPage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,114 @@ namespace winrt::TerminalApp::implementation
settings.GlobalSettings().FirstWindowPreference() == FirstWindowPreference::PersistedWindowLayout;
}

// Method Description:
// - This is a bit of trickiness: If we're running unelevated, and the user
// passed in only --elevate actions, the we don't _actually_ want to
// restore the layouts here. We're not _actually_ about to create the
// window. We're simply going to toss the commandlines
Comment on lines +306 to +309
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just so I get it: This returns true if we're not elevated but all relevant pane-spawning actions are elevated. Is that correct? Would such a comment be helpful for others in the future?

// Arguments:
// - <none>
// Return Value:
// - <none>
bool TerminalPage::ShouldImmediatelyHandoffToElevated(CascadiaSettings& settings) const
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't seem to need a mutable reference to settings. Make it a const ref?
Same for the one below.

{
if (!_startupActions || IsElevated())
{
// there arent startup actions, or we're elevated. In that case, go for it.
return false;
}

// Check that there's at least one action that's not just an elevated newTab action.
for (const auto& action : _startupActions)
{
NewTerminalArgs newTerminalArgs{ nullptr };

if (action.Action() == ShortcutAction::NewTab)
{
const auto& args{ action.Args().try_as<NewTabArgs>() };
if (args)
{
newTerminalArgs = args.TerminalArgs();
}
else
{
// This was a nt action that didn't have any args. The default
// profile may want to be elevated, so don't just early return.
}
}
else if (action.Action() == ShortcutAction::SplitPane)
{
const auto& args{ action.Args().try_as<SplitPaneArgs>() };
if (args)
{
newTerminalArgs = args.TerminalArgs();
}
else
{
// This was a nt action that didn't have any args. The default
// profile may want to be elevated, so don't just early return.
}
}
else
{
// This was not a new tab or split pane action.
// This doesn't affect the outcome
continue;
}

// It's possible that newTerminalArgs is null here.
// GetProfileForArgs should be resilient to that.
const auto profile{ settings.GetProfileForArgs(newTerminalArgs) };
if (profile.Elevate())
{
continue;
}

// The profile didn't want to be elevated, and we aren't elevated.
// We're going to open at least one tab, so return false.
return false;
}
return true;
}

// Method Description:
// - Escape hatch for immediately dispatching requests to elevated windows
// when first launched. At this point in startup, the window doesn't exist
// yet, XAML hasn't been started, but we need to dispatch these actions.
// We can't just go through ProcessStartupActions, because that processes
// the actions async using the XAML dispatcher (which doesn't exist yet)
// - DON'T CALL THIS if you haven't already checked
// ShouldImmediatelyHandoffToElevated. If you're thinking about calling
// this outside of the one place it's used, that's probably the wrong
// solution.
// Arguments:
// - settings: the settings we should use for dispatching these actions. At
// this point in startup, we hadn't otherwise been initialized with these,
// so use them now.
// Return Value:
// - <none>
void TerminalPage::HandoffToElevated(CascadiaSettings& settings)
{
if (!_startupActions)
{
return;
}

// Hookup our event handlers to the ShortcutActionDispatch
_settings = settings;
_HookupKeyBindings(_settings.ActionMap());
_RegisterActionCallbacks();

for (const auto& action : _startupActions)
{
// only process new tabs and split panes. They're all going to the elevated window anyways.
if (action.Action() == ShortcutAction::NewTab || action.Action() == ShortcutAction::SplitPane)
{
_actionDispatch->DoAction(action);
}
}
}

// Method Description;
// - Checks if the current window is configured to load a particular layout
// Arguments:
Expand Down Expand Up @@ -1638,7 +1746,30 @@ namespace winrt::TerminalApp::implementation
std::shared_ptr<Pane> newPane)
{
const auto focusedTab{ _GetFocusedTabImpl() };
_SplitPane(*focusedTab, splitDirection, splitSize, newPane);

// Clever hack for a crash in startup, with multiple sub-commands. Say
// you have the following commandline:
//
// wtd nt -p "elevated cmd" ; sp -p "elevated cmd" ; sp -p "Command Prompt"
//
// Where "elevated cmd" is an elevated profile.
//
// In that scenario, we won't dump off the commandline immediately to an
// elevated window, because it's got the final unelevated split in it.
// However, when we get to that command, there won't be a tab yet. So
// we'd crash right about here.
//
// Instead, let's just promote this first split to be a tab instead.
// Crash avoided, and we don't need to worry about inserting a new-tab
// command in at the start.
if (!focusedTab && _tabs.Size() == 0)
{
_CreateNewTabFromPane(newPane);
}
else
{
_SplitPane(*focusedTab, splitDirection, splitSize, newPane);
}
}

// Method Description:
Expand Down
2 changes: 2 additions & 0 deletions src/cascadia/TerminalApp/TerminalPage.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ namespace winrt::TerminalApp::implementation
void Create();

bool ShouldUsePersistedLayout(Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) const;
bool ShouldImmediatelyHandoffToElevated(Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) const;
void HandoffToElevated(Microsoft::Terminal::Settings::Model::CascadiaSettings& settings);
std::optional<uint32_t> LoadPersistedLayoutIdx(Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) const;
winrt::Microsoft::Terminal::Settings::Model::WindowLayout LoadPersistedLayout(Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) const;
Microsoft::Terminal::Settings::Model::WindowLayout GetWindowLayout();
Expand Down
18 changes: 17 additions & 1 deletion src/cascadia/WindowsTerminal/AppHost.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,20 @@ void AppHost::_HandleCommandlineArgs()
}
}

// This is a fix for GH#12190 and hopefully GH#12169.
//
// If the commandline we were provided is going to result in us only
// opening elevated terminal instances, then we need to not even create
// the window at all here. In that case, we're going through this
// special escape hatch to dispatch all the calls to elevate-shim, and
// then we're going to exit immediately.
if (_logic.ShouldImmediatelyHandoffToElevated())
{
_shouldCreateWindow = false;
_logic.HandoffToElevated();
return;
}

// After handling the initial args, hookup the callback for handling
// future commandline invocations. When our peasant is told to execute a
// commandline (in the future), it'll trigger this callback, that we'll
Expand All @@ -231,7 +245,9 @@ void AppHost::_HandleCommandlineArgs()
{
const auto numPeasants = _windowManager.GetNumberOfPeasants();
const auto layouts = ApplicationState::SharedInstance().PersistedWindowLayouts();
if (_logic.ShouldUsePersistedLayout() && layouts && layouts.Size() > 0)
if (_logic.ShouldUsePersistedLayout() &&
layouts &&
layouts.Size() > 0)
{
uint32_t startIdx = 0;
// We want to create a window for every saved layout.
Expand Down