Repository: microsoft/terminal (language: cpp)

Issue:
Using double-size characters can cause the Atlas engine to crash
### Windows Terminal version

Commit 3e6abd37df192d969088188ce836edbf645b13b4

### Windows build number

10.0.19044.1889

### Other Software

[Vttest](https://www.invisible-island.net/vttest/)

### Steps to reproduce

1. Make sure the Atlas renderer is enabled.
2. Open a WSL shell.
3. Run _Vttest_, and select option 4, the "Test of double-size characters".
4. While the first test page is visible, open the terminal settings.
5. Without changing anything click _Save_ and then close the settings.


### Expected Behavior

No crash.

### Actual Behavior

The app crashes with:

```
Unhandled exception thrown: write access violation.
it->_Ptr->_Next->**_Prev** was 0x7FFE52FDE460.
```

and the stack trace:

```
>	Microsoft.Terminal.Control.dll!Microsoft::Console::Render::AtlasEngine::StartPaint() Line 280	C++
 	Microsoft.Terminal.Control.dll!Microsoft::Console::Render::Renderer::_PaintFrameForEngine(Microsoft::Console::Render::IRenderEngine * const pEngine) Line 121	C++
 	Microsoft.Terminal.Control.dll!Microsoft::Console::Render::RenderThread::_ThreadProc() Line 214	C++
 	[External Code]	
```

Sometimes the crash occurs elsewhere, but it always seems to be something with the `_Prev` pointer being null or pointing somewhere inaccessible.


Discussion / hints:
I can sometimes reproduce this with a simple `printf "\e#3Test\n"`, but that may require opening and closing the settings more than once. I'm not exactly sure what is triggering it, so maybe there is an aspect of randomness involved.

As a wild guess, could it be that the Atlas engine can't cope with the fact that double-width lines only contain half the number of buffer cells as a full width line?
> As a wild guess, could it be that the Atlas engine can't cope with the fact that double-width lines only contain half the number of buffer cells as a full width line?

Yeah that sounds quite likely unfortunately.

Fix patch:
diff --git a/src/renderer/atlas/AtlasEngine.cpp b/src/renderer/atlas/AtlasEngine.cpp
index 983b4cd082b..9d0cc9f4e97 100644
--- a/src/renderer/atlas/AtlasEngine.cpp
+++ b/src/renderer/atlas/AtlasEngine.cpp
@@ -935,6 +935,21 @@ void AtlasEngine::_recreateSizeDependentResources()
         _api.glyphProps = Buffer<DWRITE_SHAPING_GLYPH_PROPERTIES>{ projectedGlyphSize };
         _api.glyphAdvances = Buffer<f32>{ projectedGlyphSize };
         _api.glyphOffsets = Buffer<DWRITE_GLYPH_OFFSET>{ projectedGlyphSize };
+
+        // Initialize cellGlyphMapping with valid data (whitespace), so that it can be
+        // safely used by the TileHashMap refresh logic via makeNewest() in StartPaint().
+        {
+            u16x2* coords{};
+            AtlasKey key{ { .cellCount = 1 }, 1, L" " };
+            AtlasValue value{ CellFlags::None, 1, &coords };
+
+            coords[0] = _r.tileAllocator.allocate(_r.glyphs);
+
+            const auto it = _r.glyphs.insert(std::move(key), std::move(value));
+            _r.glyphQueue.emplace_back(it);
+
+            std::fill(_r.cellGlyphMapping.begin(), _r.cellGlyphMapping.end(), it);
+        }
     }
 
     if (!_r.d2dMode)
@@ -1176,6 +1191,15 @@ void AtlasEngine::_flushBufferLine()
     // This would seriously blow us up otherwise.
     Expects(_api.bufferLineColumn.size() == _api.bufferLine.size() + 1);
 
+    // GH#13962: With the lack of proper LineRendition support, just fill
+    // the remaining columns with whitespace to prevent any weird artifacts.
+    for (auto lastColumn = _api.bufferLineColumn.back(); lastColumn < _api.cellCount.x;)
+    {
+        ++lastColumn;
+        _api.bufferLine.emplace_back(L' ');
+        _api.bufferLineColumn.emplace_back(lastColumn);
+    }
+
     // NOTE:
     // This entire function is one huge hack to see if it works.
 


Test patch: