Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AtlasEngine: Fix a OOB read when rendering PUA chars #16894

Merged
merged 3 commits into from
Mar 21, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions src/cascadia/TerminalSettingsModel/Command.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,8 @@
#include "Command.h"
#include "Command.g.cpp"

#include "ActionAndArgs.h"
#include "KeyChordSerialization.h"
#include <LibraryResources.h>
#include "TerminalSettingsSerializationHelpers.h"
#include <til/replace.h>

using namespace winrt::Microsoft::Terminal::Settings::Model;
using namespace winrt::Windows::Foundation::Collections;
Expand All @@ -23,7 +21,6 @@ namespace winrt
static constexpr std::string_view NameKey{ "name" };
static constexpr std::string_view IconKey{ "icon" };
static constexpr std::string_view ActionKey{ "command" };
static constexpr std::string_view ArgsKey{ "args" };
static constexpr std::string_view IterateOnKey{ "iterateOn" };
static constexpr std::string_view CommandsKey{ "commands" };
static constexpr std::string_view KeysKey{ "keys" };
Expand Down
4 changes: 2 additions & 2 deletions src/cascadia/TerminalSettingsModel/KeyChordSerialization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ static KeyChord _fromString(std::wstring_view wstr)
}

using nameToVkeyPair = std::pair<std::wstring_view, int32_t>;
static constinit til::static_map nameToVkey{
static constexpr til::static_map nameToVkey{
// The above VKEY_NAME_PAIRS macro contains a list of key-binding names for each virtual key.
// This god-awful macro inverts VKEY_NAME_PAIRS and creates a static map of key-binding names to virtual keys.
// clang-format off
Expand Down Expand Up @@ -245,7 +245,7 @@ static KeyChord _fromString(std::wstring_view wstr)
static std::wstring _toString(const KeyChord& chord)
{
using vkeyToNamePair = std::pair<int32_t, std::wstring_view>;
static constinit til::static_map vkeyToName{
static constexpr til::static_map vkeyToName{
// The above VKEY_NAME_PAIRS macro contains a list of key-binding strings for each virtual key.
// This macro picks the first (most preferred) name and creates a static map of virtual keys to key-binding names.
#define GENERATOR(vkey, name1, ...) vkeyToNamePair{ vkey, name1 },
Expand Down
20 changes: 19 additions & 1 deletion src/inc/til.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
#include "til/color.h"
#include "til/enumset.h"
#include "til/pmr.h"
#include "til/replace.h"
#include "til/string.h"
#include "til/type_traits.h"
#include "til/u8u16convert.h"

// Use keywords on TraceLogging providers to specify the category
Expand Down Expand Up @@ -54,6 +54,24 @@

namespace til // Terminal Implementation Library. Also: "Today I Learned"
{
template<typename T>
as_view_t<T> clamp_slice_abs(const T& view, size_t beg, size_t end)
{
const auto len = view.size();
end = std::min(end, len);
beg = std::min(beg, end);
return { view.data() + beg, end - beg };
}

template<typename T>
as_view_t<T> clamp_slice_len(const T& view, size_t start, size_t count)
{
const auto len = view.size();
start = std::min(start, len);
count = std::min(count, len - start);
return { view.data() + start, count };
}

template<typename T>
void manage_vector(std::vector<T>& vector, typename std::vector<T>::size_type requestedSize, float shrinkThreshold)
{
Expand Down
2 changes: 0 additions & 2 deletions src/inc/til/bytes.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@

#pragma once

#include "type_traits.h"

namespace til
{
template<ContiguousBytes Target>
Expand Down
29 changes: 4 additions & 25 deletions src/inc/til/replace.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,6 @@

namespace til
{
namespace details
{
template<typename T>
struct view_type_oracle
{
};

template<>
struct view_type_oracle<std::string>
{
using type = std::string_view;
};

template<>
struct view_type_oracle<std::wstring>
{
using type = std::wstring_view;
};

}

// Method Description:
// - This is a function for finding all occurrences of a given string
// `needle` in a larger string `haystack`, and replacing them with the
Expand All @@ -39,8 +18,8 @@ namespace til
// - <none>
template<typename T>
void replace_needle_in_haystack_inplace(T& haystack,
const typename details::view_type_oracle<T>::type& needle,
const typename details::view_type_oracle<T>::type& replacement)
const as_view_t<T>& needle,
const as_view_t<T>& replacement)
{
auto pos{ T::npos };
while ((pos = haystack.rfind(needle, pos)) != T::npos)
Expand All @@ -63,8 +42,8 @@ namespace til
// - a copy of `haystack` with all instances of `needle` replaced with `replacement`.`
template<typename T>
T replace_needle_in_haystack(const T& haystack,
const typename details::view_type_oracle<T>::type& needle,
const typename details::view_type_oracle<T>::type& replacement)
const as_view_t<T>& needle,
const as_view_t<T>& replacement)
{
std::basic_string<typename T::value_type> result{ haystack };
replace_needle_in_haystack_inplace(result, needle, replacement);
Expand Down
21 changes: 21 additions & 0 deletions src/inc/til/type_traits.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,24 @@ namespace til
struct is_byte<std::byte> : std::true_type
{
};

template<typename T>
struct as_view
{
using type = T;
};

template<typename T, typename Alloc>
struct as_view<std::vector<T, Alloc>>
{
using type = std::span<const T>;
};

template<typename T, typename Traits, typename Alloc>
struct as_view<std::basic_string<T, Traits, Alloc>>
{
using type = std::basic_string_view<T, Traits>;
};
}

template<typename T>
Expand All @@ -45,4 +63,7 @@ namespace til

template<typename T>
concept TriviallyCopyable = std::is_trivially_copyable_v<T>;

template<typename T>
using as_view_t = typename details::as_view<T>::type;
}
56 changes: 29 additions & 27 deletions src/renderer/atlas/BackendD3D.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1430,19 +1430,19 @@ BackendD3D::AtlasGlyphEntry* BackendD3D::_drawBuiltinGlyph(const RenderingPayloa
_d2dBeginDrawing();

auto shadingType = ShadingType::TextGrayscale;
const D2D1_RECT_F r{
static_cast<f32>(rect.x),
static_cast<f32>(rect.y),
static_cast<f32>(rect.x + rect.w),
static_cast<f32>(rect.y + rect.h),
};

if (BuiltinGlyphs::IsSoftFontChar(glyphIndex))
{
_drawSoftFontGlyph(p, rect, glyphIndex);
_drawSoftFontGlyph(p, r, glyphIndex);
}
else
{
const D2D1_RECT_F r{
static_cast<f32>(rect.x),
static_cast<f32>(rect.y),
static_cast<f32>(rect.x + rect.w),
static_cast<f32>(rect.y + rect.h),
};
BuiltinGlyphs::DrawBuiltinGlyph(p.d2dFactory.get(), _d2dRenderTarget.get(), _brush.get(), r, glyphIndex);
shadingType = ShadingType::TextBuiltinGlyph;
}
Expand All @@ -1465,15 +1465,31 @@ BackendD3D::AtlasGlyphEntry* BackendD3D::_drawBuiltinGlyph(const RenderingPayloa
return glyphEntry;
}

void BackendD3D::_drawSoftFontGlyph(const RenderingPayload& p, const stbrp_rect& rect, u32 glyphIndex)
void BackendD3D::_drawSoftFontGlyph(const RenderingPayload& p, const D2D1_RECT_F& rect, u32 glyphIndex)
{
_d2dRenderTarget->PushAxisAlignedClip(&rect, D2D1_ANTIALIAS_MODE_ALIASED);
const auto restoreD2D = wil::scope_exit([&]() {
_d2dRenderTarget->PopAxisAlignedClip();
});

const auto width = static_cast<size_t>(p.s->font->softFontCellSize.width);
const auto height = static_cast<size_t>(p.s->font->softFontCellSize.height);
const auto softFontIndex = glyphIndex - 0xEF20u;
const auto data = til::clamp_slice_len(p.s->font->softFontPattern, height * softFontIndex, height);

if (data.empty() || data.size() != height)
{
_d2dRenderTarget->Clear();
return;
}

if (!_softFontBitmap)
{
// Allocating such a tiny texture is very wasteful (min. texture size on GPUs
// right now is 64kB), but this is a seldomly used feature so it's fine...
// right now is 64kB), but this is a seldom used feature, so it's fine...
const D2D1_SIZE_U size{
static_cast<UINT32>(p.s->font->softFontCellSize.width),
static_cast<UINT32>(p.s->font->softFontCellSize.height),
static_cast<UINT32>(width),
static_cast<UINT32>(height),
};
const D2D1_BITMAP_PROPERTIES1 bitmapProperties{
.pixelFormat = { DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED },
Expand All @@ -1484,17 +1500,11 @@ void BackendD3D::_drawSoftFontGlyph(const RenderingPayload& p, const stbrp_rect&
}

{
const auto width = static_cast<size_t>(p.s->font->softFontCellSize.width);
const auto height = static_cast<size_t>(p.s->font->softFontCellSize.height);

auto bitmapData = Buffer<u32>{ width * height };
const auto softFontIndex = glyphIndex - 0xEF20u;
auto src = p.s->font->softFontPattern.begin() + height * softFontIndex;
auto dst = bitmapData.begin();

for (size_t y = 0; y < height; y++)
for (auto srcBits : data)
{
auto srcBits = *src++;
for (size_t x = 0; x < width; x++)
{
const auto srcBitIsSet = (srcBits & 0x8000) != 0;
Expand All @@ -1508,15 +1518,7 @@ void BackendD3D::_drawSoftFontGlyph(const RenderingPayload& p, const stbrp_rect&
}

const auto interpolation = p.s->font->antialiasingMode == AntialiasingMode::Aliased ? D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR : D2D1_INTERPOLATION_MODE_HIGH_QUALITY_CUBIC;
const D2D1_RECT_F dest{
static_cast<f32>(rect.x),
static_cast<f32>(rect.y),
static_cast<f32>(rect.x + rect.w),
static_cast<f32>(rect.y + rect.h),
};

_d2dBeginDrawing();
_d2dRenderTarget->DrawBitmap(_softFontBitmap.get(), &dest, 1, interpolation, nullptr, nullptr);
_d2dRenderTarget->DrawBitmap(_softFontBitmap.get(), &rect, 1, interpolation, nullptr, nullptr);
}

void BackendD3D::_drawGlyphAtlasAllocate(const RenderingPayload& p, stbrp_rect& rect)
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/atlas/BackendD3D.h
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ namespace Microsoft::Console::Render::Atlas
ATLAS_ATTR_COLD void _drawTextOverlapSplit(const RenderingPayload& p, u16 y);
[[nodiscard]] ATLAS_ATTR_COLD AtlasGlyphEntry* _drawGlyph(const RenderingPayload& p, const ShapedRow& row, AtlasFontFaceEntry& fontFaceEntry, u32 glyphIndex);
AtlasGlyphEntry* _drawBuiltinGlyph(const RenderingPayload& p, const ShapedRow& row, AtlasFontFaceEntry& fontFaceEntry, u32 glyphIndex);
void _drawSoftFontGlyph(const RenderingPayload& p, const stbrp_rect& rect, u32 glyphIndex);
void _drawSoftFontGlyph(const RenderingPayload& p, const D2D1_RECT_F& rect, u32 glyphIndex);
void _drawGlyphAtlasAllocate(const RenderingPayload& p, stbrp_rect& rect);
static AtlasGlyphEntry* _drawGlyphAllocateEntry(const ShapedRow& row, AtlasFontFaceEntry& fontFaceEntry, u32 glyphIndex);
static void _splitDoubleHeightGlyph(const RenderingPayload& p, const ShapedRow& row, AtlasFontFaceEntry& fontFaceEntry, AtlasGlyphEntry* glyphEntry);
Expand Down
2 changes: 2 additions & 0 deletions src/til/ut_til/ReplaceTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#include "precomp.h"
#include "WexTestClass.h"

#include <til/replace.h>

using namespace WEX::Common;
using namespace WEX::Logging;
using namespace WEX::TestExecution;
Expand Down
4 changes: 2 additions & 2 deletions src/types/colorTable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ static constexpr std::array<til::color, 256> standard256ColorTable{
til::color{ 0xEE, 0xEE, 0xEE },
};

static constinit til::presorted_static_map xorgAppVariantColorTable{
static constexpr til::presorted_static_map xorgAppVariantColorTable{
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 } } },
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 } } },
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 } } },
Expand Down Expand Up @@ -348,7 +348,7 @@ static constinit til::presorted_static_map xorgAppVariantColorTable{
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 } } },
};

static constinit til::presorted_static_map xorgAppColorTable{
static constexpr til::presorted_static_map xorgAppColorTable{
std::pair{ "aliceblue"sv, til::color{ 240, 248, 255 } },
std::pair{ "aqua"sv, til::color{ 0, 255, 255 } },
std::pair{ "beige"sv, til::color{ 245, 245, 220 } },
Expand Down
Loading