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
Pick-to: 5.15 6.2 6.5 6.6 6.6.0
Change-Id: I156df3b8833c9ed785fcc690821a7a74d9a51126
Reviewed-by: Lars Knoll <lars@knoll.priv.no>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
This commit is contained in:
Eskil Abrahamsen Blomfeldt 2023-09-11 11:40:46 +02:00
parent a1b55b1360
commit 9fe47cf2e1

View File

@ -873,36 +873,70 @@ QT_WARNING_POP
return fontEngine; return fontEngine;
} }
static QList<quint32> getTrueTypeFontOffsets(const uchar *fontData) static QList<quint32> getTrueTypeFontOffsets(const uchar *fontData, const uchar *fileEndSentinel)
{ {
QList<quint32> offsets; QList<quint32> offsets;
const quint32 headerTag = *reinterpret_cast<const quint32 *>(fontData); if (fileEndSentinel - fontData < 12) {
qCWarning(lcQpaFonts) << "Corrupted font data detected";
return offsets;
}
const quint32 headerTag = qFromUnaligned<quint32>(fontData);
if (headerTag != MAKE_TAG('t', 't', 'c', 'f')) { if (headerTag != MAKE_TAG('t', 't', 'c', 'f')) {
if (headerTag != MAKE_TAG(0, 1, 0, 0) if (headerTag != MAKE_TAG(0, 1, 0, 0)
&& headerTag != MAKE_TAG('O', 'T', 'T', 'O') && headerTag != MAKE_TAG('O', 'T', 'T', 'O')
&& headerTag != MAKE_TAG('t', 'r', 'u', 'e') && headerTag != MAKE_TAG('t', 'r', 'u', 'e')
&& headerTag != MAKE_TAG('t', 'y', 'p', '1')) && headerTag != MAKE_TAG('t', 'y', 'p', '1')) {
return offsets; return offsets;
}
offsets << 0; offsets << 0;
return offsets; return offsets;
} }
const quint32 maximumNumFonts = 0xffff;
const quint32 numFonts = qFromBigEndian<quint32>(fontData + 8); const quint32 numFonts = qFromBigEndian<quint32>(fontData + 8);
for (uint i = 0; i < numFonts; ++i) { if (numFonts > maximumNumFonts) {
offsets << qFromBigEndian<quint32>(fontData + 12 + i * 4); 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<quint32>(fontData + 12 + i * 4);
} else {
qCWarning(lcQpaFonts) << "Corrupted font data detected";
}
return offsets; 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<quint16>(data + 4); if (fileEndSentinel - data >= 6) {
for (uint i = 0; i < numTables; ++i) { const quint16 numTables = qFromBigEndian<quint16>(data + 4);
const quint32 offset = 12 + 16 * i; if (fileEndSentinel - data >= 28 + 16 * (numTables - 1)) {
if (*reinterpret_cast<const quint32 *>(data + offset) == tag) { for (quint32 i = 0; i < numTables; ++i) {
*table = fileBegin + qFromBigEndian<quint32>(data + offset + 8); const quint32 offset = 12 + 16 * i;
*length = qFromBigEndian<quint32>(data + offset + 12); if (qFromUnaligned<quint32>(data + offset) == tag) {
return; const quint32 tableOffset = qFromBigEndian<quint32>(data + offset + 8);
if (quintptr(fileEndSentinel - fileBegin) <= tableOffset) {
qCWarning(lcQpaFonts) << "Corrupted font data detected";
break;
}
*table = fileBegin + tableOffset;
*length = qFromBigEndian<quint32>(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; *table = 0;
*length = 0; *length = 0;
@ -915,8 +949,9 @@ static void getFamiliesAndSignatures(const QByteArray &fontData,
QList<QFontValues> *values) QList<QFontValues> *values)
{ {
const uchar *data = reinterpret_cast<const uchar *>(fontData.constData()); const uchar *data = reinterpret_cast<const uchar *>(fontData.constData());
const uchar *dataEndSentinel = data + fontData.size();
QList<quint32> offsets = getTrueTypeFontOffsets(data); QList<quint32> offsets = getTrueTypeFontOffsets(data, dataEndSentinel);
if (offsets.isEmpty()) if (offsets.isEmpty())
return; return;
@ -924,7 +959,7 @@ static void getFamiliesAndSignatures(const QByteArray &fontData,
const uchar *font = data + offsets.at(i); const uchar *font = data + offsets.at(i);
const uchar *table; const uchar *table;
quint32 length; 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) if (!table)
continue; continue;
QFontNames names = qt_getCanonicalFontNames(table, length); QFontNames names = qt_getCanonicalFontNames(table, length);
@ -934,7 +969,7 @@ static void getFamiliesAndSignatures(const QByteArray &fontData,
families->append(std::move(names)); families->append(std::move(names));
if (values || signatures) 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) { if (values) {
QFontValues fontValues; QFontValues fontValues;