[PATCH v4 0/3] MR10859: gdi32/uniscribe: Add bounds checks in GPOS_apply_MarkToMark().
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=59680 -- v4: gdi32/uniscribe: Guard against int overflow in GPOS functions. https://gitlab.winehq.org/wine/wine/-/merge_requests/10859
From: समीर सिंह Sameer Singh <lumarzeli30@gmail.com> Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=59680 --- dlls/gdi32/uniscribe/opentype.c | 62 +++++++++++++++++++++++++++------ 1 file changed, 52 insertions(+), 10 deletions(-) diff --git a/dlls/gdi32/uniscribe/opentype.c b/dlls/gdi32/uniscribe/opentype.c index 2e6d5e80746..724e3948a75 100644 --- a/dlls/gdi32/uniscribe/opentype.c +++ b/dlls/gdi32/uniscribe/opentype.c @@ -1722,7 +1722,7 @@ static void GPOS_apply_SingleAdjustment(const OT_LookupTable *look, const SCRIPT spf2 = (const GPOS_SinglePosFormat2*)spf1; offset = GET_BE_WORD(spf2->Coverage); index = GSUB_is_glyph_covered((const BYTE*)spf2+offset, glyphs[glyph_index]); - if (index != -1) + if (index != -1 && index < GET_BE_WORD(spf2->ValueCount)) { int size; GPOS_ValueRecord ValueRecord = {0,0,0,0,0,0,0,0}; @@ -1873,10 +1873,10 @@ static void GPOS_apply_CursiveAttachment(const OT_LookupTable *look, const SCRIP int index_exit, index_entry; WORD offset = GET_BE_WORD( cpf1->Coverage ); index_exit = GSUB_is_glyph_covered((const BYTE*)cpf1+offset, glyphs[glyph_index]); - if (index_exit != -1 && cpf1->EntryExitRecord[index_exit].ExitAnchor!= 0) + if (index_exit != -1 && index_exit < GET_BE_WORD(cpf1->EntryExitCount) && cpf1->EntryExitRecord[index_exit].ExitAnchor!= 0) { index_entry = GSUB_is_glyph_covered((const BYTE*)cpf1+offset, glyphs[glyph_index+write_dir]); - if (index_entry != -1 && cpf1->EntryExitRecord[index_entry].EntryAnchor != 0) + if (index_entry != -1 && index_entry < GET_BE_WORD(cpf1->EntryExitCount) && cpf1->EntryExitRecord[index_entry].EntryAnchor != 0) { POINT exit_pt, entry_pt; offset = GET_BE_WORD(cpf1->EntryExitRecord[index_exit].ExitAnchor); @@ -1913,6 +1913,12 @@ static int GPOS_apply_MarkToBase(const ScriptCache *script_cache, const OT_Looku glyph_class_table = (const BYTE *)script_cache->GDEF_Table + offset; } + if (glyph_index - write_dir >= glyph_count) + { + TRACE("Mark looking outside range\n"); + return -1; + } + TRACE("MarkToBase Attachment Positioning Subtable\n"); for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++) @@ -1930,7 +1936,7 @@ static int GPOS_apply_MarkToBase(const ScriptCache *script_cache, const OT_Looku if (glyph_class_table) { - while (OT_get_glyph_class(glyph_class_table, glyphs[base_glyph]) == MarkGlyph && base_glyph > 0 && base_glyph < glyph_count) + while ((unsigned int)base_glyph < glyph_count && OT_get_glyph_class(glyph_class_table, glyphs[base_glyph]) == MarkGlyph) base_glyph -= write_dir; } @@ -1958,8 +1964,18 @@ static int GPOS_apply_MarkToBase(const ScriptCache *script_cache, const OT_Looku mr = &ma->MarkRecord[mark_index]; mark_class = GET_BE_WORD(mr->Class); TRACE("Mark Class %i total classes %i\n",mark_class,class_count); + if (mark_class >= class_count) + { + ERR("Mark class exceeded total classes\n"); + return -1; + } offset = GET_BE_WORD(mbpf1->BaseArray); ba = (const GPOS_BaseArray*)((const BYTE*)mbpf1 + offset); + if (base_index >= GET_BE_WORD(ba->BaseCount)) + { + ERR("Base index exceeded base count\n"); + return -1; + } baserecord_size = class_count * sizeof(WORD); br = (const GPOS_BaseRecord*)((const BYTE*)ba + sizeof(WORD) + (baserecord_size * base_index)); offset = GET_BE_WORD(br->BaseAnchor[mark_class]); @@ -1987,6 +2003,12 @@ static void GPOS_apply_MarkToLigature(const OT_LookupTable *look, const SCRIPT_A int j; int write_dir = (analysis->fRTL && !analysis->fLogicalOrder) ? -1 : 1; + if (glyph_index - write_dir >= glyph_count) + { + TRACE("Mark looking outside range\n"); + return; + } + TRACE("MarkToLigature Attachment Positioning Subtable\n"); for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++) @@ -2077,6 +2099,13 @@ static BOOL GPOS_apply_MarkToMark(const OT_LookupTable *look, const SCRIPT_ANALY int j; BOOL rc = FALSE; int write_dir = (analysis->fRTL && !analysis->fLogicalOrder) ? -1 : 1; + unsigned int prev_glyph_index = glyph_index - write_dir; + + if (prev_glyph_index >= glyph_count) + { + TRACE("Mark looking outside range\n"); + return FALSE; + } TRACE("MarkToMark Attachment Positioning Subtable\n"); @@ -2091,12 +2120,6 @@ static BOOL GPOS_apply_MarkToMark(const OT_LookupTable *look, const SCRIPT_ANALY if (mark_index != -1) { int mark2_index; - unsigned int prev_glyph_index = glyph_index - write_dir; - if (prev_glyph_index >= glyph_count) - { - TRACE("Mark looking outside range\n"); - return FALSE; - } offset = GET_BE_WORD(mmpf1->Mark2Coverage); mark2_index = GSUB_is_glyph_covered((const BYTE*)mmpf1+offset, glyphs[prev_glyph_index]); if (mark2_index != -1) @@ -2121,8 +2144,18 @@ static BOOL GPOS_apply_MarkToMark(const OT_LookupTable *look, const SCRIPT_ANALY mr = &ma->MarkRecord[mark_index]; mark_class = GET_BE_WORD(mr->Class); TRACE("Mark Class %i total classes %i\n",mark_class,class_count); + if (mark_class >= class_count) + { + ERR("Mark class exceeded total classes\n"); + return FALSE; + } offset = GET_BE_WORD(mmpf1->Mark2Array); m2a = (const GPOS_Mark2Array*)((const BYTE*)mmpf1 + offset); + if (mark2_index >= GET_BE_WORD(m2a->Mark2Count)) + { + ERR("Mark2 index exceeded mark2 count\n"); + return FALSE; + } mark2record_size = class_count * sizeof(WORD); m2r = (const GPOS_Mark2Record*)((const BYTE*)m2a + sizeof(WORD) + (mark2record_size * mark2_index)); offset = GET_BE_WORD(m2r->Mark2Anchor[mark_class]); @@ -2185,6 +2218,9 @@ static unsigned int GPOS_apply_ContextPos(const ScriptCache *script_cache, const class = OT_get_glyph_class(glyph_class_table,glyphs[glyph_index]); + if (class >= GET_BE_WORD(cpf2->PosClassSetCnt)) + continue; + offset = GET_BE_WORD(cpf2->PosClassSet[class]); if (offset == 0) { @@ -2407,6 +2443,12 @@ static unsigned int GPOS_apply_lookup(const ScriptCache *script_cache, const OUT int ppem = lpotm->otmTextMetrics.tmAscent + lpotm->otmTextMetrics.tmDescent - lpotm->otmTextMetrics.tmInternalLeading; enum gpos_lookup_type type; + if (glyph_index >= glyph_count) + { + WARN("Glyph index greater than total glyphs\n"); + return 1; + } + offset = GET_BE_WORD(lookup->Lookup[lookup_index]); look = (const OT_LookupTable*)((const BYTE*)lookup + offset); type = GET_BE_WORD(look->LookupType); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10859
From: समीर सिंह Sameer Singh <lumarzeli30@gmail.com> --- dlls/gdi32/uniscribe/opentype.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dlls/gdi32/uniscribe/opentype.c b/dlls/gdi32/uniscribe/opentype.c index 724e3948a75..c4cd5bdbec6 100644 --- a/dlls/gdi32/uniscribe/opentype.c +++ b/dlls/gdi32/uniscribe/opentype.c @@ -1956,7 +1956,7 @@ static int GPOS_apply_MarkToBase(const ScriptCache *script_cache, const OT_Looku TRACE("Mark %x(%i) and base %x(%i)\n",glyphs[glyph_index], mark_index, glyphs[base_glyph], base_index); offset = GET_BE_WORD(mbpf1->MarkArray); ma = (const GPOS_MarkArray*)((const BYTE*)mbpf1 + offset); - if (mark_index > GET_BE_WORD(ma->MarkCount)) + if (mark_index >= GET_BE_WORD(ma->MarkCount)) { ERR("Mark index exceeded mark count\n"); return -1; @@ -2042,7 +2042,7 @@ static void GPOS_apply_MarkToLigature(const OT_LookupTable *look, const SCRIPT_A TRACE("Mark %x(%i) and ligature %x(%i)\n",glyphs[glyph_index], mark_index, glyphs[glyph_index - write_dir], ligature_index); offset = GET_BE_WORD(mlpf1->MarkArray); ma = (const GPOS_MarkArray*)((const BYTE*)mlpf1 + offset); - if (mark_index > GET_BE_WORD(ma->MarkCount)) + if (mark_index >= GET_BE_WORD(ma->MarkCount)) { ERR("Mark index exceeded mark count\n"); return; @@ -2052,7 +2052,7 @@ static void GPOS_apply_MarkToLigature(const OT_LookupTable *look, const SCRIPT_A TRACE("Mark Class %i total classes %i\n",mark_class,class_count); offset = GET_BE_WORD(mlpf1->LigatureArray); la = (const GPOS_LigatureArray*)((const BYTE*)mlpf1 + offset); - if (ligature_index > GET_BE_WORD(la->LigatureCount)) + if (ligature_index >= GET_BE_WORD(la->LigatureCount)) { ERR("Ligature index exceeded ligature count\n"); return; @@ -2136,7 +2136,7 @@ static BOOL GPOS_apply_MarkToMark(const OT_LookupTable *look, const SCRIPT_ANALY TRACE("Mark %x(%i) and Mark2 %x(%i)\n",glyphs[glyph_index], mark_index, glyphs[prev_glyph_index], mark2_index); offset = GET_BE_WORD(mmpf1->Mark1Array); ma = (const GPOS_MarkArray*)((const BYTE*)mmpf1 + offset); - if (mark_index > GET_BE_WORD(ma->MarkCount)) + if (mark_index >= GET_BE_WORD(ma->MarkCount)) { ERR("Mark index exceeded mark count\n"); return FALSE; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10859
From: समीर सिंह Sameer Singh <lumarzeli30@gmail.com> --- dlls/gdi32/uniscribe/opentype.c | 45 ++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/dlls/gdi32/uniscribe/opentype.c b/dlls/gdi32/uniscribe/opentype.c index c4cd5bdbec6..634a2ca60b0 100644 --- a/dlls/gdi32/uniscribe/opentype.c +++ b/dlls/gdi32/uniscribe/opentype.c @@ -1837,9 +1837,19 @@ static int GPOS_apply_PairAdjustment(const OT_LookupTable *look, const SCRIPT_AN class2 = OT_get_glyph_class( (const BYTE *)ppf2 + GET_BE_WORD(ppf2->ClassDef2), glyphs[glyph_index + write_dir] ); if (class1 < class1_count && class2 < class2_count) { - const WORD *pair_val = ppf2->Class1Record + (class1 * class2_count + class2) * (val_fmt1_size + val_fmt2_size); + unsigned int pair_offset = (unsigned int)class1 * class2_count + class2; + unsigned int record_size = val_fmt1_size + val_fmt2_size; + const WORD *pair_val; int next = 1; + if (record_size > 0 && pair_offset > INT_MAX / record_size) + { + ERR("Integer overflow in pair value offset\n"); + return 1; + } + + pair_val = ppf2->Class1Record + (pair_offset * record_size); + TRACE( "Format 2: Found Pair %x,%x\n", glyphs[glyph_index], glyphs[glyph_index + write_dir] ); apply_pair_value(ppf2, ValueFormat1, ValueFormat2, pair_val, ppem, adjust, advance); @@ -1951,6 +1961,7 @@ static int GPOS_apply_MarkToBase(const ScriptCache *script_cache, const OT_Looku int mark_class; int class_count = GET_BE_WORD(mbpf1->ClassCount); int baserecord_size; + int baserecord_offset; POINT base_pt; POINT mark_pt; TRACE("Mark %x(%i) and base %x(%i)\n",glyphs[glyph_index], mark_index, glyphs[base_glyph], base_index); @@ -1977,7 +1988,18 @@ static int GPOS_apply_MarkToBase(const ScriptCache *script_cache, const OT_Looku return -1; } baserecord_size = class_count * sizeof(WORD); - br = (const GPOS_BaseRecord*)((const BYTE*)ba + sizeof(WORD) + (baserecord_size * base_index)); + if (baserecord_size > 0 && base_index > INT_MAX / baserecord_size) + { + ERR("Integer overflow in base record size\n"); + return -1; + } + baserecord_offset = baserecord_size * base_index; + if (baserecord_offset > INT_MAX - sizeof(WORD)) + { + ERR("Integer overflow in base record offset\n"); + return -1; + } + br = (const GPOS_BaseRecord*)((const BYTE*)ba + sizeof(WORD) + baserecord_offset); offset = GET_BE_WORD(br->BaseAnchor[mark_class]); GPOS_get_anchor_values((const BYTE*)ba + offset, &base_pt, ppem); offset = GET_BE_WORD(mr->MarkAnchor); @@ -2062,6 +2084,11 @@ static void GPOS_apply_MarkToLigature(const OT_LookupTable *look, const SCRIPT_A component_count = GET_BE_WORD(lt->ComponentCount); component_size = class_count * sizeof(WORD); + if (component_count > 0 && component_size > INT_MAX / component_count) + { + ERR("Integer overflow in component record size\n"); + return; + } offset = 0; for (i = 0; i < component_count && !offset; i++) { @@ -2131,6 +2158,7 @@ static BOOL GPOS_apply_MarkToMark(const OT_LookupTable *look, const SCRIPT_ANALY int mark_class; int class_count = GET_BE_WORD(mmpf1->ClassCount); int mark2record_size; + int mark2_offset; POINT mark2_pt; POINT mark_pt; TRACE("Mark %x(%i) and Mark2 %x(%i)\n",glyphs[glyph_index], mark_index, glyphs[prev_glyph_index], mark2_index); @@ -2157,7 +2185,18 @@ static BOOL GPOS_apply_MarkToMark(const OT_LookupTable *look, const SCRIPT_ANALY return FALSE; } mark2record_size = class_count * sizeof(WORD); - m2r = (const GPOS_Mark2Record*)((const BYTE*)m2a + sizeof(WORD) + (mark2record_size * mark2_index)); + if (mark2record_size > 0 && mark2_index > INT_MAX / mark2record_size) + { + ERR("Integer overflow in mark2 record size\n"); + return FALSE; + } + mark2_offset = mark2record_size * mark2_index; + if (mark2_offset > INT_MAX - sizeof(WORD)) + { + ERR("Integer overflow in mark2 offset\n"); + return FALSE; + } + m2r = (const GPOS_Mark2Record*)((const BYTE*)m2a + sizeof(WORD) + mark2_offset); offset = GET_BE_WORD(m2r->Mark2Anchor[mark_class]); GPOS_get_anchor_values((const BYTE*)m2a + offset, &mark2_pt, ppem); offset = GET_BE_WORD(mr->MarkAnchor); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10859
participants (2)
-
समीर सिंह Sameer Singh -
समीरसिंह Sameer Singh (@ss141309)