Skip to content

Audit FAIL_FASTs across the code #14298

Open
@DHowett

Description

Back in 2018 we switched every NTASSERT/ASSERT/NT_ASSERT in these components to a FAIL_FAST, unequivocally, because if we cared enough to check it, we should care enough to die for it.
I think that’s good and valuable, and it has its place.

Now that these components are shared with Terminal (who seeks to host multiple copies of each of them in parallel), and now that a11y has taken a bigger bet on them performing sanely and safely,
I’m wondering if we should scale back a little bit on the take down the entire process if something lifts off into space stance.

We’re seeing a good number of crashes due to accessibility that I think should just be “oh, the API returned an error code, NVDA can recover”. We once saw reports that resizing the window when an unexpected DBCS half lands in the buffer (#4907) caused a crash.

They’re programming errors, for sure, but our users probably want things to be more reliable.

Should, or could, we surface those as exceptions and let the caller recover wherever possible?
Should we just internally recover?

Let's find out.


Generated as of 2022-10-27 ab04067

How did I generate this?
rg -n FAIL_FAST_IF\`( --no-heading | % {
	$p=$_ -split ":";
	$pa=$p[0] -replace "\\","/";
	$ln=$p[1];
	[pscustomobject]@{File=$pa;Line=$ln}
} | group File | sort -Descending Count | % {
	"## $($_.Name)`n";
	$_.Group | % {
		"https://github.com/microsoft/terminal/blob/$(git rev-parse HEAD)/src/$($_.File)#L$($_.Line)"
	};
	"`n"
}

host/screenInfo.cpp

FAIL_FAST_IF(!(gci.IsConsoleLocked()));

FAIL_FAST_IF(coordFontSize.X == 0);
FAIL_FAST_IF(coordFontSize.Y == 0);

FAIL_FAST_IF(coordFont.X == 0);
FAIL_FAST_IF(coordFont.Y == 0);

FAIL_FAST_IF(coordFontSize.X == 0);
FAIL_FAST_IF(coordFontSize.Y == 0);

FAIL_FAST_IF(coordFontSize.X == 0);
FAIL_FAST_IF(coordFontSize.Y == 0);

FAIL_FAST_IF(coordFont.X == 0);
FAIL_FAST_IF(coordFont.Y == 0);

FAIL_FAST_IF(!(sEndX < coordScreenBufferSize.X));

FAIL_FAST_IF(!(_viewport.Top() >= 0));
// TODO MSFT: 17663344 - Audit call sites for this precondition. Extremely tiny offscreen windows.
//FAIL_FAST_IF(!(_viewport.IsValid()));

FAIL_FAST_IF(!(DeltaY <= 0));

host/cmdline.cpp

FAIL_FAST_IF(!(cookedReadData.BufferStartPtr() == cookedReadData.BufferCurrentPtr()));

FAIL_FAST_IF(!(cookedReadData.BufferStartPtr() == cookedReadData.BufferCurrentPtr()));

FAIL_FAST_IF(!(cookedReadData.BufferStartPtr() == cookedReadData.BufferCurrentPtr()));

FAIL_FAST_IF(!(cookedReadData.BufferStartPtr() == cookedReadData.BufferCurrentPtr()));

FAIL_FAST_IF(!(LastWord > cookedReadData.BufferStartPtr()));

FAIL_FAST_IF(!(LastWord > cookedReadData.BufferStartPtr()));

FAIL_FAST_IF(!(LastWord > cookedReadData.BufferStartPtr()));

FAIL_FAST_IF(!(LastWord >= cookedReadData.BufferStartPtr()));

FAIL_FAST_IF(!(NextWord < BufLast));

FAIL_FAST_IF(!(cookedReadData.BufferStartPtr() == cookedReadData.BufferCurrentPtr()));

host/_stream.cpp

FAIL_FAST_IF(!(coordCursor.Y == bufferSize.Y));

FAIL_FAST_IF(!(WI_IsFlagSet(screenInfo.OutputMode, ENABLE_PROCESSED_OUTPUT)));

FAIL_FAST_IF(!(WI_IsFlagSet(screenInfo.OutputMode, ENABLE_PROCESSED_OUTPUT)));

FAIL_FAST_IF(!(Tmp2 >= buffer.get()));

FAIL_FAST_IF(!(WI_IsFlagSet(screenInfo.OutputMode, ENABLE_PROCESSED_OUTPUT)));
FAIL_FAST_IF(!(WI_IsFlagSet(screenInfo.OutputMode, ENABLE_VIRTUAL_TERMINAL_PROCESSING)));
// defined down in the WriteBuffer default case hiding on the other end of the state machine. See outputStream.cpp
// This is the only mode used by DoWriteConsole.
FAIL_FAST_IF(!(WI_IsFlagSet(dwFlags, WC_LIMIT_BACKSPACE)));

FAIL_FAST_IF(wFromComplemented.size() != 1);

host/inputBuffer.cpp

FAIL_FAST_IF(streamRead && readCount != 1);

FAIL_FAST_IF(!(unusedWaitStatus));
// write all previously existing records
size_t existingEventsWritten;
_WriteBuffer(existingStorage, existingEventsWritten, unusedWaitStatus);
FAIL_FAST_IF(!(!unusedWaitStatus));

FAIL_FAST_IF(!(inEvents.size() == 1));
FAIL_FAST_IF(_storage.empty());

FAIL_FAST_IF(!(inEvents.size() == 1));

FAIL_FAST_IF(_storage.empty());

propsheet/fontdlg.cpp

FAIL_FAST_IF(!(OEMCP != 0)); // must be initialized

FAIL_FAST_IF(!(i == LB_ERR || (ULONG)i < NumberOfFonts));

FAIL_FAST_IF(!(OEMCP != 0));

FAIL_FAST_IF(!(FontIndex < (int)NumberOfFonts));

FAIL_FAST_IF(!((ULONG)nFont < NumberOfFonts));

FAIL_FAST_IF(!((ULONG)FontIndex < NumberOfFonts));

host/utils.cpp

terminal/src/host/utils.cpp

Lines 176 to 179 in ab04067

FAIL_FAST_IF(!(coordFirst.X >= 0 && coordFirst.X < cRowWidth));
FAIL_FAST_IF(!(coordSecond.X >= 0 && coordSecond.X < cRowWidth));
FAIL_FAST_IF(!(coordFirst.Y >= 0 && coordFirst.Y < cRowHeight));
FAIL_FAST_IF(!(coordSecond.Y >= 0 && coordSecond.Y < cRowHeight));

terminal/src/host/utils.cpp

Lines 231 to 232 in ab04067

FAIL_FAST_IF(!(coordCorner.X == srRectangle.Left || coordCorner.X == srRectangle.Right));
FAIL_FAST_IF(!(coordCorner.Y == srRectangle.Top || coordCorner.Y == srRectangle.Bottom));

interactivity/win32/icon.cpp

FAIL_FAST_IF(!(&hIconRef == &_hIcon || &hIconRef == &_hSmIcon));
// expecting hDefaultIconRef to be pointing to either the regular or small default handles
FAIL_FAST_IF(!(&hDefaultIconRef == &_hDefaultIcon || &hDefaultIconRef == &_hDefaultSmIcon));

FAIL_FAST_IF(!(&hIconRef == &_hDefaultIcon || &hIconRef == &_hDefaultSmIcon));

FAIL_FAST_IF(!(&hIconRef == &_hIcon || &hIconRef == &_hSmIcon));

FAIL_FAST_IF(!(&hIconRef == &_hIcon || &hIconRef == &_hSmIcon));

host/readDataCooked.cpp

FAIL_FAST_IF(!gci.IsConsoleLocked());

FAIL_FAST_IF(_pInputReadHandleData->IsInputPending());
// this routine should be called by a thread owning the same lock on the same console as we're reading from.
FAIL_FAST_IF(_pInputReadHandleData->GetReadCount() == 0);

FAIL_FAST_IF(!(Tmp < (_backupLimit + _bytesRead)));

host/settings.cpp

FAIL_FAST_IF(!(_dwWindowSize.X > 0));
FAIL_FAST_IF(!(_dwWindowSize.Y > 0));
FAIL_FAST_IF(!(_dwScreenBufferSize.X > 0));
FAIL_FAST_IF(!(_dwScreenBufferSize.Y > 0));

host/history.cpp

FAIL_FAST_IF(WI_IsFlagClear(historyList.Flags, CLE_ALLOCATED));

FAIL_FAST_IF(commands > SHORT_MAX);

FAIL_FAST_IF(WI_IsFlagClear(Flags, CLE_ALLOCATED));

FAIL_FAST_IF(!(WI_IsFlagSet(Flags, CLE_ALLOCATED)));

host/srvinit.cpp

FAIL_FAST_IF(!(sizeof(Cac->AppName) == sizeof(Data.ApplicationName)));
FAIL_FAST_IF(!(sizeof(Cac->Title) == sizeof(Data.Title)));
FAIL_FAST_IF(!(sizeof(Cac->CurDir) == sizeof(Data.CurrentDirectory)));

host/misc.cpp

FAIL_FAST_IF(!(IsDBCSLeadByteConsole(*pch, &gci.OutputCPInfo) || cch == 1));

FAIL_FAST_IF(!(pwchSource != (LPWSTR)pchTarget));

FAIL_FAST_IF(!(cchTarget > 0));

interactivity/win32/window.cpp

FAIL_FAST_IF(!(s_atomWindowClass == 0));

FAIL_FAST_IF(!((fInKeyboardMarkMode && !fInMouseSelectMode && !fInScrollMode) ||

FAIL_FAST_IF(!(rectSizeTemp.top == 0 && rectSizeTemp.left == 0));

host/readDataDirect.cpp

FAIL_FAST_IF(_pInputReadHandleData->GetReadCount() == 0);

FAIL_FAST_IF(!gci.IsConsoleLocked());

FAIL_FAST_IF(!(readEvents.empty()));

server/ProcessList.cpp

FAIL_FAST_IF(!(ServiceLocator::LocateGlobals().getConsoleInformation().IsConsoleLocked()));

FAIL_FAST_IF(!(ServiceLocator::LocateGlobals().getConsoleInformation().IsConsoleLocked()));

FAIL_FAST_IF(!(_processes.cend() != std::find(_processes.cbegin(), _processes.cend(), pProcessData)));

host/selectionInput.cpp

FAIL_FAST_IF(!IsInSelectingState());

FAIL_FAST_IF(!fMoveSucceeded); // we should never fail to move forward after having moved backward

FAIL_FAST_IF(!bufferSize.IsInBounds(coordSelPoint));

tsf/TfEditSession.cpp

FAIL_FAST_IF(!(cr >= 0));

FAIL_FAST_IF(!((cchCompleted > 0) && (cchCompleted < cch)));

FAIL_FAST_IF(!(lTextLength > 0));

server/ObjectHandle.cpp

FAIL_FAST_IF(!(_IsInput()));

FAIL_FAST_IF(pReadHandleData->GetReadCount() > 0);

FAIL_FAST_IF(!(_IsOutput()));

host/CommandListPopup.cpp

FAIL_FAST_IF(_currentCommand < 0);

FAIL_FAST_IF(!(Tmp < (cookedReadData.BufferStartPtr() + cookedReadData.BytesRead())));

interactivity/win32/windowio.cpp

FAIL_FAST_IF(!(gci.IsConsoleLocked()));

FAIL_FAST_IF(!(ProcessData != nullptr && ProcessData->fRootProcess));

host/conimeinfo.cpp

FAIL_FAST_IF(size <= 0); // It's a programming error to have <= 0 cells to insert.

FAIL_FAST_IF(text.size() != attributes.size());

host/input.cpp

FAIL_FAST_IF(EventsWritten != 1);

FAIL_FAST_IF(!(!((CtrlFlags & (CONSOLE_CTRL_CLOSE_FLAG | CONSOLE_CTRL_BREAK_FLAG | CONSOLE_CTRL_C_FLAG)) && (CtrlFlags & (CONSOLE_CTRL_LOGOFF_FLAG | CONSOLE_CTRL_SHUTDOWN_FLAG)))));

host/consoleInformation.cpp

FAIL_FAST_IF(rc == 0);

FAIL_FAST_IF(rc == ULONG_MAX);

host/readDataRaw.cpp

FAIL_FAST_IF(_pInputReadHandleData->GetReadCount() == 0);
FAIL_FAST_IF(!Microsoft::Console::Interactivity::ServiceLocator::LocateGlobals().getConsoleInformation().IsConsoleLocked());

tsf/TfDispAttr.cpp

FAIL_FAST_IF(!(var.vt == VT_UNKNOWN));

FAIL_FAST_IF(!(tfPropVal.varValue.vt == VT_I4)); // expecting GUIDATOMs

host/inputReadHandleData.cpp

FAIL_FAST_IF(prevCount == 0); // we just underflowed, that's a programming error.

host/directio.cpp

FAIL_FAST_IF(!(readEvents.empty()));

FAIL_FAST_IF(!(readEvents.empty()));

host/ConsoleArguments.cpp

FAIL_FAST_IF(!(CLIENT_COMMANDLINE_ARG == start->c_str()));

FAIL_FAST_IF(!args.empty());

propsheet/registry.cpp

FAIL_FAST_IF(!(OEMCP != 0));

FAIL_FAST_IF(!(OEMCP != 0));

server/WaitBlock.cpp

FAIL_FAST_IF(!(WI_IsFlagClear(TerminationReason, WaitTerminationReason::ThreadDying)));

host/writeData.cpp

FAIL_FAST_IF(!(Microsoft::Console::Interactivity::ServiceLocator::LocateGlobals().getConsoleInformation().IsConsoleLocked()));

propsheet/misc.cpp

FAIL_FAST_IF(!(ShouldAllowAllMonoTTFonts()));

interactivity/base/ServiceLocator.cpp

FAIL_FAST_IF(nullptr != s_oneCoreTeardownFunction);

host/stream.cpp

FAIL_FAST_IF(Wait);

host/dbcs.cpp

FAIL_FAST_IF(iDst != buffer.size());

server/IoSorter.cpp

FAIL_FAST_IF(!(!ReplyPending));

types/sgrStack.cpp

FAIL_FAST_IF(validParts.test(SgrSaveRestoreStackOptions::All));

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Area-CodeHealthIssues related to code cleanliness, linting, rules, warnings, errors, static analysis, etc.Needs-Tag-FixDoesn't match tag requirementsProduct-ConhostFor issues in the Console codebaseSeverity-CrashCrashes are real bad news.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions