Skip to content

Commit 0a2f238

Browse files
Eskil Abrahamsen Blomfeldtossilator
Eskil Abrahamsen Blomfeldt
authored andcommitted
Better handling of invalid font tables
Specifically when reading files with broken cmap tables, we could get some undeterministic results. We handle this more gracefully by verifying that the offsets are sane and bailing out early if not. This replaces the current pattern throughout the font engine for consistency. This is a back-port of 4a1e5dbade4bab55f39bd368480dcca9a11e4b38 from Qt 5. Change-Id: If4172b9ef0808801c8e27ffaad962535afe572ed Reviewed-by: Thiago Macieira <[email protected]> Reviewed-by: Lars Knoll <[email protected]>
1 parent b05d05f commit 0a2f238

10 files changed

+198
-85
lines changed

src/gui/text/qfontengine.cpp

+161-51
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,16 @@ static inline bool qtransform_equals_no_translate(const QTransform &a, const QTr
6969
}
7070
}
7171

72+
template<typename T>
73+
static inline bool qSafeFromBigEndian(const uchar *source, const uchar *end, T *output)
74+
{
75+
if (source + sizeof(T) > end)
76+
return false;
77+
78+
*output = qFromBigEndian<T>(source);
79+
return true;
80+
}
81+
7282
// Harfbuzz helper functions
7383

7484
static HB_Bool hb_stringToGlyphs(HB_Font font, const HB_UChar16 *string, hb_uint32 length, HB_Glyph *glyphs, hb_uint32 *numGlyphs, HB_Bool rightToLeft)
@@ -808,26 +818,38 @@ void QFontEngine::loadKerningPairs(QFixed scalingFactor)
808818
return;
809819

810820
const uchar *table = reinterpret_cast<const uchar *>(tab.constData());
821+
const uchar *end = table + tab.size();
822+
823+
quint16 version;
824+
if (!qSafeFromBigEndian(table, end, &version))
825+
return;
811826

812-
unsigned short version = qFromBigEndian<quint16>(table);
813827
if (version != 0) {
814828
// qDebug("wrong version");
815829
return;
816830
}
817831

818-
unsigned short numTables = qFromBigEndian<quint16>(table + 2);
832+
quint16 numTables;
833+
if (!qSafeFromBigEndian(table + 2, end, &numTables))
834+
return;
835+
819836
{
820837
int offset = 4;
821838
for(int i = 0; i < numTables; ++i) {
822-
if (offset + 6 > tab.size()) {
823-
// qDebug("offset out of bounds");
824-
goto end;
825-
}
826839
const uchar *header = table + offset;
827840

828-
ushort version = qFromBigEndian<quint16>(header);
829-
ushort length = qFromBigEndian<quint16>(header+2);
830-
ushort coverage = qFromBigEndian<quint16>(header+4);
841+
quint16 version;
842+
if (!qSafeFromBigEndian(header, end, &version))
843+
goto end;
844+
845+
quint16 length;
846+
if (!qSafeFromBigEndian(header + 2, end, &length))
847+
goto end;
848+
849+
quint16 coverage;
850+
if (!qSafeFromBigEndian(header + 4, end, &coverage))
851+
goto end;
852+
831853
// qDebug("subtable: version=%d, coverage=%x",version, coverage);
832854
if(version == 0 && coverage == 0x0001) {
833855
if (offset + length > tab.size()) {
@@ -836,7 +858,10 @@ void QFontEngine::loadKerningPairs(QFixed scalingFactor)
836858
}
837859
const uchar *data = table + offset + 6;
838860

839-
ushort nPairs = qFromBigEndian<quint16>(data);
861+
quint16 nPairs;
862+
if (!qSafeFromBigEndian(data, end, &nPairs))
863+
goto end;
864+
840865
if(nPairs * 6 + 8 > length - 6) {
841866
// qDebug("corrupt table!");
842867
// corrupt table
@@ -846,8 +871,21 @@ void QFontEngine::loadKerningPairs(QFixed scalingFactor)
846871
int off = 8;
847872
for(int i = 0; i < nPairs; ++i) {
848873
QFontEngine::KernPair p;
849-
p.left_right = (((uint)qFromBigEndian<quint16>(data+off)) << 16) + qFromBigEndian<quint16>(data+off+2);
850-
p.adjust = QFixed(((int)(short)qFromBigEndian<quint16>(data+off+4))) / scalingFactor;
874+
875+
quint16 tmp;
876+
if (!qSafeFromBigEndian(data + off, end, &tmp))
877+
goto end;
878+
879+
p.left_right = uint(tmp) << 16;
880+
if (!qSafeFromBigEndian(data + off + 2, end, &tmp))
881+
goto end;
882+
883+
p.left_right |= tmp;
884+
885+
if (!qSafeFromBigEndian(data + off + 4, end, &tmp))
886+
goto end;
887+
888+
p.adjust = QFixed(int(short(tmp))) / scalingFactor;
851889
kerning_pairs.append(p);
852890
off += 6;
853891
}
@@ -872,26 +910,31 @@ int QFontEngine::glyphCount() const
872910
QByteArray maxpTable = getSfntTable(MAKE_TAG('m', 'a', 'x', 'p'));
873911
if (maxpTable.size() < 6)
874912
return 0;
875-
return qFromBigEndian<quint16>(reinterpret_cast<const uchar *>(maxpTable.constData() + 4));
913+
914+
const uchar *source = reinterpret_cast<const uchar *>(maxpTable.constData() + 4);
915+
const uchar *end = source + maxpTable.size();
916+
917+
quint16 count = 0;
918+
qSafeFromBigEndian(source, end, &count);
919+
return count;
876920
}
877921

878922
const uchar *QFontEngine::getCMap(const uchar *table, uint tableSize, bool *isSymbolFont, int *cmapSize)
879923
{
880924
const uchar *header = table;
881-
if (tableSize < 4)
882-
return 0;
883-
884925
const uchar *endPtr = table + tableSize;
885926

886927
// version check
887-
if (qFromBigEndian<quint16>(header) != 0)
928+
quint16 version;
929+
if (!qSafeFromBigEndian(header, endPtr, &version) || version != 0)
888930
return 0;
889931

890-
unsigned short numTables = qFromBigEndian<quint16>(header + 2);
891-
const uchar *maps = table + 4;
892-
if (maps + 8 * numTables > endPtr)
932+
quint16 numTables;
933+
if (!qSafeFromBigEndian(header + 2, endPtr, &numTables))
893934
return 0;
894935

936+
const uchar *maps = table + 4;
937+
895938
enum {
896939
Invalid,
897940
AppleRoman,
@@ -906,8 +949,14 @@ const uchar *QFontEngine::getCMap(const uchar *table, uint tableSize, bool *isSy
906949
int tableToUse = -1;
907950
int score = Invalid;
908951
for (int n = 0; n < numTables; ++n) {
909-
const quint16 platformId = qFromBigEndian<quint16>(maps + 8 * n);
910-
const quint16 platformSpecificId = qFromBigEndian<quint16>(maps + 8 * n + 2);
952+
quint16 platformId;
953+
if (!qSafeFromBigEndian(maps + 8 * n, endPtr, &platformId))
954+
return 0;
955+
956+
quint16 platformSpecificId;
957+
if (!qSafeFromBigEndian(maps + 8 * n + 2, endPtr, &platformSpecificId))
958+
return 0;
959+
911960
switch (platformId) {
912961
case 0: // Unicode
913962
if (score < Unicode &&
@@ -961,20 +1010,30 @@ const uchar *QFontEngine::getCMap(const uchar *table, uint tableSize, bool *isSy
9611010
resolveTable:
9621011
*isSymbolFont = (symbolTable > -1);
9631012

964-
unsigned int unicode_table = qFromBigEndian<quint32>(maps + 8*tableToUse + 4);
1013+
quint32 unicode_table;
1014+
if (!qSafeFromBigEndian(maps + 8 * tableToUse + 4, endPtr, &unicode_table))
1015+
return 0;
9651016

966-
if (!unicode_table || unicode_table + 8 > tableSize)
1017+
if (!unicode_table)
9671018
return 0;
9681019

9691020
// get the header of the unicode table
9701021
header = table + unicode_table;
9711022

972-
unsigned short format = qFromBigEndian<quint16>(header);
973-
unsigned int length;
974-
if(format < 8)
975-
length = qFromBigEndian<quint16>(header + 2);
976-
else
977-
length = qFromBigEndian<quint32>(header + 4);
1023+
quint16 format;
1024+
if (!qSafeFromBigEndian(header, endPtr, &format))
1025+
return 0;
1026+
1027+
quint32 length;
1028+
if (format < 8) {
1029+
quint16 tmp;
1030+
if (!qSafeFromBigEndian(header + 2, endPtr, &tmp))
1031+
return 0;
1032+
length = tmp;
1033+
} else {
1034+
if (!qSafeFromBigEndian(header + 4, endPtr, &length))
1035+
return 0;
1036+
}
9781037

9791038
if (table + unicode_table + length > endPtr)
9801039
return 0;
@@ -989,7 +1048,7 @@ const uchar *QFontEngine::getCMap(const uchar *table, uint tableSize, bool *isSy
9891048
// Check that none of the latin1 range are in the unicode table
9901049
bool unicodeTableHasLatin1 = false;
9911050
for (int uc=0x00; uc<0x100; ++uc) {
992-
if (getTrueTypeGlyphIndex(selectedTable, uc) != 0) {
1051+
if (getTrueTypeGlyphIndex(selectedTable, length, uc) != 0) {
9931052
unicodeTableHasLatin1 = true;
9941053
break;
9951054
}
@@ -999,7 +1058,7 @@ const uchar *QFontEngine::getCMap(const uchar *table, uint tableSize, bool *isSy
9991058
bool unicodeTableHasSymbols = false;
10001059
if (!unicodeTableHasLatin1) {
10011060
for (int uc=0xf000; uc<0xf100; ++uc) {
1002-
if (getTrueTypeGlyphIndex(selectedTable, uc) != 0) {
1061+
if (getTrueTypeGlyphIndex(selectedTable, length, uc) != 0) {
10031062
unicodeTableHasSymbols = true;
10041063
break;
10051064
}
@@ -1017,12 +1076,17 @@ const uchar *QFontEngine::getCMap(const uchar *table, uint tableSize, bool *isSy
10171076
return table + unicode_table;
10181077
}
10191078

1020-
quint32 QFontEngine::getTrueTypeGlyphIndex(const uchar *cmap, uint unicode)
1079+
quint32 QFontEngine::getTrueTypeGlyphIndex(const uchar *cmap, int cmapSize, uint unicode)
10211080
{
1022-
unsigned short format = qFromBigEndian<quint16>(cmap);
1081+
const uchar *end = cmap + cmapSize;
1082+
quint16 format;
1083+
if (!qSafeFromBigEndian(cmap, end, &format))
1084+
return 0;
1085+
10231086
if (format == 0) {
1024-
if (unicode < 256)
1025-
return (int) *(cmap+6+unicode);
1087+
const uchar *ptr = cmap + 6 + unicode;
1088+
if (unicode < 256 && ptr < end)
1089+
return quint32(*ptr);
10261090
} else if (format == 4) {
10271091
/* some fonts come with invalid cmap tables, where the last segment
10281092
specified end = start = rangeoffset = 0xffff, delta = 0x0001
@@ -1031,25 +1095,49 @@ quint32 QFontEngine::getTrueTypeGlyphIndex(const uchar *cmap, uint unicode)
10311095
*/
10321096
if(unicode >= 0xffff)
10331097
return 0;
1034-
quint16 segCountX2 = qFromBigEndian<quint16>(cmap + 6);
1098+
1099+
quint16 segCountX2;
1100+
if (!qSafeFromBigEndian(cmap + 6, end, &segCountX2))
1101+
return 0;
1102+
10351103
const unsigned char *ends = cmap + 14;
1104+
10361105
int i = 0;
1037-
for (; i < segCountX2/2 && qFromBigEndian<quint16>(ends + 2*i) < unicode; i++) {}
1106+
for (; i < segCountX2/2; ++i) {
1107+
quint16 codePoint;
1108+
if (!qSafeFromBigEndian(ends + 2 * i, end, &codePoint))
1109+
return 0;
1110+
if (codePoint >= unicode)
1111+
break;
1112+
}
10381113

10391114
const unsigned char *idx = ends + segCountX2 + 2 + 2*i;
1040-
quint16 startIndex = qFromBigEndian<quint16>(idx);
10411115

1116+
quint16 startIndex;
1117+
if (!qSafeFromBigEndian(idx, end, &startIndex))
1118+
return 0;
10421119
if (startIndex > unicode)
10431120
return 0;
10441121

10451122
idx += segCountX2;
1046-
qint16 idDelta = (qint16)qFromBigEndian<quint16>(idx);
1123+
1124+
quint16 tmp;
1125+
if (!qSafeFromBigEndian(idx, end, &tmp))
1126+
return 0;
1127+
qint16 idDelta = qint16(tmp);
1128+
10471129
idx += segCountX2;
1048-
quint16 idRangeoffset_t = (quint16)qFromBigEndian<quint16>(idx);
1130+
1131+
quint16 idRangeoffset_t;
1132+
if (!qSafeFromBigEndian(idx, end, &idRangeoffset_t))
1133+
return 0;
10491134

10501135
quint16 glyphIndex;
10511136
if (idRangeoffset_t) {
1052-
quint16 id = qFromBigEndian<quint16>(idRangeoffset_t + 2*(unicode - startIndex) + idx);
1137+
quint16 id;
1138+
if (!qSafeFromBigEndian(idRangeoffset_t + 2 * (unicode - startIndex) + idx, end, &id))
1139+
return 0;
1140+
10531141
if (id)
10541142
glyphIndex = (idDelta + id) % 0x10000;
10551143
else
@@ -1059,13 +1147,19 @@ quint32 QFontEngine::getTrueTypeGlyphIndex(const uchar *cmap, uint unicode)
10591147
}
10601148
return glyphIndex;
10611149
} else if (format == 6) {
1062-
quint16 tableSize = qFromBigEndian<quint16>(cmap + 2);
1150+
quint16 tableSize;
1151+
if (!qSafeFromBigEndian(cmap + 2, end, &tableSize))
1152+
return 0;
10631153

1064-
quint16 firstCode6 = qFromBigEndian<quint16>(cmap + 6);
1154+
quint16 firstCode6;
1155+
if (!qSafeFromBigEndian(cmap + 6, end, &firstCode6))
1156+
return 0;
10651157
if (unicode < firstCode6)
10661158
return 0;
10671159

1068-
quint16 entryCount6 = qFromBigEndian<quint16>(cmap + 8);
1160+
quint16 entryCount6;
1161+
if (!qSafeFromBigEndian(cmap + 8, end, &entryCount6))
1162+
return 0;
10691163
if (entryCount6 * 2 + 10 > tableSize)
10701164
return 0;
10711165

@@ -1074,23 +1168,39 @@ quint32 QFontEngine::getTrueTypeGlyphIndex(const uchar *cmap, uint unicode)
10741168
return 0;
10751169

10761170
quint16 entryIndex6 = unicode - firstCode6;
1077-
return qFromBigEndian<quint16>(cmap + 10 + (entryIndex6 * 2));
1171+
1172+
quint16 index = 0;
1173+
qSafeFromBigEndian(cmap + 10 + (entryIndex6 * 2), end, &index);
1174+
return index;
10781175
} else if (format == 12) {
1079-
quint32 nGroups = qFromBigEndian<quint32>(cmap + 12);
1176+
quint32 nGroups;
1177+
if (!qSafeFromBigEndian(cmap + 12, end, &nGroups))
1178+
return 0;
10801179

10811180
cmap += 16; // move to start of groups
10821181

10831182
int left = 0, right = nGroups - 1;
10841183
while (left <= right) {
10851184
int middle = left + ( ( right - left ) >> 1 );
10861185

1087-
quint32 startCharCode = qFromBigEndian<quint32>(cmap + 12*middle);
1186+
quint32 startCharCode;
1187+
if (!qSafeFromBigEndian(cmap + 12 * middle, end, &startCharCode))
1188+
return 0;
1189+
10881190
if(unicode < startCharCode)
10891191
right = middle - 1;
10901192
else {
1091-
quint32 endCharCode = qFromBigEndian<quint32>(cmap + 12*middle + 4);
1092-
if(unicode <= endCharCode)
1093-
return qFromBigEndian<quint32>(cmap + 12*middle + 8) + unicode - startCharCode;
1193+
quint32 endCharCode;
1194+
if (!qSafeFromBigEndian(cmap + 12 * middle + 4, end, &endCharCode))
1195+
return 0;
1196+
1197+
if (unicode <= endCharCode) {
1198+
quint32 index;
1199+
if (!qSafeFromBigEndian(cmap + 12 * middle + 8, end, &index))
1200+
return 0;
1201+
1202+
return index + unicode - startCharCode;
1203+
}
10941204
left = middle + 1;
10951205
}
10961206
}

src/gui/text/qfontengine_mac.mm

+5-6
Original file line numberDiff line numberDiff line change
@@ -625,7 +625,7 @@ static OSStatus atsuPostLayoutCallback(ATSULayoutOperationSelector selector, ATS
625625
}
626626

627627
QFontEngineMac::QFontEngineMac(ATSUStyle baseStyle, ATSUFontID fontID, const QFontDef &def, QFontEngineMacMulti *multiEngine)
628-
: fontID(fontID), multiEngine(multiEngine), cmap(0), symbolCMap(false)
628+
: fontID(fontID), multiEngine(multiEngine), cmap(0), symbolCMap(false), cmapSize(0)
629629
{
630630
fontDef = def;
631631
ATSUCreateAndCopyStyle(baseStyle, &style);
@@ -747,22 +747,21 @@ static inline unsigned int getChar(const QChar *str, int &i, const int len)
747747
{
748748
if (!cmap) {
749749
cmapTable = getSfntTable(MAKE_TAG('c', 'm', 'a', 'p'));
750-
int size = 0;
751-
cmap = getCMap(reinterpret_cast<const uchar *>(cmapTable.constData()), cmapTable.size(), &symbolCMap, &size);
750+
cmap = getCMap(reinterpret_cast<const uchar *>(cmapTable.constData()), cmapTable.size(), &symbolCMap, &cmapSize);
752751
if (!cmap)
753752
return false;
754753
}
755754
if (symbolCMap) {
756755
for (int i = 0; i < len; ++i) {
757756
unsigned int uc = getChar(str, i, len);
758-
glyphs->glyphs[i] = getTrueTypeGlyphIndex(cmap, uc);
757+
glyphs->glyphs[i] = getTrueTypeGlyphIndex(cmap, cmapSize, uc);
759758
if(!glyphs->glyphs[i] && uc < 0x100)
760-
glyphs->glyphs[i] = getTrueTypeGlyphIndex(cmap, uc + 0xf000);
759+
glyphs->glyphs[i] = getTrueTypeGlyphIndex(cmap, cmapSize, uc + 0xf000);
761760
}
762761
} else {
763762
for (int i = 0; i < len; ++i) {
764763
unsigned int uc = getChar(str, i, len);
765-
glyphs->glyphs[i] = getTrueTypeGlyphIndex(cmap, uc);
764+
glyphs->glyphs[i] = getTrueTypeGlyphIndex(cmap, cmapSize, uc);
766765
}
767766
}
768767

0 commit comments

Comments
 (0)