[PATCH 1/3] gdi32: Add bounds checking for metafile records.

Vincent Povirk vincent at codeweavers.com
Wed May 16 11:29:21 CDT 2018


Signed-off-by: Vincent Povirk <vincent at 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);
-- 
2.14.1




More information about the wine-devel mailing list