[WIN32K] Fix probing and parameter validation in NtGdiPolyPolyDraw
[reactos.git] / reactos / win32ss / gdi / ntgdi / fillshap.c
index 95f607c..e20f199 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * PROJECT:         ReactOS win32 kernel mode subsystem
  * LICENSE:         GPL - See COPYING in the top level directory
- * FILE:            subsystems/win32/win32k/objects/fillshap.c
+ * FILE:            win32ss/gdi/ntgdi/fillshap.c
  * PURPOSE:         fillshap
  * PROGRAMMER:
  */
@@ -81,8 +81,11 @@ IntGdiPolygon(PDC    dc,
         pbrFill = dc->dclevel.pbrFill;
         pbrLine = dc->dclevel.pbrLine;
         psurf = dc->dclevel.pSurface;
-        /* FIXME: psurf can be NULL!!!! don't assert but handle this case gracefully! */
-        ASSERT(psurf);
+        if (psurf == NULL)
+        {
+            /* Memory DC without a bitmap selected, nothing to do. */
+            return TRUE;
+        }
 
         /* Now fill the polygon with the current fill brush. */
         if (!(pbrFill->flAttrs & BR_IS_NULL))
@@ -112,7 +115,7 @@ IntGdiPolygon(PDC    dc,
 //                                 Points[1].x, Points[1].y );
 
                 ret = IntEngLineTo(&psurf->SurfObj,
-                                   &dc->co.ClipObj,
+                                   (CLIPOBJ *)&dc->co,
                                    &dc->eboLine.BrushObject,
                                    Points[i].x,          /* From */
                                    Points[i].y,
@@ -126,7 +129,7 @@ IntGdiPolygon(PDC    dc,
             if (ret)
             {
                 ret = IntEngLineTo(&psurf->SurfObj,
-                                   &dc->co.ClipObj,
+                                   (CLIPOBJ *)&dc->co,
                                    &dc->eboLine.BrushObject,
                                    Points[Count-1].x, /* From */
                                    Points[Count-1].y,
@@ -162,13 +165,20 @@ IntGdiPolyPolygon(DC      *dc,
 BOOL FASTCALL
 IntPolygon(HDC hdc, POINT *Point, int Count)
 {
-   PDC dc;
-   if (!(dc = DC_LockDc(hdc)))
-   {
-      EngSetLastError(ERROR_INVALID_HANDLE);
-      return FALSE;
-   }
-   return IntGdiPolygon(dc, Point, Count);
+    BOOL bResult;
+    PDC pdc;
+
+    pdc = DC_LockDc(hdc);
+    if (pdc == NULL)
+    {
+        EngSetLastError(ERROR_INVALID_HANDLE);
+        return FALSE;
+    }
+
+    bResult = IntGdiPolygon(pdc, Point, Count);
+
+    DC_UnlockDc(pdc);
+    return bResult;
 }
 
 
@@ -204,20 +214,12 @@ NtGdiEllipse(
     PBRUSH pFillBrushObj;
     BRUSH tmpFillBrushObj;
 
-    if ((Left == Right) || (Top == Bottom)) return TRUE;
-
     dc = DC_LockDc(hDC);
     if (dc == NULL)
     {
        EngSetLastError(ERROR_INVALID_HANDLE);
        return FALSE;
     }
-    if (dc->dctype == DC_TYPE_INFO)
-    {
-       DC_UnlockDc(dc);
-       /* Yes, Windows really returns TRUE in this case */
-       return TRUE;
-    }
 
     if (PATH_IsPathOpen(dc->dclevel))
     {
@@ -226,6 +228,15 @@ NtGdiEllipse(
         return ret;
     }
 
+    ////
+    //// Could this use PATH_CheckCorners ?
+    ////
+    if ((Left == Right) || (Top == Bottom))
+    {
+       DC_UnlockDc(dc);
+       return TRUE;
+    }
+
     if (Right < Left)
     {
        INT tmp = Right; Right = Left; Left = tmp;
@@ -234,6 +245,7 @@ NtGdiEllipse(
     {
        INT tmp = Bottom; Bottom = Top; Top = tmp;
     }
+    ////
 
     pdcattr = dc->pdcattr;
 
@@ -371,56 +383,56 @@ NtGdiPolyPolyDraw( IN HDC hDC,
     PVOID pTemp;
     LPPOINT SafePoints;
     PULONG SafeCounts;
-    NTSTATUS Status = STATUS_SUCCESS;
+    NTSTATUS Status;
     BOOL Ret = TRUE;
-    ULONG nPoints = 0, nMaxPoints = 0, nInvalid = 0, i;
+    ULONG nPoints = 0, nMaxPoints = 0, i;
 
-    if (!UnsafePoints || !UnsafeCounts ||
-        Count == 0 || iFunc == 0 || iFunc > GdiPolyPolyRgn)
+    /* Validate parameters */
+    if ((UnsafePoints == NULL) ||
+        (UnsafeCounts == NULL) ||
+        (Count == 0) ||
+        (Count > ULONG_MAX / sizeof(ULONG)) ||
+        (iFunc == 0) ||
+        (iFunc > GdiPolyPolyRgn))
     {
+        DPRINT1("NtGdiPolyPolyDraw - Invalid parameter!\n");
         /* Windows doesn't set last error */
         return FALSE;
     }
 
     _SEH2_TRY
     {
-        ProbeForRead(UnsafePoints, Count * sizeof(POINT), 1);
+        /* Probe the buffer of counts for each polygon */
         ProbeForRead(UnsafeCounts, Count * sizeof(ULONG), 1);
 
-        /* Count points and validate poligons */
+        /* Count points. Note: We are not copying the buffer, so it can be
+           changed by usermode. This is ok, since the content is validated
+           again later. */
         for (i = 0; i < Count; i++)
         {
-            if (UnsafeCounts[i] < 2)
+            Status = RtlULongAdd(nMaxPoints, UnsafeCounts[i], &nMaxPoints);
+            if (!NT_SUCCESS(Status))
             {
-                nInvalid++;
+                DPRINT1("Overflow when counting points!\n");
+                return FALSE;
             }
-            nPoints += UnsafeCounts[i];
-            nMaxPoints = max(nMaxPoints, UnsafeCounts[i]);
         }
+
+        ProbeForRead(UnsafePoints, nMaxPoints * sizeof(POINT), 1);
     }
     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
     {
-        Status = _SEH2_GetExceptionCode();
-    }
-    _SEH2_END;
-
-    if (!NT_SUCCESS(Status))
-    {
+        DPRINT1("Got exception!\n");
         /* Windows doesn't set last error */
         return FALSE;
     }
+    _SEH2_END;
 
-    if (nPoints == 0 || nPoints < nMaxPoints)
-    {
-        /* If all polygon counts are zero, or we have overflow,
-           return without setting a last error code. */
-        return FALSE;
-    }
-
-    if (nInvalid != 0)
+    if (nMaxPoints == 0)
     {
-        /* If at least one poly count is 0 or 1, fail */
-        EngSetLastError(ERROR_INVALID_PARAMETER);
+        /* If all polygon counts are zero, return FALSE
+           without setting a last error code. */
+        DPRINT1("nMaxPoints == 0!\n");
         return FALSE;
     }
 
@@ -430,12 +442,16 @@ NtGdiPolyPolyDraw( IN HDC hDC,
                                   TAG_SHAPE);
     if (!pTemp)
     {
+        DPRINT1("Failed to allocate %lu bytes (Count = %lu, nPoints = %u).\n",
+                Count * sizeof(ULONG) + nPoints * sizeof(POINT),
+                Count,
+                nPoints);
         EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
         return FALSE;
     }
 
     SafeCounts = pTemp;
-    SafePoints = (PVOID)(SafeCounts + Count);
+    SafePoints = (PPOINT)&SafeCounts[Count];
 
     _SEH2_TRY
     {
@@ -445,12 +461,37 @@ NtGdiPolyPolyDraw( IN HDC hDC,
     }
     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
     {
-        Status = _SEH2_GetExceptionCode();
+        DPRINT1("Got exception!\n");
+        ExFreePoolWithTag(pTemp, TAG_SHAPE);
+        return FALSE;
     }
     _SEH2_END;
 
-    if (!NT_SUCCESS(Status))
+    /* Now that the buffers are copied, validate them again */
+    for (i = 0; i < Count; i++)
     {
+        /* If any poly count is 0 or 1, fail */
+        if (SafeCounts[i] < 2)
+        {
+            DPRINT1("Invalid: UnsafeCounts[%lu] == %lu\n", i, SafeCounts[i]);
+            ExFreePoolWithTag(pTemp, TAG_SHAPE);
+            EngSetLastError(ERROR_INVALID_PARAMETER);
+            return FALSE;
+        }
+
+        Status = RtlULongAdd(nPoints, SafeCounts[i], &nPoints);
+        if (!NT_SUCCESS(Status))
+        {
+            DPRINT1("Overflow when counting points!\n");
+            return FALSE;
+        }
+    }
+
+    /* If the 2nd count does not match the 1st, someone changed the buffer
+       behind our back! */
+    if (nPoints != nMaxPoints)
+    {
+        DPRINT1("Polygon count mismatch: %lu != %lu\n", nPoints, nMaxPoints);
         ExFreePoolWithTag(pTemp, TAG_SHAPE);
         return FALSE;
     }
@@ -475,14 +516,6 @@ NtGdiPolyPolyDraw( IN HDC hDC,
         return FALSE;
     }
 
-    if (dc->dctype == DC_TYPE_INFO)
-    {
-        DC_UnlockDc(dc);
-        ExFreePoolWithTag(pTemp, TAG_SHAPE);
-        /* Yes, Windows really returns TRUE in this case */
-        return TRUE;
-    }
-
     DC_vPrepareDCsForBlit(dc, NULL, NULL, NULL);
 
     if (dc->pdcattr->ulDirty_ & (DIRTY_FILL | DC_BRUSH_DIRTY))
@@ -562,6 +595,11 @@ IntRectangle(PDC dc,
     DestRect.top    += dc->ptlDCOrig.y;
     DestRect.bottom += dc->ptlDCOrig.y;
 
+    if (dc->fs & (DC_ACCUM_APP|DC_ACCUM_WMGR))
+    {
+       IntUpdateBoundsRect(dc, &DestRect);
+    }
+
     /* In GM_COMPATIBLE, don't include bottom and right edges */
     if (pdcattr->iGraphicsMode == GM_COMPATIBLE)
     {
@@ -588,7 +626,7 @@ IntRectangle(PDC dc,
     psurf = dc->dclevel.pSurface;
     if (!psurf)
     {
-        ret = FALSE;
+        ret = TRUE;
         goto cleanup;
     }
 
@@ -602,7 +640,7 @@ IntRectangle(PDC dc,
             ret = IntEngBitBlt(&psurf->SurfObj,
                                NULL,
                                NULL,
-                               &dc->co.ClipObj,
+                               (CLIPOBJ *)&dc->co,
                                NULL,
                                &DestRect,
                                NULL,
@@ -621,28 +659,28 @@ IntRectangle(PDC dc,
     {
         Mix = ROP2_TO_MIX(pdcattr->jROP2);
         ret = ret && IntEngLineTo(&psurf->SurfObj,
-                                  &dc->co.ClipObj,
+                                  (CLIPOBJ *)&dc->co,
                                   &dc->eboLine.BrushObject,
                                   DestRect.left, DestRect.top, DestRect.right, DestRect.top,
                                   &DestRect, // Bounding rectangle
                                   Mix);
 
         ret = ret && IntEngLineTo(&psurf->SurfObj,
-                                  &dc->co.ClipObj,
+                                  (CLIPOBJ *)&dc->co,
                                   &dc->eboLine.BrushObject,
                                   DestRect.right, DestRect.top, DestRect.right, DestRect.bottom,
                                   &DestRect, // Bounding rectangle
                                   Mix);
 
         ret = ret && IntEngLineTo(&psurf->SurfObj,
-                                  &dc->co.ClipObj,
+                                  (CLIPOBJ *)&dc->co,
                                   &dc->eboLine.BrushObject,
                                   DestRect.right, DestRect.bottom, DestRect.left, DestRect.bottom,
                                   &DestRect, // Bounding rectangle
                                   Mix);
 
         ret = ret && IntEngLineTo(&psurf->SurfObj,
-                                  &dc->co.ClipObj,
+                                  (CLIPOBJ *)&dc->co,
                                   &dc->eboLine.BrushObject,
                                   DestRect.left, DestRect.bottom, DestRect.left, DestRect.top,
                                   &DestRect, // Bounding rectangle
@@ -675,12 +713,6 @@ NtGdiRectangle(HDC  hDC,
         EngSetLastError(ERROR_INVALID_HANDLE);
         return FALSE;
     }
-    if (dc->dctype == DC_TYPE_INFO)
-    {
-        DC_UnlockDc(dc);
-        /* Yes, Windows really returns TRUE in this case */
-        return TRUE;
-    }
 
     /* Do we rotate or shear? */
     if (!(dc->pdcattr->mxWorldToDevice.flAccel & XFORM_SCALE))
@@ -854,12 +886,6 @@ NtGdiRoundRect(
         DPRINT1("NtGdiRoundRect() - hDC is invalid\n");
         EngSetLastError(ERROR_INVALID_HANDLE);
     }
-    else if (dc->dctype == DC_TYPE_INFO)
-    {
-        DC_UnlockDc(dc);
-        /* Yes, Windows really returns TRUE in this case */
-        ret = TRUE;
-    }
     else
     {
         ret = IntRoundRect ( dc, LeftRect, TopRect, RightRect, BottomRect, Width, Height );
@@ -924,13 +950,6 @@ GreGradientFill(
         return FALSE;
     }
 
-    if(pdc->dctype == DC_TYPE_INFO)
-    {
-        DC_UnlockDc(pdc);
-        /* Yes, Windows really returns TRUE in this case */
-        return TRUE;
-    }
-
     if (!pdc->dclevel.pSurface)
     {
         /* Memory DC with no surface selected */
@@ -961,6 +980,11 @@ GreGradientFill(
     ptlDitherOrg.x += pdc->ptlDCOrig.x;
     ptlDitherOrg.y += pdc->ptlDCOrig.y;
 
+   if (pdc->fs & (DC_ACCUM_APP|DC_ACCUM_WMGR))
+   {
+      IntUpdateBoundsRect(pdc, &rclExtent);
+   }
+
     DC_vPrepareDCsForBlit(pdc, &rclExtent, NULL, NULL);
 
     psurf = pdc->dclevel.pSurface;
@@ -968,7 +992,7 @@ GreGradientFill(
     EXLATEOBJ_vInitialize(&exlo, &gpalRGB, psurf->ppal, 0, 0, 0);
 
     bRet = IntEngGradientFill(&psurf->SurfObj,
-                             &pdc->co.ClipObj,
+                             (CLIPOBJ *)&pdc->co,
                              &exlo.xlo,
                              pVertex,
                              nVertex,
@@ -1087,16 +1111,10 @@ NtGdiExtFloodFill(
         EngSetLastError(ERROR_INVALID_HANDLE);
         return FALSE;
     }
-    if (dc->dctype == DC_TYPE_INFO)
-    {
-        DC_UnlockDc(dc);
-        /* Yes, Windows really returns TRUE in this case */
-        return TRUE;
-    }
 
     if (!dc->dclevel.pSurface)
     {
-        Ret = FALSE;
+        Ret = TRUE;
         goto cleanup;
     }
 
@@ -1128,6 +1146,11 @@ NtGdiExtFloodFill(
         RECTL_vSetRect(&DestRect, 0, 0, psurf->SurfObj.sizlBitmap.cx, psurf->SurfObj.sizlBitmap.cy);
     }
 
+    if (dc->fs & (DC_ACCUM_APP|DC_ACCUM_WMGR))
+    {
+       IntUpdateBoundsRect(dc, &DestRect);
+    }
+
     EXLATEOBJ_vInitialize(&exlo, &gpalRGB, psurf->ppal, 0, 0xffffff, 0);
 
     /* Only solid fills supported for now