Skip to content

Commit 5b8e731

Browse files
authored
AtlasEngine: Fix a OOB read when rendering PUA chars (microsoft#16894)
When no soft fonts are set up but we're asked to draw a U+EF20, etc., character we'll currently read out-of-bounds, because we don't check whether the soft fonts array is non-empty. This PR fixes the issue by first getting a slice of the data and then checking if it's ok to use. This changeset additionally fixes a couple constinit vs. constexpr cases. I changed them to constinit at some point because I thought that it's more constexpr than constexpr by guaranteeing initialization at compile time. But nope, constinit is actually weaker in a way, because while it does guarantee that, it doesn't actually make the data constant. In other words, constinit is not `.rdata`.
1 parent b9a0cae commit 5b8e731

File tree

10 files changed

+83
-64
lines changed

10 files changed

+83
-64
lines changed

src/cascadia/TerminalSettingsModel/Command.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55
#include "Command.h"
66
#include "Command.g.cpp"
77

8-
#include "ActionAndArgs.h"
9-
#include "KeyChordSerialization.h"
108
#include <LibraryResources.h>
11-
#include "TerminalSettingsSerializationHelpers.h"
9+
#include <til/replace.h>
10+
11+
#include "KeyChordSerialization.h"
1212

1313
using namespace winrt::Microsoft::Terminal::Settings::Model;
1414
using namespace winrt::Windows::Foundation::Collections;
@@ -23,7 +23,6 @@ namespace winrt
2323
static constexpr std::string_view NameKey{ "name" };
2424
static constexpr std::string_view IconKey{ "icon" };
2525
static constexpr std::string_view ActionKey{ "command" };
26-
static constexpr std::string_view ArgsKey{ "args" };
2726
static constexpr std::string_view IterateOnKey{ "iterateOn" };
2827
static constexpr std::string_view CommandsKey{ "commands" };
2928
static constexpr std::string_view KeysKey{ "keys" };

src/cascadia/TerminalSettingsModel/KeyChordSerialization.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ static KeyChord _fromString(std::wstring_view wstr)
129129
}
130130

131131
using nameToVkeyPair = std::pair<std::wstring_view, int32_t>;
132-
static constinit til::static_map nameToVkey{
132+
static constexpr til::static_map nameToVkey{
133133
// The above VKEY_NAME_PAIRS macro contains a list of key-binding names for each virtual key.
134134
// This god-awful macro inverts VKEY_NAME_PAIRS and creates a static map of key-binding names to virtual keys.
135135
// clang-format off
@@ -245,7 +245,7 @@ static KeyChord _fromString(std::wstring_view wstr)
245245
static std::wstring _toString(const KeyChord& chord)
246246
{
247247
using vkeyToNamePair = std::pair<int32_t, std::wstring_view>;
248-
static constinit til::static_map vkeyToName{
248+
static constexpr til::static_map vkeyToName{
249249
// The above VKEY_NAME_PAIRS macro contains a list of key-binding strings for each virtual key.
250250
// This macro picks the first (most preferred) name and creates a static map of virtual keys to key-binding names.
251251
#define GENERATOR(vkey, name1, ...) vkeyToNamePair{ vkey, name1 },

src/inc/til.h

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020
#include "til/color.h"
2121
#include "til/enumset.h"
2222
#include "til/pmr.h"
23-
#include "til/replace.h"
2423
#include "til/string.h"
24+
#include "til/type_traits.h"
2525
#include "til/u8u16convert.h"
2626

2727
// Use keywords on TraceLogging providers to specify the category
@@ -54,6 +54,24 @@
5454

5555
namespace til // Terminal Implementation Library. Also: "Today I Learned"
5656
{
57+
template<typename T>
58+
as_view_t<T> clamp_slice_abs(const T& view, size_t beg, size_t end)
59+
{
60+
const auto len = view.size();
61+
end = std::min(end, len);
62+
beg = std::min(beg, end);
63+
return { view.data() + beg, end - beg };
64+
}
65+
66+
template<typename T>
67+
as_view_t<T> clamp_slice_len(const T& view, size_t start, size_t count)
68+
{
69+
const auto len = view.size();
70+
start = std::min(start, len);
71+
count = std::min(count, len - start);
72+
return { view.data() + start, count };
73+
}
74+
5775
template<typename T>
5876
void manage_vector(std::vector<T>& vector, typename std::vector<T>::size_type requestedSize, float shrinkThreshold)
5977
{

src/inc/til/bytes.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33

44
#pragma once
55

6-
#include "type_traits.h"
7-
86
namespace til
97
{
108
template<ContiguousBytes Target>

src/inc/til/replace.h

Lines changed: 4 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,6 @@
55

66
namespace til
77
{
8-
namespace details
9-
{
10-
template<typename T>
11-
struct view_type_oracle
12-
{
13-
};
14-
15-
template<>
16-
struct view_type_oracle<std::string>
17-
{
18-
using type = std::string_view;
19-
};
20-
21-
template<>
22-
struct view_type_oracle<std::wstring>
23-
{
24-
using type = std::wstring_view;
25-
};
26-
27-
}
28-
298
// Method Description:
309
// - This is a function for finding all occurrences of a given string
3110
// `needle` in a larger string `haystack`, and replacing them with the
@@ -39,8 +18,8 @@ namespace til
3918
// - <none>
4019
template<typename T>
4120
void replace_needle_in_haystack_inplace(T& haystack,
42-
const typename details::view_type_oracle<T>::type& needle,
43-
const typename details::view_type_oracle<T>::type& replacement)
21+
const as_view_t<T>& needle,
22+
const as_view_t<T>& replacement)
4423
{
4524
auto pos{ T::npos };
4625
while ((pos = haystack.rfind(needle, pos)) != T::npos)
@@ -63,8 +42,8 @@ namespace til
6342
// - a copy of `haystack` with all instances of `needle` replaced with `replacement`.`
6443
template<typename T>
6544
T replace_needle_in_haystack(const T& haystack,
66-
const typename details::view_type_oracle<T>::type& needle,
67-
const typename details::view_type_oracle<T>::type& replacement)
45+
const as_view_t<T>& needle,
46+
const as_view_t<T>& replacement)
6847
{
6948
std::basic_string<typename T::value_type> result{ haystack };
7049
replace_needle_in_haystack_inplace(result, needle, replacement);

src/inc/til/type_traits.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,24 @@ namespace til
3232
struct is_byte<std::byte> : std::true_type
3333
{
3434
};
35+
36+
template<typename T>
37+
struct as_view
38+
{
39+
using type = T;
40+
};
41+
42+
template<typename T, typename Alloc>
43+
struct as_view<std::vector<T, Alloc>>
44+
{
45+
using type = std::span<const T>;
46+
};
47+
48+
template<typename T, typename Traits, typename Alloc>
49+
struct as_view<std::basic_string<T, Traits, Alloc>>
50+
{
51+
using type = std::basic_string_view<T, Traits>;
52+
};
3553
}
3654

3755
template<typename T>
@@ -45,4 +63,7 @@ namespace til
4563

4664
template<typename T>
4765
concept TriviallyCopyable = std::is_trivially_copyable_v<T>;
66+
67+
template<typename T>
68+
using as_view_t = typename details::as_view<T>::type;
4869
}

src/renderer/atlas/BackendD3D.cpp

Lines changed: 29 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1430,19 +1430,19 @@ BackendD3D::AtlasGlyphEntry* BackendD3D::_drawBuiltinGlyph(const RenderingPayloa
14301430
_d2dBeginDrawing();
14311431

14321432
auto shadingType = ShadingType::TextGrayscale;
1433+
const D2D1_RECT_F r{
1434+
static_cast<f32>(rect.x),
1435+
static_cast<f32>(rect.y),
1436+
static_cast<f32>(rect.x + rect.w),
1437+
static_cast<f32>(rect.y + rect.h),
1438+
};
14331439

14341440
if (BuiltinGlyphs::IsSoftFontChar(glyphIndex))
14351441
{
1436-
_drawSoftFontGlyph(p, rect, glyphIndex);
1442+
_drawSoftFontGlyph(p, r, glyphIndex);
14371443
}
14381444
else
14391445
{
1440-
const D2D1_RECT_F r{
1441-
static_cast<f32>(rect.x),
1442-
static_cast<f32>(rect.y),
1443-
static_cast<f32>(rect.x + rect.w),
1444-
static_cast<f32>(rect.y + rect.h),
1445-
};
14461446
BuiltinGlyphs::DrawBuiltinGlyph(p.d2dFactory.get(), _d2dRenderTarget.get(), _brush.get(), r, glyphIndex);
14471447
shadingType = ShadingType::TextBuiltinGlyph;
14481448
}
@@ -1465,15 +1465,31 @@ BackendD3D::AtlasGlyphEntry* BackendD3D::_drawBuiltinGlyph(const RenderingPayloa
14651465
return glyphEntry;
14661466
}
14671467

1468-
void BackendD3D::_drawSoftFontGlyph(const RenderingPayload& p, const stbrp_rect& rect, u32 glyphIndex)
1468+
void BackendD3D::_drawSoftFontGlyph(const RenderingPayload& p, const D2D1_RECT_F& rect, u32 glyphIndex)
14691469
{
1470+
_d2dRenderTarget->PushAxisAlignedClip(&rect, D2D1_ANTIALIAS_MODE_ALIASED);
1471+
const auto restoreD2D = wil::scope_exit([&]() {
1472+
_d2dRenderTarget->PopAxisAlignedClip();
1473+
});
1474+
1475+
const auto width = static_cast<size_t>(p.s->font->softFontCellSize.width);
1476+
const auto height = static_cast<size_t>(p.s->font->softFontCellSize.height);
1477+
const auto softFontIndex = glyphIndex - 0xEF20u;
1478+
const auto data = til::clamp_slice_len(p.s->font->softFontPattern, height * softFontIndex, height);
1479+
1480+
if (data.empty() || data.size() != height)
1481+
{
1482+
_d2dRenderTarget->Clear();
1483+
return;
1484+
}
1485+
14701486
if (!_softFontBitmap)
14711487
{
14721488
// Allocating such a tiny texture is very wasteful (min. texture size on GPUs
1473-
// right now is 64kB), but this is a seldomly used feature so it's fine...
1489+
// right now is 64kB), but this is a seldom used feature, so it's fine...
14741490
const D2D1_SIZE_U size{
1475-
static_cast<UINT32>(p.s->font->softFontCellSize.width),
1476-
static_cast<UINT32>(p.s->font->softFontCellSize.height),
1491+
static_cast<UINT32>(width),
1492+
static_cast<UINT32>(height),
14771493
};
14781494
const D2D1_BITMAP_PROPERTIES1 bitmapProperties{
14791495
.pixelFormat = { DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED },
@@ -1484,17 +1500,11 @@ void BackendD3D::_drawSoftFontGlyph(const RenderingPayload& p, const stbrp_rect&
14841500
}
14851501

14861502
{
1487-
const auto width = static_cast<size_t>(p.s->font->softFontCellSize.width);
1488-
const auto height = static_cast<size_t>(p.s->font->softFontCellSize.height);
1489-
14901503
auto bitmapData = Buffer<u32>{ width * height };
1491-
const auto softFontIndex = glyphIndex - 0xEF20u;
1492-
auto src = p.s->font->softFontPattern.begin() + height * softFontIndex;
14931504
auto dst = bitmapData.begin();
14941505

1495-
for (size_t y = 0; y < height; y++)
1506+
for (auto srcBits : data)
14961507
{
1497-
auto srcBits = *src++;
14981508
for (size_t x = 0; x < width; x++)
14991509
{
15001510
const auto srcBitIsSet = (srcBits & 0x8000) != 0;
@@ -1508,15 +1518,7 @@ void BackendD3D::_drawSoftFontGlyph(const RenderingPayload& p, const stbrp_rect&
15081518
}
15091519

15101520
const auto interpolation = p.s->font->antialiasingMode == AntialiasingMode::Aliased ? D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR : D2D1_INTERPOLATION_MODE_HIGH_QUALITY_CUBIC;
1511-
const D2D1_RECT_F dest{
1512-
static_cast<f32>(rect.x),
1513-
static_cast<f32>(rect.y),
1514-
static_cast<f32>(rect.x + rect.w),
1515-
static_cast<f32>(rect.y + rect.h),
1516-
};
1517-
1518-
_d2dBeginDrawing();
1519-
_d2dRenderTarget->DrawBitmap(_softFontBitmap.get(), &dest, 1, interpolation, nullptr, nullptr);
1521+
_d2dRenderTarget->DrawBitmap(_softFontBitmap.get(), &rect, 1, interpolation, nullptr, nullptr);
15201522
}
15211523

15221524
void BackendD3D::_drawGlyphAtlasAllocate(const RenderingPayload& p, stbrp_rect& rect)

src/renderer/atlas/BackendD3D.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ namespace Microsoft::Console::Render::Atlas
215215
ATLAS_ATTR_COLD void _drawTextOverlapSplit(const RenderingPayload& p, u16 y);
216216
[[nodiscard]] ATLAS_ATTR_COLD AtlasGlyphEntry* _drawGlyph(const RenderingPayload& p, const ShapedRow& row, AtlasFontFaceEntry& fontFaceEntry, u32 glyphIndex);
217217
AtlasGlyphEntry* _drawBuiltinGlyph(const RenderingPayload& p, const ShapedRow& row, AtlasFontFaceEntry& fontFaceEntry, u32 glyphIndex);
218-
void _drawSoftFontGlyph(const RenderingPayload& p, const stbrp_rect& rect, u32 glyphIndex);
218+
void _drawSoftFontGlyph(const RenderingPayload& p, const D2D1_RECT_F& rect, u32 glyphIndex);
219219
void _drawGlyphAtlasAllocate(const RenderingPayload& p, stbrp_rect& rect);
220220
static AtlasGlyphEntry* _drawGlyphAllocateEntry(const ShapedRow& row, AtlasFontFaceEntry& fontFaceEntry, u32 glyphIndex);
221221
static void _splitDoubleHeightGlyph(const RenderingPayload& p, const ShapedRow& row, AtlasFontFaceEntry& fontFaceEntry, AtlasGlyphEntry* glyphEntry);

src/til/ut_til/ReplaceTests.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
#include "precomp.h"
55
#include "WexTestClass.h"
66

7+
#include <til/replace.h>
8+
79
using namespace WEX::Common;
810
using namespace WEX::Logging;
911
using namespace WEX::TestExecution;

src/types/colorTable.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ static constexpr std::array<til::color, 256> standard256ColorTable{
267267
til::color{ 0xEE, 0xEE, 0xEE },
268268
};
269269

270-
static constinit til::presorted_static_map xorgAppVariantColorTable{
270+
static constexpr til::presorted_static_map xorgAppVariantColorTable{
271271
std::pair{ "antiquewhite"sv, std::array<til::color, 5>{ til::color{ 250, 235, 215 }, til::color{ 255, 239, 219 }, til::color{ 238, 223, 204 }, til::color{ 205, 192, 176 }, til::color{ 139, 131, 120 } } },
272272
std::pair{ "aquamarine"sv, std::array<til::color, 5>{ til::color{ 127, 255, 212 }, til::color{ 127, 255, 212 }, til::color{ 118, 238, 198 }, til::color{ 102, 205, 170 }, til::color{ 69, 139, 116 } } },
273273
std::pair{ "azure"sv, std::array<til::color, 5>{ til::color{ 240, 255, 255 }, til::color{ 240, 255, 255 }, til::color{ 224, 238, 238 }, til::color{ 193, 205, 205 }, til::color{ 131, 139, 139 } } },
@@ -348,7 +348,7 @@ static constinit til::presorted_static_map xorgAppVariantColorTable{
348348
std::pair{ "yellow"sv, std::array<til::color, 5>{ til::color{ 255, 255, 0 }, til::color{ 255, 255, 0 }, til::color{ 238, 238, 0 }, til::color{ 205, 205, 0 }, til::color{ 139, 139, 0 } } },
349349
};
350350

351-
static constinit til::presorted_static_map xorgAppColorTable{
351+
static constexpr til::presorted_static_map xorgAppColorTable{
352352
std::pair{ "aliceblue"sv, til::color{ 240, 248, 255 } },
353353
std::pair{ "aqua"sv, til::color{ 0, 255, 255 } },
354354
std::pair{ "beige"sv, til::color{ 245, 245, 220 } },

0 commit comments

Comments
 (0)