Signed-off-by: Vincent Povirk vincent@codeweavers.com --- dlls/gdi32/enhmetafile.c | 610 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 610 insertions(+)
diff --git a/dlls/gdi32/enhmetafile.c b/dlls/gdi32/enhmetafile.c index 2d8a9b04fb2..d8f72c98718 100644 --- a/dlls/gdi32/enhmetafile.c +++ b/dlls/gdi32/enhmetafile.c @@ -743,6 +743,604 @@ static BOOL emr_produces_output(int type) } }
+static BOOL emr_validate_size(const ENHMETARECORD *emr) +{ + switch(emr->iType) { + case EMR_HEADER: + if (emr->nSize < FIELD_OFFSET(ENHMETAHEADER, cbPixelFormat)) return FALSE; + break; + case EMR_EOF: + { + const EMREOF *lpEof = (const EMREOF *)emr; + if (emr->nSize < sizeof(EMREOF) || + (lpEof->nPalEntries * 4 / 4) != lpEof->nPalEntries || + lpEof->offPalEntries > lpEof->offPalEntries + lpEof->nPalEntries * 4 || + emr->nSize < lpEof->offPalEntries + lpEof->nPalEntries * 4) + return FALSE; + break; + } + case EMR_GDICOMMENT: + { + const EMRGDICOMMENT *lpGdiComment = (const EMRGDICOMMENT *)emr; + if (emr->nSize < FIELD_OFFSET(EMRGDICOMMENT, Data) || + lpGdiComment->cbData > lpGdiComment->cbData + FIELD_OFFSET(EMRGDICOMMENT, Data) || + emr->nSize < lpGdiComment->cbData + FIELD_OFFSET(EMRGDICOMMENT, Data)) + return FALSE; + break; + } + case EMR_SETMAPMODE: + if (emr->nSize < sizeof(EMRSETMAPMODE)) return FALSE; + break; + case EMR_SETBKMODE: + if (emr->nSize < sizeof(EMRSETBKMODE)) return FALSE; + break; + case EMR_SETBKCOLOR: + if (emr->nSize < sizeof(EMRSETBKCOLOR)) return FALSE; + break; + case EMR_SETPOLYFILLMODE: + if (emr->nSize < sizeof(EMRSETPOLYFILLMODE)) return FALSE; + break; + case EMR_SETROP2: + if (emr->nSize < sizeof(EMRSETROP2)) return FALSE; + break; + case EMR_SETTEXTALIGN: + if (emr->nSize < sizeof(EMRSETTEXTALIGN)) return FALSE; + break; + case EMR_SETTEXTCOLOR: + if (emr->nSize < sizeof(EMRSETTEXTCOLOR)) return FALSE; + break; + case EMR_SAVEDC: + if (emr->nSize < sizeof(EMRSAVEDC)) return FALSE; + break; + case EMR_RESTOREDC: + if (emr->nSize < sizeof(EMRRESTOREDC)) return FALSE; + break; + case EMR_INTERSECTCLIPRECT: + if (emr->nSize < sizeof(EMRINTERSECTCLIPRECT)) return FALSE; + break; + case EMR_SELECTOBJECT: + if (emr->nSize < sizeof(EMRSELECTOBJECT)) return FALSE; + break; + case EMR_DELETEOBJECT: + if (emr->nSize < sizeof(EMRDELETEOBJECT)) return FALSE; + break; + case EMR_SETWINDOWORGEX: + if (emr->nSize < sizeof(EMRSETWINDOWORGEX)) return FALSE; + break; + case EMR_SETWINDOWEXTEX: + if (emr->nSize < sizeof(EMRSETWINDOWEXTEX)) return FALSE; + break; + case EMR_SETVIEWPORTORGEX: + if (emr->nSize < sizeof(EMRSETVIEWPORTORGEX)) return FALSE; + break; + case EMR_SETVIEWPORTEXTEX: + if (emr->nSize < sizeof(EMRSETVIEWPORTEXTEX)) return FALSE; + break; + case EMR_CREATEPEN: + if (emr->nSize < sizeof(EMRCREATEPEN)) return FALSE; + break; + case EMR_EXTCREATEPEN: + { + const EMREXTCREATEPEN *pPen = (const EMREXTCREATEPEN *)emr; + if (emr->nSize < sizeof(EMREXTCREATEPEN) || + pPen->cbBmi > pPen->offBmi + pPen->cbBmi || + emr->nSize < pPen->offBmi + pPen->cbBmi || + pPen->cbBits > pPen->offBits + pPen->cbBits || + emr->nSize < pPen->offBits + pPen->cbBits || + (pPen->elp.elpNumEntries * 4 / 4) != pPen->elp.elpNumEntries || + pPen->elp.elpNumEntries * 4 > pPen->elp.elpNumEntries * 4 + FIELD_OFFSET(EMREXTCREATEPEN, elp.elpStyleEntry) || + emr->nSize < pPen->elp.elpNumEntries * 4 + FIELD_OFFSET(EMREXTCREATEPEN, elp.elpStyleEntry)) + return FALSE; + /* FIXME: validate DIB size */ + break; + } + case EMR_CREATEBRUSHINDIRECT: + if (emr->nSize < sizeof(EMRCREATEBRUSHINDIRECT)) return FALSE; + break; + case EMR_EXTCREATEFONTINDIRECTW: + if (emr->nSize < sizeof(EMREXTCREATEFONTINDIRECTW)) return FALSE; + break; + case EMR_MOVETOEX: + if (emr->nSize < sizeof(EMRMOVETOEX)) return FALSE; + break; + case EMR_LINETO: + if (emr->nSize < sizeof(EMRLINETO)) return FALSE; + break; + case EMR_RECTANGLE: + if (emr->nSize < sizeof(EMRRECTANGLE)) return FALSE; + break; + case EMR_ELLIPSE: + if (emr->nSize < sizeof(EMRELLIPSE)) return FALSE; + break; + case EMR_POLYGON16: + case EMR_POLYLINE16: + case EMR_POLYLINETO16: + case EMR_POLYBEZIER16: + case EMR_POLYBEZIERTO16: + { + const EMRPOLYGON16 *pPoly = (const EMRPOLYGON16 *)emr; + DWORD pts_ofs = FIELD_OFFSET(EMRPOLYGON16, apts); + if (emr->nSize < FIELD_OFFSET(EMRPOLYGON16, apts) || + (pPoly->cpts * 4 / 4) != pPoly->cpts || + pts_ofs > pts_ofs + pPoly->cpts * 4 || + emr->nSize < pts_ofs + pPoly->cpts * 4) + return FALSE; + break; + } + case EMR_POLYPOLYGON16: + case EMR_POLYPOLYLINE16: + { + const EMRPOLYPOLYGON16 *pPolyPoly = (const EMRPOLYPOLYGON16 *)emr; + DWORD counts_ofs = FIELD_OFFSET(EMRPOLYPOLYGON16, aPolyCounts); + DWORD points_ofs, total_points=0, i; + if (emr->nSize < FIELD_OFFSET(EMRPOLYPOLYGON16, aPolyCounts) || + (pPolyPoly->nPolys * 4 / 4) != pPolyPoly->nPolys || + counts_ofs > counts_ofs + pPolyPoly->nPolys * 4 || + emr->nSize < counts_ofs + pPolyPoly->nPolys * 4) + return FALSE; + points_ofs = counts_ofs + pPolyPoly->nPolys * 4; + if ((pPolyPoly->cpts * 4 / 4) != pPolyPoly->cpts || + points_ofs > points_ofs + pPolyPoly->cpts * 4 || + emr->nSize < points_ofs + pPolyPoly->cpts * 4) + return FALSE; + + for(i = 0; i < pPolyPoly->nPolys; i++) + { + if (total_points > total_points + pPolyPoly->aPolyCounts[i] || + pPolyPoly->cpts < total_points + pPolyPoly->aPolyCounts[i]) + return FALSE; + total_points += pPolyPoly->aPolyCounts[i]; + } + break; + } + case EMR_STRETCHDIBITS: + if (emr->nSize < sizeof(EMRSTRETCHDIBITS)) return FALSE; + /* FIXME: validate DIB size */ + break; + case EMR_EXTTEXTOUTA: + { + const EMREXTTEXTOUTA *pExtTextOutA = (const EMREXTTEXTOUTA *)emr; + DWORD string_ofs, dx_ofs; + if (emr->nSize < sizeof(EMREXTTEXTOUTA)) + return FALSE; + string_ofs = pExtTextOutA->emrtext.offString; + if (string_ofs > string_ofs + pExtTextOutA->emrtext.nChars || + emr->nSize < string_ofs + pExtTextOutA->emrtext.nChars) + return FALSE; + dx_ofs = pExtTextOutA->emrtext.offDx; + if (dx_ofs && ( + pExtTextOutA->emrtext.nChars * 4 / 4 != pExtTextOutA->emrtext.nChars || + dx_ofs > dx_ofs + pExtTextOutA->emrtext.nChars * 4 || + emr->nSize < dx_ofs + pExtTextOutA->emrtext.nChars * 4)) + return FALSE; + break; + } + + case EMR_EXTTEXTOUTW: + { + const EMREXTTEXTOUTW *pExtTextOutW = (const EMREXTTEXTOUTW *)emr; + DWORD string_ofs, dx_ofs; + if (emr->nSize < sizeof(EMREXTTEXTOUTW)) + return FALSE; + string_ofs = pExtTextOutW->emrtext.offString; + if (string_ofs > string_ofs + pExtTextOutW->emrtext.nChars || + pExtTextOutW->emrtext.nChars * 2 / 2 != pExtTextOutW->emrtext.nChars || + emr->nSize < string_ofs + pExtTextOutW->emrtext.nChars * 2) + return FALSE; + dx_ofs = pExtTextOutW->emrtext.offDx; + if (dx_ofs && ( + pExtTextOutW->emrtext.nChars * 4 / 4 != pExtTextOutW->emrtext.nChars || + dx_ofs > dx_ofs + pExtTextOutW->emrtext.nChars * 4 || + emr->nSize < dx_ofs + pExtTextOutW->emrtext.nChars * 4)) + return FALSE; + break; + } + + case EMR_CREATEPALETTE: + { + const EMRCREATEPALETTE *lpCreatePal = (const EMRCREATEPALETTE *)emr; + DWORD entries_ofs = FIELD_OFFSET(EMRCREATEPALETTE, lgpl.palPalEntry); + + if (emr->nSize < FIELD_OFFSET(EMRCREATEPALETTE, lgpl.palPalEntry) || + lpCreatePal->lgpl.palNumEntries * 4 / 4 != lpCreatePal->lgpl.palNumEntries || + entries_ofs > entries_ofs + lpCreatePal->lgpl.palNumEntries * 4 || + emr->nSize < entries_ofs + lpCreatePal->lgpl.palNumEntries * 4) + return FALSE; + + break; + } + + case EMR_SELECTPALETTE: + if (emr->nSize < sizeof(EMRSELECTPALETTE)) return FALSE; + break; + case EMR_REALIZEPALETTE: + if (emr->nSize < sizeof(EMRREALIZEPALETTE)) return FALSE; + break; + case EMR_EXTSELECTCLIPRGN: + { + const EMREXTSELECTCLIPRGN *lpRgn = (const EMREXTSELECTCLIPRGN *)emr; + DWORD data_ofs = FIELD_OFFSET(EMREXTSELECTCLIPRGN, RgnData); + + if (emr->nSize < FIELD_OFFSET(EMREXTSELECTCLIPRGN, RgnData) || + data_ofs > data_ofs + lpRgn->cbRgnData || + emr->nSize < data_ofs + lpRgn->cbRgnData) + return FALSE; + + break; + } + + case EMR_SETMETARGN: + if (emr->nSize < sizeof(EMRSETMETARGN)) return FALSE; + break; + case EMR_SETWORLDTRANSFORM: + if (emr->nSize < sizeof(EMRSETWORLDTRANSFORM)) return FALSE; + break; + case EMR_POLYBEZIER: + case EMR_POLYGON: + case EMR_POLYLINE: + case EMR_POLYBEZIERTO: + case EMR_POLYLINETO: + { + const EMRPOLYBEZIER *pPoly = (const EMRPOLYBEZIER *)emr; + DWORD pts_ofs = FIELD_OFFSET(EMRPOLYBEZIER, aptl); + if (emr->nSize < FIELD_OFFSET(EMRPOLYBEZIER, aptl) || + (pPoly->cptl * 8 / 8) != pPoly->cptl || + pts_ofs > pts_ofs + pPoly->cptl * 8 || + emr->nSize < pts_ofs + pPoly->cptl * 8) + return FALSE; + break; + } + case EMR_POLYPOLYLINE: + case EMR_POLYPOLYGON: + { + const EMRPOLYPOLYLINE *pPolyPoly = (const EMRPOLYPOLYLINE *)emr; + DWORD counts_ofs = FIELD_OFFSET(EMRPOLYPOLYLINE, aPolyCounts); + DWORD points_ofs, total_points=0, i; + if (emr->nSize < FIELD_OFFSET(EMRPOLYPOLYLINE, aPolyCounts) || + (pPolyPoly->nPolys * 4 / 4) != pPolyPoly->nPolys || + counts_ofs > counts_ofs + pPolyPoly->nPolys * 4 || + emr->nSize < counts_ofs + pPolyPoly->nPolys * 4) + return FALSE; + points_ofs = counts_ofs + pPolyPoly->nPolys * 4; + if ((pPolyPoly->cptl * 8 / 8) != pPolyPoly->cptl || + points_ofs > points_ofs + pPolyPoly->cptl * 8 || + emr->nSize < points_ofs + pPolyPoly->cptl * 8) + return FALSE; + + for(i = 0; i < pPolyPoly->nPolys; i++) + { + if (total_points > total_points + pPolyPoly->aPolyCounts[i] || + pPolyPoly->cptl < total_points + pPolyPoly->aPolyCounts[i]) + return FALSE; + total_points += pPolyPoly->aPolyCounts[i]; + } + break; + } + case EMR_SETBRUSHORGEX: + if (emr->nSize < sizeof(EMRSETBRUSHORGEX)) return FALSE; + break; + case EMR_SETPIXELV: + if (emr->nSize < sizeof(EMRSETPIXELV)) return FALSE; + break; + case EMR_SETMAPPERFLAGS: + if (emr->nSize < sizeof(EMRSETMAPPERFLAGS)) return FALSE; + break; + case EMR_SETCOLORADJUSTMENT: + if (emr->nSize < sizeof(EMRSETCOLORADJUSTMENT)) return FALSE; + break; + case EMR_OFFSETCLIPRGN: + if (emr->nSize < sizeof(EMROFFSETCLIPRGN)) return FALSE; + break; + case EMR_EXCLUDECLIPRECT: + if (emr->nSize < sizeof(EMREXCLUDECLIPRECT)) return FALSE; + break; + case EMR_SCALEVIEWPORTEXTEX: + if (emr->nSize < sizeof(EMRSCALEVIEWPORTEXTEX)) return FALSE; + break; + case EMR_SCALEWINDOWEXTEX: + if (emr->nSize < sizeof(EMRSCALEWINDOWEXTEX)) return FALSE; + break; + case EMR_MODIFYWORLDTRANSFORM: + if (emr->nSize < sizeof(EMRMODIFYWORLDTRANSFORM)) return FALSE; + break; + case EMR_ANGLEARC: + if (emr->nSize < sizeof(EMRANGLEARC)) return FALSE; + break; + case EMR_ROUNDRECT: + if (emr->nSize < sizeof(EMRROUNDRECT)) return FALSE; + break; + case EMR_ARC: + if (emr->nSize < sizeof(EMRARC)) return FALSE; + break; + case EMR_CHORD: + if (emr->nSize < sizeof(EMRCHORD)) return FALSE; + break; + case EMR_PIE: + if (emr->nSize < sizeof(EMRPIE)) return FALSE; + break; + case EMR_ARCTO: + if (emr->nSize < sizeof(EMRARCTO)) return FALSE; + break; + case EMR_EXTFLOODFILL: + if (emr->nSize < sizeof(EMREXTFLOODFILL)) return FALSE; + break; + case EMR_POLYDRAW: + { + const EMRPOLYDRAW *lpPolyDraw = (const EMRPOLYDRAW *)emr; + DWORD pts_ofs = FIELD_OFFSET(EMRPOLYDRAW, aptl); + if (emr->nSize < FIELD_OFFSET(EMRPOLYDRAW, aptl) || + (lpPolyDraw->cptl * 5 / 5) != lpPolyDraw->cptl || + pts_ofs > pts_ofs + lpPolyDraw->cptl * 5 || + emr->nSize < pts_ofs + lpPolyDraw->cptl * 5) + return FALSE; + break; + } + case EMR_SETARCDIRECTION: + if (emr->nSize < sizeof(EMRSETARCDIRECTION)) return FALSE; + break; + case EMR_SETMITERLIMIT: + if (emr->nSize < sizeof(EMRSETMITERLIMIT)) return FALSE; + break; + case EMR_BEGINPATH: + if (emr->nSize < sizeof(EMRBEGINPATH)) return FALSE; + break; + case EMR_ENDPATH: + if (emr->nSize < sizeof(EMRENDPATH)) return FALSE; + break; + case EMR_CLOSEFIGURE: + if (emr->nSize < sizeof(EMRCLOSEFIGURE)) return FALSE; + break; + case EMR_FILLPATH: + if (emr->nSize < sizeof(EMRFILLPATH)) return FALSE; + break; + case EMR_STROKEANDFILLPATH: + if (emr->nSize < sizeof(EMRSTROKEANDFILLPATH)) return FALSE; + break; + case EMR_STROKEPATH: + if (emr->nSize < sizeof(EMRSTROKEPATH)) return FALSE; + break; + case EMR_FLATTENPATH: + if (emr->nSize < sizeof(EMRFLATTENPATH)) return FALSE; + break; + case EMR_WIDENPATH: + if (emr->nSize < sizeof(EMRWIDENPATH)) return FALSE; + break; + case EMR_SELECTCLIPPATH: + if (emr->nSize < sizeof(EMRSELECTCLIPPATH)) return FALSE; + break; + case EMR_ABORTPATH: + if (emr->nSize < sizeof(EMRABORTPATH)) return FALSE; + break; + case EMR_CREATECOLORSPACE: + { + const EMRCREATECOLORSPACE *lpCreateColorSpace = (const EMRCREATECOLORSPACE*)emr; + DWORD i; + if (emr->nSize < FIELD_OFFSET(EMRCREATECOLORSPACE, lcs.lcsFilename)) return FALSE; + for (i=0; i<MAX_PATH; i++) + { + if (emr->nSize < FIELD_OFFSET(EMRCREATECOLORSPACE, lcs.lcsFilename[i+1])) return FALSE; + if (!lpCreateColorSpace->lcs.lcsFilename[i]) break; + } + if (i == MAX_PATH) return FALSE; + break; + } + + case EMR_SETCOLORSPACE: + if (emr->nSize < sizeof(EMRSETCOLORSPACE)) return FALSE; + break; + case EMR_DELETECOLORSPACE: + if (emr->nSize < sizeof(EMRDELETECOLORSPACE)) return FALSE; + break; + case EMR_SETICMMODE: + if (emr->nSize < sizeof(EMRSETICMMODE)) return FALSE; + break; + case EMR_PIXELFORMAT: + if (emr->nSize < sizeof(EMRPIXELFORMAT)) return FALSE; + break; + case EMR_SETPALETTEENTRIES: + { + const EMRSETPALETTEENTRIES *lpSetPaletteEntries = (const EMRSETPALETTEENTRIES *)emr; + DWORD entries_ofs = FIELD_OFFSET(EMRSETPALETTEENTRIES, aPalEntries); + + if (emr->nSize < FIELD_OFFSET(EMRSETPALETTEENTRIES, aPalEntries) || + (lpSetPaletteEntries->cEntries * 4 / 4) != lpSetPaletteEntries->cEntries || + entries_ofs > entries_ofs + lpSetPaletteEntries->cEntries * 4 || + emr->nSize < entries_ofs + lpSetPaletteEntries->cEntries * 4) + return FALSE; + + break; + } + case EMR_RESIZEPALETTE: + if (emr->nSize < sizeof(EMRRESIZEPALETTE)) return FALSE; + break; + case EMR_CREATEDIBPATTERNBRUSHPT: + if (emr->nSize < sizeof(EMRCREATEDIBPATTERNBRUSHPT)) return FALSE; + /* FIXME: validate dib size */ + break; + case EMR_CREATEMONOBRUSH: + if (emr->nSize < sizeof(EMRCREATEMONOBRUSH)) return FALSE; + /* FIXME: validate dib size */ + break; + case EMR_BITBLT: + if (emr->nSize < sizeof(EMRBITBLT)) return FALSE; + /* FIXME: validate dib size */ + break; + case EMR_STRETCHBLT: + if (emr->nSize < sizeof(EMRSTRETCHBLT)) return FALSE; + /* FIXME: validate dib size */ + break; + case EMR_ALPHABLEND: + if (emr->nSize < sizeof(EMRALPHABLEND)) return FALSE; + /* FIXME: validate dib size */ + break; + case EMR_MASKBLT: + if (emr->nSize < sizeof(EMRMASKBLT)) return FALSE; + /* FIXME: validate dib size */ + break; + case EMR_PLGBLT: + { + const EMRPLGBLT *pPlgBlt = (const EMRPLGBLT *)emr; + if (emr->nSize < sizeof(EMRPLGBLT) || + pPlgBlt->offBmiSrc > pPlgBlt->offBmiSrc + pPlgBlt->cbBmiSrc || + emr->nSize < pPlgBlt->offBmiSrc + pPlgBlt->cbBmiSrc || + pPlgBlt->offBitsSrc > pPlgBlt->offBitsSrc + pPlgBlt->cbBitsSrc || + emr->nSize < pPlgBlt->offBitsSrc + pPlgBlt->cbBitsSrc || + pPlgBlt->offBmiMask > pPlgBlt->offBmiMask + pPlgBlt->cbBmiMask || + emr->nSize < pPlgBlt->offBmiMask + pPlgBlt->cbBmiMask || + pPlgBlt->offBitsMask > pPlgBlt->offBitsMask + pPlgBlt->cbBitsMask || + emr->nSize < pPlgBlt->offBitsMask + pPlgBlt->cbBitsMask) + return FALSE; + /* FIXME: validate dib size */ + break; + } + + case EMR_SETDIBITSTODEVICE: + { + const EMRSETDIBITSTODEVICE *pSetDIBitsToDevice = (const EMRSETDIBITSTODEVICE *)emr; + if (emr->nSize < sizeof(EMRSETDIBITSTODEVICE) || + pSetDIBitsToDevice->offBmiSrc > pSetDIBitsToDevice->offBmiSrc + pSetDIBitsToDevice->cbBmiSrc || + emr->nSize < pSetDIBitsToDevice->offBmiSrc + pSetDIBitsToDevice->cbBmiSrc || + pSetDIBitsToDevice->offBitsSrc > pSetDIBitsToDevice->offBitsSrc + pSetDIBitsToDevice->cbBitsSrc || + emr->nSize < pSetDIBitsToDevice->offBitsSrc + pSetDIBitsToDevice->cbBitsSrc) + return FALSE; + break; + } + + case EMR_POLYTEXTOUTA: + { + const EMRPOLYTEXTOUTA *pPolyTextOutA = (const EMRPOLYTEXTOUTA *)emr; + int ofs_text = FIELD_OFFSET(EMRPOLYTEXTOUTA, aemrtext); + DWORD i; + if (emr->nSize < ofs_text || + pPolyTextOutA->cStrings * sizeof(EMRTEXT) / sizeof(EMRTEXT) != pPolyTextOutA->cStrings || + ofs_text < ofs_text + pPolyTextOutA->cStrings * sizeof(EMRTEXT) || + emr->nSize < ofs_text + pPolyTextOutA->cStrings * sizeof(EMRTEXT)) + return FALSE; + for (i=0; i < pPolyTextOutA->cStrings; i++) + { + const EMRTEXT *text = &pPolyTextOutA->aemrtext[i]; + DWORD string_ofs, dx_ofs; + string_ofs = text->offString; + if (string_ofs > string_ofs + text->nChars || + emr->nSize < string_ofs + text->nChars) + return FALSE; + dx_ofs = text->offDx; + if (dx_ofs && ( + text->nChars * 4 / 4 != text->nChars || + dx_ofs > dx_ofs + text->nChars * 4 || + emr->nSize < dx_ofs + text->nChars * 4)) + return FALSE; + } + break; + } + + case EMR_POLYTEXTOUTW: + { + const EMRPOLYTEXTOUTW *pPolyTextOutW = (const EMRPOLYTEXTOUTW *)emr; + DWORD ofs_text = FIELD_OFFSET(EMRPOLYTEXTOUTW, aemrtext); + DWORD i; + if (emr->nSize < ofs_text || + pPolyTextOutW->cStrings * sizeof(EMRTEXT) / sizeof(EMRTEXT) != pPolyTextOutW->cStrings || + ofs_text < ofs_text + pPolyTextOutW->cStrings * sizeof(EMRTEXT) || + emr->nSize < ofs_text + pPolyTextOutW->cStrings * sizeof(EMRTEXT)) + return FALSE; + for (i=0; i < pPolyTextOutW->cStrings; i++) + { + const EMRTEXT *text = &pPolyTextOutW->aemrtext[i]; + DWORD string_ofs, dx_ofs; + string_ofs = text->offString; + if (text->nChars * 2 / 2 != text->nChars || + string_ofs > string_ofs + text->nChars * 2 || + emr->nSize < string_ofs + text->nChars * 2) + return FALSE; + dx_ofs = text->offDx; + if (dx_ofs && ( + text->nChars * 4 / 4 != text->nChars || + dx_ofs > dx_ofs + text->nChars * 4 || + emr->nSize < dx_ofs + text->nChars * 4)) + return FALSE; + } + break; + } + + case EMR_FILLRGN: + { + const EMRFILLRGN *pFillRgn = (const EMRFILLRGN *)emr; + DWORD ofs_data = FIELD_OFFSET(EMRFILLRGN, RgnData); + if (emr->nSize < ofs_data || + ofs_data > ofs_data + pFillRgn->cbRgnData || + emr->nSize < ofs_data + pFillRgn->cbRgnData) + return FALSE; + break; + } + + case EMR_FRAMERGN: + { + const EMRFRAMERGN *pFrameRgn = (const EMRFRAMERGN *)emr; + DWORD ofs_data = FIELD_OFFSET(EMRFRAMERGN, RgnData); + if (emr->nSize < ofs_data || + ofs_data > ofs_data + pFrameRgn->cbRgnData || + emr->nSize < ofs_data + pFrameRgn->cbRgnData) + return FALSE; + break; + } + + case EMR_INVERTRGN: + case EMR_PAINTRGN: + { + const EMRINVERTRGN *pInvertRgn = (const EMRINVERTRGN *)emr; + DWORD ofs_data = FIELD_OFFSET(EMRINVERTRGN, RgnData); + if (emr->nSize < ofs_data || + ofs_data > ofs_data + pInvertRgn->cbRgnData || + emr->nSize < ofs_data + pInvertRgn->cbRgnData) + return FALSE; + break; + } + + case EMR_SETTEXTJUSTIFICATION: + { + if (emr->nSize < sizeof(EMRSETTEXTJUSTIFICATION)) return FALSE; + break; + } + + case EMR_SETLAYOUT: + { + if (emr->nSize < sizeof(EMRSETLAYOUT)) return FALSE; + break; + } + + case EMR_GRADIENTFILL: + { + EMRGRADIENTFILL *grad = (EMRGRADIENTFILL *)emr; + DWORD vertex_ofs, mesh_item_size, mesh_ofs; + vertex_ofs = FIELD_OFFSET(EMRGRADIENTFILL, Ver); + if (emr->nSize < vertex_ofs) return FALSE; + + mesh_ofs = vertex_ofs + grad->nVer * sizeof(TRIVERTEX); + if (grad->nVer * sizeof(TRIVERTEX) / sizeof(TRIVERTEX) != grad->nVer || + vertex_ofs > mesh_ofs || + emr->nSize < mesh_ofs) + return FALSE; + + if (grad->ulMode == GRADIENT_FILL_TRIANGLE) + mesh_item_size = sizeof(GRADIENT_TRIANGLE); + else + mesh_item_size = sizeof(GRADIENT_RECT); + + if (grad->nTri * mesh_item_size / mesh_item_size != grad->nTri || + mesh_ofs > mesh_ofs + grad->nTri * mesh_item_size || + emr->nSize < mesh_ofs + grad->nTri * mesh_item_size) + return FALSE; + + break; + } + + default: + break; + } + + return TRUE; +}
/***************************************************************************** * PlayEnhMetaFileRecord (GDI32.@) @@ -774,6 +1372,12 @@ BOOL WINAPI PlayEnhMetaFileRecord( hdc, handletable, mr, handles); if (!mr) return FALSE;
+ if (!emr_validate_size(mr)) + { + ERR("record has insufficient size\n"); + return FALSE; + } + type = mr->iType;
TRACE("record %s\n", get_emr_name(type)); @@ -2478,6 +3082,12 @@ BOOL WINAPI EnumEnhMetaFile( break; }
+ if (!emr_validate_size(emr)) + { + ERR("record has insufficient size\n"); + break; + } + /* In Win9x mode we update the xform if the record will produce output */ if (hdc && IS_WIN9X() && emr_produces_output(emr->iType)) EMF_Update_MF_Xform(hdc, info);
On Wed, May 16, 2018 at 11:29:21AM -0500, Vincent Povirk wrote:
Signed-off-by: Vincent Povirk vincent@codeweavers.com
dlls/gdi32/enhmetafile.c | 610 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 610 insertions(+)
diff --git a/dlls/gdi32/enhmetafile.c b/dlls/gdi32/enhmetafile.c index 2d8a9b04fb2..d8f72c98718 100644 --- a/dlls/gdi32/enhmetafile.c +++ b/dlls/gdi32/enhmetafile.c @@ -743,6 +743,604 @@ static BOOL emr_produces_output(int type) } }
+static BOOL emr_validate_size(const ENHMETARECORD *emr) +{
- switch(emr->iType) {
- case EMR_HEADER:
if (emr->nSize < FIELD_OFFSET(ENHMETAHEADER, cbPixelFormat)) return FALSE;
break;
- case EMR_EOF:
- {
const EMREOF *lpEof = (const EMREOF *)emr;
if (emr->nSize < sizeof(EMREOF) ||
(lpEof->nPalEntries * 4 / 4) != lpEof->nPalEntries ||
lpEof->offPalEntries > lpEof->offPalEntries + lpEof->nPalEntries * 4 ||
emr->nSize < lpEof->offPalEntries + lpEof->nPalEntries * 4)
return FALSE;
break;
- }
- case EMR_GDICOMMENT:
- {
const EMRGDICOMMENT *lpGdiComment = (const EMRGDICOMMENT *)emr;
if (emr->nSize < FIELD_OFFSET(EMRGDICOMMENT, Data) ||
lpGdiComment->cbData > lpGdiComment->cbData + FIELD_OFFSET(EMRGDICOMMENT, Data) ||
emr->nSize < lpGdiComment->cbData + FIELD_OFFSET(EMRGDICOMMENT, Data))
return FALSE;
break;
- }
- case EMR_SETMAPMODE:
if (emr->nSize < sizeof(EMRSETMAPMODE)) return FALSE;
break;
- case EMR_SETBKMODE:
if (emr->nSize < sizeof(EMRSETBKMODE)) return FALSE;
break;
- case EMR_SETBKCOLOR:
if (emr->nSize < sizeof(EMRSETBKCOLOR)) return FALSE;
break;
Since there are so many fixed-size records, it would most likely be cleaner to use a lookup table, storing zero (or something) for the variable-size records, then handle those remaining ones in a switch.
Please issue a FIXME() on unhandled record types - I'm ok with a FIXME comment for partially implemented ones, but unknown record types should get a FIXME().
Also, please use 4-space indents.
Huw.