microsoft / microsoft/terminal

Rare hang / infinite loop writing line containing emoji unicode characters

Open
#18,897 5 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

Area-Output Issue-Bug Priority-1 Product-Conhost Product-Terminal
Dominant language
C++
Stars
105k
Forks
9.6k
Avg merge
3d 17h
Merged PRs (30d)
29

Description

Windows Terminal version

1.22.11141.0

Windows build number

10.0.26100.3775

Other Software

Output coming from a node.js script

Steps to reproduce

Unfortunately I cannot reproduce this consistently. I had noticed this hanging on occasion, and would have to close the window, and OpenConsole.exe would remain spinning a core until I terminated the process. Eventually I decided to grab a full memory dump file next time it occurred, though I have not been able to reproduce again. I do still have the full memory dump available.

The underlying node.js script is simply logging operations to the console.

In this case, it is writing the text: "countByTag 🎵 Oblivion - Grimes [1]: 0.215ms\r\n"

Expected Behavior

Text should output without hanging the console.

Actual Behavior

The console hangs and becomes unresponsive. A simple analysis of the memory dump follows:

OpenConsole!WriteCharsLegacy is writing the text "countByTag 🎵 Oblivion - Grimes [1]: 0.215ms\r\n"

this calls _writeCharsLegacyUnprocessed which appears to get stuck in the while loop while (!state.text.empty())

Presumably the first textBuffer.Replace call in this loop succeeds, because by the time there is a hang the state has advanced to the first emoji character in the string; that is, state.text is now pointing to the first emoji / unicode character in the text: "🎵 Oblivion - Grimes [1]: 0.215ms\r\n"

Note that the emoji is made of two unicode chars, which I have a hunch is the underlying issue.

state.columnBegin is 126, .columnLimit is 127, columnEnd is 127, columnBeginDirty is 126, columnEndDirty is 127 (all decimal numbers here). The TextBuffer.width is also 127

I grabbed two dumps a few seconds apart and both are in pretty much the same area, one was in ROW::WriteHelper::Finish and one was ROW::ReplaceText, but notably there does not seem to be any change/progress in the state variable.

I think somehow textBuffer.Replace is not advancing the text for some reason due to an edge case here, which causes it to never be empty, and therefore spin forever.

I can provide a dump file securely if desired.

stack trace:

 # Child-SP          RetAddr               Call Site
00 (Inline Function) --------`--------     OpenConsole!ROW::WriteHelper::Finish(void)+0x3c [C:\__w\1\s\src\buffer\out\Row.cpp @ 860] 
01 0000003f`b86ff0a0 00007ff7`e129e447     OpenConsole!ROW::ReplaceText(
			struct RowWriteState * state = 0x00000000`00000000)+0x188 [C:\__w\1\s\src\buffer\out\Row.cpp @ 620] 
02 0000003f`b86ff160 00007ff7`e1294e0a     OpenConsole!TextBuffer::Replace(
			int row = 0n26, 
			class TextAttribute * attributes = 0x000001de`0311adec, 
			struct RowWriteState * state = 0x0000003f`b86ff1e8)+0x37 [C:\__w\1\s\src\buffer\out\textBuffer.cpp @ 531] 
03 0000003f`b86ff1b0 00007ff7`e1294ffa     OpenConsole!_writeCharsLegacyUnprocessed(
			class SCREEN_INFORMATION * screenInfo = 0x000001de`03110430, 
			class std::basic_string_view<wchar_t,std::char_traits<wchar_t> > * text = <Value unavailable error>, 
			<Unimplemented error> psScrollY = <Unimplemented error>)+0x9a [C:\__w\1\s\src\host\_stream.cpp @ 133] 
04 0000003f`b86ff250 00007ff7`e12957f6     OpenConsole!WriteCharsLegacy(
			class SCREEN_INFORMATION * screenInfo = 0x000001de`03110430, 
			class std::basic_string_view<wchar_t,std::char_traits<wchar_t> > * text = 0x0000003f`b86ff378 "countByTag 🎵 Oblivion - Grimes [1]: 0.215ms
", 
			<Unimplemented error> psScrollY = <Unimplemented error>)+0x14a [C:\__w\1\s\src\host\_stream.cpp @ 195] 
05 0000003f`b86ff350 00007ff7`e129588c     OpenConsole!DoWriteConsole(
			wchar_t * pwchBuffer = 0x000001de`03166510 "countByTag ???", 
			unsigned int64 * pcbBuffer = 0x0000003f`b86ff3f0, 
			class SCREEN_INFORMATION * screenInfo = 0x000001de`03110430, 
			class std::unique_ptr<WriteData,std::default_delete<WriteData> > * waiter = 0x0000003f`b86ff460 empty)+0xe6 [C:\__w\1\s\src\host\_stream.cpp @ 436] 
06 0000003f`b86ff3d0 00007ff7`e129618a     OpenConsole!WriteConsoleWImplHelper(
			class SCREEN_INFORMATION * context = 0x000001de`03110430, 
			class std::basic_string_view<wchar_t,std::char_traits<wchar_t> > * buffer = 0x0000003f`b86ff450 "countByTag 🎵 Oblivion - Grimes [1]: 0.215ms
", 
			unsigned int64 * read = 0x0000003f`b86ff510, 
			class std::unique_ptr<WriteData,std::default_delete<WriteData> > * waiter = 0x0000003f`b86ff460 empty)+0x5c [C:\__w\1\s\src\host\_stream.cpp @ 478] 
07 0000003f`b86ff430 00007ff7`e12d6b89     OpenConsole!ApiRoutines::WriteConsoleWImpl(
			class SCREEN_INFORMATION * context = <Value unavailable error>, 
			class std::basic_string_view<wchar_t,std::char_traits<wchar_t> > * buffer = 0x0000003f`b86ff500 "countByTag 🎵 Oblivion - Grimes [1]: 0.215ms
", 
			unsigned int64 * read = 0x0000003f`b86ff510, 
			class std::unique_ptr<IWaitRoutine,std::default_delete<IWaitRoutine> > * waiter = 0x0000003f`b86ff4f0 empty)+0x8a [C:\__w\1\s\src\host\_stream.cpp @ 705] 
08 0000003f`b86ff4a0 00007ff7`e12de4cd     OpenConsole!ApiDispatchers::ServerWriteConsole(
			struct _CONSOLE_API_MSG * m = 0x0000003f`b86ff670, 
			int * pbReplyPending = 0x0000003f`b86ff5b8)+0x229 [C:\__w\1\s\src\server\ApiDispatchers.cpp @ 392] 
09 0000003f`b86ff570 00007ff7`e12d4607     OpenConsole!ApiSorter::ConsoleDispatchRequest(
			struct _CONSOLE_API_MSG * Message = 0x0000003f`b86ff670)+0xbd [C:\__w\1\s\src\server\ApiSorter.cpp @ 174] 
0a (Inline Function) --------`--------     OpenConsole!IoDispatchers::ConsoleDispatchRequest(void)+0x8 [C:\__w\1\s\src\server\IoDispatchers.cpp @ 582] 
0b 0000003f`b86ff5e0 00007ff7`e126f136     OpenConsole!IoSorter::ServiceIoOperation(
			struct _CONSOLE_API_MSG * pMsg = <Value unavailable error>, 
			struct _CONSOLE_API_MSG ** ReplyMsg = <Value unavailable error>)+0x67 [C:\__w\1\s\src\server\IoSorter.cpp @ 100] 
0c 0000003f`b86ff630 00007ff9`1aede8d7     OpenConsole!ConsoleIoThread(
			void * lpParameter = <Value unavailable error>)+0x1f6 [C:\__w\1\s\src\host\srvinit.cpp @ 990] 
0d 0000003f`b86ff8b0 00007ff9`1c2b14fc     kernel32!BaseThreadInitThunk+0x17
0e 0000003f`b86ff8e0 00000000`00000000     ntdll!RtlUserThreadStart+0x2c

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with _stream.cpp in _writeCharsLegacyUnprocessed, then trace TextBuffer::Replace and ROW::ReplaceText in Row.cpp, especially the reported while loop and boundary state. Use the supplied stack trace and memory-dump details to investigate the emoji case. Done means console writes containing the reported emoji no longer spin indefinitely, with coverage for the boundary condition if an existing test location is identified.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
cli, operating-systems
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.