From a66a3983f213ca3dcfabeef3d68733bcd48748b8 Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Mon, 11 Sep 2023 11:40:46 +0200 Subject: [PATCH] Fix crash when reading corrupt font data When loading the font data, we had some unprotected reads. To harden this, we check everything against the length of the font data before reading. [ChangeLog][QtGui][Windows] Fixed a possible crash that could happen when loading corrupted font data. Fixes: QTBUG-116773 Change-Id: I156df3b8833c9ed785fcc690821a7a74d9a51126 Reviewed-by: Lars Knoll Reviewed-by: Thiago Macieira Reviewed-by: Qt CI Bot (cherry picked from commit 9fe47cf2e11d7c9ad4f72e6fc5e53f10a9743b03) Reviewed-by: Qt Cherry-pick Bot (cherry picked from commit d0ed5db78160ce7c16d9fffef79aa11a7783e34e) --- src/gui/text/windows/qwindowsfontdatabase.cpp | 67 ++++++++++++++----- 1 file changed, 51 insertions(+), 16 deletions(-) diff --git a/src/gui/text/windows/qwindowsfontdatabase.cpp b/src/gui/text/windows/qwindowsfontdatabase.cpp index 2de53be6a8a..3f27466df65 100644 --- a/src/gui/text/windows/qwindowsfontdatabase.cpp +++ b/src/gui/text/windows/qwindowsfontdatabase.cpp @@ -868,36 +868,70 @@ QT_WARNING_POP return fontEngine; } -static QList getTrueTypeFontOffsets(const uchar *fontData) +static QList getTrueTypeFontOffsets(const uchar *fontData, const uchar *fileEndSentinel) { QList offsets; - const quint32 headerTag = *reinterpret_cast(fontData); + if (fileEndSentinel - fontData < 12) { + qCWarning(lcQpaFonts) << "Corrupted font data detected"; + return offsets; + } + + const quint32 headerTag = qFromUnaligned(fontData); if (headerTag != MAKE_TAG('t', 't', 'c', 'f')) { if (headerTag != MAKE_TAG(0, 1, 0, 0) && headerTag != MAKE_TAG('O', 'T', 'T', 'O') && headerTag != MAKE_TAG('t', 'r', 'u', 'e') - && headerTag != MAKE_TAG('t', 'y', 'p', '1')) + && headerTag != MAKE_TAG('t', 'y', 'p', '1')) { return offsets; + } offsets << 0; return offsets; } + + const quint32 maximumNumFonts = 0xffff; const quint32 numFonts = qFromBigEndian(fontData + 8); - for (uint i = 0; i < numFonts; ++i) { - offsets << qFromBigEndian(fontData + 12 + i * 4); + if (numFonts > maximumNumFonts) { + qCWarning(lcQpaFonts) << "Font collection of" << numFonts << "fonts is too large. Aborting."; + return offsets; } + + if (quintptr(fileEndSentinel - fontData) > 12 + (numFonts - 1) * 4) { + for (quint32 i = 0; i < numFonts; ++i) + offsets << qFromBigEndian(fontData + 12 + i * 4); + } else { + qCWarning(lcQpaFonts) << "Corrupted font data detected"; + } + return offsets; } -static void getFontTable(const uchar *fileBegin, const uchar *data, quint32 tag, const uchar **table, quint32 *length) +static void getFontTable(const uchar *fileBegin, const uchar *fileEndSentinel, const uchar *data, quint32 tag, const uchar **table, quint32 *length) { - const quint16 numTables = qFromBigEndian(data + 4); - for (uint i = 0; i < numTables; ++i) { - const quint32 offset = 12 + 16 * i; - if (*reinterpret_cast(data + offset) == tag) { - *table = fileBegin + qFromBigEndian(data + offset + 8); - *length = qFromBigEndian(data + offset + 12); - return; + if (fileEndSentinel - data >= 6) { + const quint16 numTables = qFromBigEndian(data + 4); + if (fileEndSentinel - data >= 28 + 16 * (numTables - 1)) { + for (quint32 i = 0; i < numTables; ++i) { + const quint32 offset = 12 + 16 * i; + if (qFromUnaligned(data + offset) == tag) { + const quint32 tableOffset = qFromBigEndian(data + offset + 8); + if (quintptr(fileEndSentinel - fileBegin) <= tableOffset) { + qCWarning(lcQpaFonts) << "Corrupted font data detected"; + break; + } + *table = fileBegin + tableOffset; + *length = qFromBigEndian(data + offset + 12); + if (quintptr(fileEndSentinel - *table) < *length) { + qCWarning(lcQpaFonts) << "Corrupted font data detected"; + break; + } + return; + } + } + } else { + qCWarning(lcQpaFonts) << "Corrupted font data detected"; } + } else { + qCWarning(lcQpaFonts) << "Corrupted font data detected"; } *table = 0; *length = 0; @@ -910,8 +944,9 @@ static void getFamiliesAndSignatures(const QByteArray &fontData, QList *values) { const uchar *data = reinterpret_cast(fontData.constData()); + const uchar *dataEndSentinel = data + fontData.size(); - QList offsets = getTrueTypeFontOffsets(data); + QList offsets = getTrueTypeFontOffsets(data, dataEndSentinel); if (offsets.isEmpty()) return; @@ -919,7 +954,7 @@ static void getFamiliesAndSignatures(const QByteArray &fontData, const uchar *font = data + offsets.at(i); const uchar *table; quint32 length; - getFontTable(data, font, MAKE_TAG('n', 'a', 'm', 'e'), &table, &length); + getFontTable(data, dataEndSentinel, font, MAKE_TAG('n', 'a', 'm', 'e'), &table, &length); if (!table) continue; QFontNames names = qt_getCanonicalFontNames(table, length); @@ -929,7 +964,7 @@ static void getFamiliesAndSignatures(const QByteArray &fontData, families->append(std::move(names)); if (values || signatures) - getFontTable(data, font, MAKE_TAG('O', 'S', '/', '2'), &table, &length); + getFontTable(data, dataEndSentinel, font, MAKE_TAG('O', 'S', '/', '2'), &table, &length); if (values) { QFontValues fontValues;