Prevent deadlock in UIA Move API (#10937)

Fixes a bug where interacting with Windows Terminal when using Narrator causes Windows Terminal to hang.

`UiaTextRangeBase::Move()` locks, but later calls `UiaTextRangeBase::ExpandToEnclosingUnit()` which attempts to lock again. The workaround for this is to introduce a `_expandToEnclosingUnit()` that _does not_ lock the console. Then, `Move()` calls this new method, thus only allowing one lock to be established at a time.

This bug is observed to be in v1.11.2221.0 and _not_ in v1.9.1942.0.
This commit is contained in:
Carlos Zamora 2021-08-13 10:56:34 -07:00 committed by GitHub
parent 70560a789c
commit 0220f71883
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 80 additions and 68 deletions

View file

@ -262,6 +262,23 @@ IFACEMETHODIMP UiaTextRangeBase::ExpandToEnclosingUnit(_In_ TextUnit unit) noexc
try
{
_expandToEnclosingUnit(unit);
UiaTracing::TextRange::ExpandToEnclosingUnit(unit, *this);
return S_OK;
}
CATCH_RETURN();
}
// Method Description:
// - Moves _start and _end endpoints to encompass the enclosing text unit.
// (i.e. word --> enclosing word, line --> enclosing line)
// - IMPORTANT: this does _not_ lock the console
// Arguments:
// - attributeId - the UIA text attribute identifier we're expanding by
// Return Value:
// - <none>
void UiaTextRangeBase::_expandToEnclosingUnit(TextUnit unit)
{
const auto& buffer = _pData->GetTextBuffer();
const auto bufferSize = _getBufferSize();
const auto bufferEnd = bufferSize.EndExclusive();
@ -310,11 +327,6 @@ IFACEMETHODIMP UiaTextRangeBase::ExpandToEnclosingUnit(_In_ TextUnit unit) noexc
_start = bufferSize.Origin();
_end = bufferSize.EndExclusive();
}
UiaTracing::TextRange::ExpandToEnclosingUnit(unit, *this);
return S_OK;
}
CATCH_RETURN();
}
// Method Description:
@ -994,6 +1006,7 @@ std::wstring UiaTextRangeBase::_getTextValue(std::optional<unsigned int> maxLeng
IFACEMETHODIMP UiaTextRangeBase::Move(_In_ TextUnit unit,
_In_ int count,
_Out_ int* pRetVal) noexcept
try
{
RETURN_HR_IF(E_INVALIDARG, pRetVal == nullptr);
*pRetVal = 0;
@ -1011,8 +1024,6 @@ IFACEMETHODIMP UiaTextRangeBase::Move(_In_ TextUnit unit,
constexpr auto endpoint = TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start;
constexpr auto preventBufferEnd = true;
const auto wasDegenerate = IsDegenerate();
try
{
if (unit == TextUnit::TextUnit_Character)
{
_moveEndpointByUnitCharacter(count, endpoint, pRetVal, preventBufferEnd);
@ -1029,8 +1040,6 @@ IFACEMETHODIMP UiaTextRangeBase::Move(_In_ TextUnit unit,
{
_moveEndpointByUnitDocument(count, endpoint, pRetVal, preventBufferEnd);
}
}
CATCH_RETURN();
// If we actually moved...
if (*pRetVal != 0)
@ -1044,13 +1053,14 @@ IFACEMETHODIMP UiaTextRangeBase::Move(_In_ TextUnit unit,
else
{
// then just expand to get our _end
ExpandToEnclosingUnit(unit);
_expandToEnclosingUnit(unit);
}
}
UiaTracing::TextRange::Move(unit, count, *pRetVal, *this);
return S_OK;
}
CATCH_RETURN();
IFACEMETHODIMP UiaTextRangeBase::MoveEndpointByUnit(_In_ TextPatternRangeEndpoint endpoint,
_In_ TextUnit unit,

View file

@ -153,6 +153,8 @@ namespace Microsoft::Console::Types
void _getBoundingRect(const til::rectangle textRect, _Inout_ std::vector<double>& coords) const;
void _expandToEnclosingUnit(TextUnit unit);
void
_moveEndpointByUnitCharacter(_In_ const int moveCount,
_In_ const TextPatternRangeEndpoint endpoint,