[0.4.10][WIN32K] Revert NtGdiStretchDIBitsInternal to Previous Logic (#3774)
authorJoachim Henze <Joachim.Henze@reactos.org>
Sat, 15 Jan 2022 00:30:14 +0000 (01:30 +0100)
committerJoachim Henze <Joachim.Henze@reactos.org>
Sat, 15 Jan 2022 00:30:14 +0000 (01:30 +0100)
Fixes gdi32:dib / gdi32:bitmap tests and

CORE-16236 "SIMS graphics", which regressed by
SVN 51005 == git 2bbd8711a747e88759edcff139e64e348a79a5a6

Thanks to the patches author Doug Lyons.

It also fixed
CORE-16621 Kompozer 0.7.10 from rapps, exe does not show an icon, but does on 2k3sp2

fix picked from 0.4.15-dev-2850-g 847b037fe98ec14bdcea2cd9c3d356fecaa895cc
--------------
By taking the above, we also have to pick its addendum
[WIN32K] Fix 'use after free' in NtGdiStretchDIBitsInternal #4122 CORE-17861

0.4.15-dev-3420-g b538b9abb8c5bb834f855173fb77459456382d56
to avoid introducing another bug:
CORE-17861 MS Visual Studio 2010 Pro Fatal System Error 0x00000050
--------------
[WIN32K] Don't trust the BITMAPINFOHEADER size

This is not entirely correct yet, but less wrong than before and fixes
CORE-16031 Moo0 Audio Recorder - ReactOS icons are not displayed in the system tray
fix picked from 0.4.14-dev-848-g 53c9a6deaa61091f5bf681483cd3f36cc388c51b

win32ss/gdi/ntgdi/dibobj.c

index 0453aae..cbe6f2d 100644 (file)
@@ -1109,7 +1109,7 @@ NtGdiGetDIBitsInternal(
     }
     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
     {
-        _SEH2_YIELD(goto cleanup;)
+        goto cleanup;
     }
     _SEH2_END;
 
@@ -1190,7 +1190,6 @@ NtGdiStretchDIBitsInternal(
     IN UINT cjMaxBits,
     IN HANDLE hcmXform)
 {
-    BOOL bResult = FALSE;
     SIZEL sizel;
     RECTL rcSrc, rcDst;
     PDC pdc;
@@ -1198,7 +1197,69 @@ NtGdiStretchDIBitsInternal(
     PSURFACE psurfTmp = 0, psurfDst = 0;
     PPALETTE ppalDIB = 0;
     EXLATEOBJ exlo;
-    PVOID pvBits;
+    PBYTE pvBits;
+
+    LPBITMAPINFO pbmiSafe;
+    UINT cjAlloc;
+    HBITMAP hBitmap, hOldBitmap = NULL;
+    HDC hdcMem;
+    HPALETTE hPal = NULL;
+    ULONG BmpFormat = 0;
+    INT LinesCopied = 0;
+
+    /* Check for bad iUsage */
+    if (dwUsage > 2) return 0;
+
+    /* We must have LPBITMAPINFO */
+    if (!pbmi)
+    {
+        DPRINT1("Error, Invalid Parameter.\n");
+        EngSetLastError(ERROR_INVALID_PARAMETER);
+        return 0;
+    }
+
+    /* Check if the size of the bitmap info is large enough */
+    if (cjMaxInfo < sizeof(BITMAPCOREHEADER))
+    {
+        return 0;
+    }
+
+    /* Use maximum size */
+    cjMaxInfo = min(cjMaxInfo, sizeof(BITMAPV5HEADER) + 256 * sizeof(RGBQUAD));
+
+    // HACK: the underlying code sucks and doesn't care for the size, so we
+    // give it the maximum ever needed
+    cjAlloc = sizeof(BITMAPV5HEADER) + 256 * sizeof(RGBQUAD);
+
+    /* Allocate a buffer the bitmapinfo */
+    pbmiSafe = ExAllocatePoolWithTag(PagedPool, cjAlloc, 'imBG');
+    if (!pbmiSafe)
+    {
+        /* Fail */
+        return 0;
+    }
+
+    /* Use SEH */
+    _SEH2_TRY
+    {
+        /* Probe and copy the BITMAPINFO */
+        ProbeForRead(pbmi, cjMaxInfo, 1);
+        RtlCopyMemory(pbmiSafe, pbmi, cjMaxInfo);
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        ExFreePoolWithTag(pbmiSafe, 'imBG');
+        return 0;
+    }
+    _SEH2_END;
+
+    /* Check if the header size is large enough */
+    if ((pbmiSafe->bmiHeader.biSize < sizeof(BITMAPCOREHEADER)) ||
+        (pbmiSafe->bmiHeader.biSize > cjMaxInfo))
+    {
+        ExFreePoolWithTag(pbmiSafe, 'imBG');
+        return 0;
+    }
 
     if (!(pdc = DC_LockDc(hdc)))
     {
@@ -1220,31 +1281,9 @@ NtGdiStretchDIBitsInternal(
     IntLPtoDP(pdc, (POINTL*)&sizel, 1);
     DC_UnlockDc(pdc);
 
-    /* Check if we can use NtGdiSetDIBitsToDeviceInternal */
-    if ((sizel.cx == cxSrc) && (sizel.cy == cySrc) && (dwRop == SRCCOPY))
-    {
-        /* Yes, we can! */
-        return NtGdiSetDIBitsToDeviceInternal(hdc,
-                                              xDst,
-                                              yDst,
-                                              cxDst,
-                                              cyDst,
-                                              xSrc,
-                                              ySrc,
-                                              0,
-                                              cySrc,
-                                              pjInit,
-                                              pbmi,
-                                              dwUsage,
-                                              cjMaxBits,
-                                              cjMaxInfo,
-                                              TRUE,
-                                              hcmXform);
-    }
-
     if (pjInit && (cjMaxBits > 0))
     {
-        pvBits = ExAllocatePoolWithTag(PagedPool, cjMaxBits, 'pmeT');
+        pvBits = ExAllocatePoolWithTag(PagedPool, cjMaxBits, TAG_DIB);
         if (!pvBits)
         {
             return 0;
@@ -1257,8 +1296,8 @@ NtGdiStretchDIBitsInternal(
         }
         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
         {
-            ExFreePoolWithTag(pvBits, 'pmeT');
-            _SEH2_YIELD(return 0);
+            ExFreePoolWithTag(pvBits, TAG_DIB);
+            return 0;
         }
         _SEH2_END;
     }
@@ -1267,100 +1306,185 @@ NtGdiStretchDIBitsInternal(
         pvBits = NULL;
     }
 
-    /* FIXME: Locking twice is cheesy, coord tranlation in UM will fix it */
-    if (!(pdc = DC_LockDc(hdc)))
+    /* Here we select between the dwRop with SRCCOPY or not. */
+    if (dwRop == SRCCOPY)
     {
-        DPRINT1("Could not lock dc\n");
-        EngSetLastError(ERROR_INVALID_HANDLE);
-        goto cleanup;
-    }
+        hdcMem = NtGdiCreateCompatibleDC(hdc);
+        if (hdcMem == NULL)
+        {
+            DPRINT1("NtGdiCreateCompatibleDC failed to create hdc.\n");
+            EngSetLastError(ERROR_NO_SYSTEM_RESOURCES);
+            return 0;
+        }
 
-    /* Calculate source and destination rect */
-    rcSrc.left = xSrc;
-    rcSrc.top = ySrc;
-    rcSrc.right = xSrc + abs(cxSrc);
-    rcSrc.bottom = ySrc + abs(cySrc);
-    rcDst.left = xDst;
-    rcDst.top = yDst;
-    rcDst.right = rcDst.left + cxDst;
-    rcDst.bottom = rcDst.top + cyDst;
-    IntLPtoDP(pdc, (POINTL*)&rcDst, 2);
-    RECTL_vOffsetRect(&rcDst, pdc->ptlDCOrig.x, pdc->ptlDCOrig.y);
+        hBitmap = NtGdiCreateCompatibleBitmap(hdc,
+                                              abs(pbmiSafe->bmiHeader.biWidth),
+                                              abs(pbmiSafe->bmiHeader.biHeight));
+        if (hBitmap == NULL)
+        {
+            DPRINT1("NtGdiCreateCompatibleBitmap failed to create bitmap.\n");
+            DPRINT1("hdc : 0x%08x \n", hdc);
+            DPRINT1("width : 0x%08x \n", pbmiSafe->bmiHeader.biWidth);
+            DPRINT1("height : 0x%08x \n", pbmiSafe->bmiHeader.biHeight);
+            EngSetLastError(ERROR_NO_SYSTEM_RESOURCES);
+            return 0;
+        }
 
-    if (pdc->fs & (DC_ACCUM_APP|DC_ACCUM_WMGR))
-    {
-       IntUpdateBoundsRect(pdc, &rcDst);
-    }
+        /* Select the bitmap into hdcMem, and save a handle to the old bitmap */
+        hOldBitmap = NtGdiSelectBitmap(hdcMem, hBitmap);
+
+        if (dwUsage == DIB_PAL_COLORS)
+        {
+            hPal = NtGdiGetDCObject(hdc, GDI_OBJECT_TYPE_PALETTE);
+            hPal = GdiSelectPalette(hdcMem, hPal, FALSE);
+        }
 
-    hbmTmp = GreCreateBitmapEx(pbmi->bmiHeader.biWidth,
-                               abs(pbmi->bmiHeader.biHeight),
-                               0,
-                               BitmapFormat(pbmi->bmiHeader.biBitCount,
-                                            pbmi->bmiHeader.biCompression),
-                               pbmi->bmiHeader.biHeight < 0 ? BMF_TOPDOWN : 0,
-                               pbmi->bmiHeader.biSizeImage,
-                               pvBits,
-                               0);
+        pdc = DC_LockDc(hdcMem);
+        if (pdc != NULL)
+        {
+            IntSetDIBits(pdc, hBitmap, 0, abs(pbmiSafe->bmiHeader.biHeight), pvBits,
+                         cjMaxBits, pbmiSafe, dwUsage);
+            DC_UnlockDc(pdc);
+        }
 
-    if (!hbmTmp)
-    {
-        bResult = FALSE;
-        goto cleanup;
+        /* Origin for DIBitmap may be bottom left (positive biHeight) or top
+           left (negative biHeight) */
+        if (cxSrc == cxDst && cySrc == cyDst)
+        {
+            NtGdiBitBlt(hdc, xDst, yDst, cxDst, cyDst,
+                        hdcMem, xSrc, abs(pbmiSafe->bmiHeader.biHeight) - cySrc - ySrc,
+                        dwRop, 0, 0);
+        }
+        else
+        {
+            NtGdiStretchBlt(hdc, xDst, yDst, cxDst, cyDst,
+                            hdcMem, xSrc, abs(pbmiSafe->bmiHeader.biHeight) - cySrc - ySrc,
+                            cxSrc, cySrc, dwRop, 0);
+        }
+
+        /* cleanup */
+        if (hPal)
+            GdiSelectPalette(hdcMem, hPal, FALSE);
+
+        if (hOldBitmap)
+            NtGdiSelectBitmap(hdcMem, hOldBitmap);
+
+        NtGdiDeleteObjectApp(hdcMem);
+        GreDeleteObject(hBitmap);
+
+    } /* End of dwRop == SRCCOPY */
+    else
+    { /* Start of dwRop != SRCCOPY */
+        /* FIXME: Locking twice is cheesy, coord tranlation in UM will fix it */
+        if (!(pdc = DC_LockDc(hdc)))
+        {
+            DPRINT1("Could not lock dc\n");
+            EngSetLastError(ERROR_INVALID_HANDLE);
+            goto cleanup;
+        }
+
+        /* Calculate source and destination rect */
+        rcSrc.left = xSrc;
+        rcSrc.top = ySrc;
+        rcSrc.right = xSrc + abs(cxSrc);
+        rcSrc.bottom = ySrc + abs(cySrc);
+        rcDst.left = xDst;
+        rcDst.top = yDst;
+        rcDst.right = rcDst.left + cxDst;
+        rcDst.bottom = rcDst.top + cyDst;
+        IntLPtoDP(pdc, (POINTL*)&rcDst, 2);
+        RECTL_vOffsetRect(&rcDst, pdc->ptlDCOrig.x, pdc->ptlDCOrig.y);
+
+        if (pdc->fs & (DC_ACCUM_APP|DC_ACCUM_WMGR))
+        {
+           IntUpdateBoundsRect(pdc, &rcDst);
+        }
+
+        BmpFormat = BitmapFormat(pbmiSafe->bmiHeader.biBitCount,
+                                 pbmiSafe->bmiHeader.biCompression);
+
+        hbmTmp = GreCreateBitmapEx(pbmiSafe->bmiHeader.biWidth,
+                                   abs(pbmiSafe->bmiHeader.biHeight),
+                                   0,
+                                   BmpFormat,
+                                   pbmiSafe->bmiHeader.biHeight < 0 ? BMF_TOPDOWN : 0,
+                                   cjMaxBits,
+                                   pvBits,
+                                   0);
+
+        if (!hbmTmp)
+        {
+            goto cleanup;
+        }
+
+        psurfTmp = SURFACE_ShareLockSurface(hbmTmp);
+        if (!psurfTmp)
+        {
+            goto cleanup;
+        }
+
+        /* Create a palette for the DIB */
+        ppalDIB = CreateDIBPalette(pbmiSafe, pdc, dwUsage);
+        if (!ppalDIB)
+        {
+            goto cleanup;
+        }
+
+        /* Prepare DC for blit */
+        DC_vPrepareDCsForBlit(pdc, &rcDst, NULL, NULL);
+
+        psurfDst = pdc->dclevel.pSurface;
+
+        /* Initialize XLATEOBJ */
+        EXLATEOBJ_vInitialize(&exlo,
+                              ppalDIB,
+                              psurfDst->ppal,
+                              RGB(0xff, 0xff, 0xff),
+                              pdc->pdcattr->crBackgroundClr,
+                              pdc->pdcattr->crForegroundClr);
+
+        /* Perform the stretch operation */
+        IntEngStretchBlt(&psurfDst->SurfObj,
+                         &psurfTmp->SurfObj,
+                         NULL,
+                         (CLIPOBJ *)&pdc->co,
+                         &exlo.xlo,
+                         &pdc->dclevel.ca,
+                         &rcDst,
+                         &rcSrc,
+                         NULL,
+                         &pdc->eboFill.BrushObject,
+                         NULL,
+                         WIN32_ROP3_TO_ENG_ROP4(dwRop));
+
+        /* Cleanup */
+        DC_vFinishBlit(pdc, NULL);
+        EXLATEOBJ_vCleanup(&exlo);
+
+    cleanup:
+        if (ppalDIB) PALETTE_ShareUnlockPalette(ppalDIB);
+        if (psurfTmp) SURFACE_ShareUnlockSurface(psurfTmp);
+        if (hbmTmp) GreDeleteObject(hbmTmp);
+        if (pdc) DC_UnlockDc(pdc);
     }
 
-    psurfTmp = SURFACE_ShareLockSurface(hbmTmp);
-    if (!psurfTmp)
+    if (pvBits) ExFreePoolWithTag(pvBits, TAG_DIB);
+
+    /* This is not what MSDN says is returned from this function, but it
+     * follows Wine's dlls/gdi32/dib.c function nulldrv_StretchDIBits
+     * and it fixes over 100 gdi32:dib regression tests. */
+    if (dwRop == SRCCOPY)
     {
-        bResult = FALSE;
-        goto cleanup;
+        LinesCopied = abs(pbmiSafe->bmiHeader.biHeight);
     }
-
-    /* Create a palette for the DIB */
-    ppalDIB = CreateDIBPalette(pbmi, pdc, dwUsage);
-    if (!ppalDIB)
+    else
     {
-        bResult = FALSE;
-        goto cleanup;
+        LinesCopied = pbmiSafe->bmiHeader.biHeight;
     }
 
-    /* Prepare DC for blit */
-    DC_vPrepareDCsForBlit(pdc, &rcDst, NULL, NULL);
-
-    psurfDst = pdc->dclevel.pSurface;
-
-    /* Initialize XLATEOBJ */
-    EXLATEOBJ_vInitialize(&exlo,
-                          ppalDIB,
-                          psurfDst->ppal,
-                          RGB(0xff, 0xff, 0xff),
-                          pdc->pdcattr->crBackgroundClr,
-                          pdc->pdcattr->crForegroundClr);
-
-    /* Perform the stretch operation */
-    bResult = IntEngStretchBlt(&psurfDst->SurfObj,
-                               &psurfTmp->SurfObj,
-                               NULL,
-                               (CLIPOBJ *)&pdc->co,
-                               &exlo.xlo,
-                               &pdc->dclevel.ca,
-                               &rcDst,
-                               &rcSrc,
-                               NULL,
-                               &pdc->eboFill.BrushObject,
-                               NULL,
-                               WIN32_ROP3_TO_ENG_ROP4(dwRop));
-
-    /* Cleanup */
-    DC_vFinishBlit(pdc, NULL);
-    EXLATEOBJ_vCleanup(&exlo);
-cleanup:
-    if (ppalDIB) PALETTE_ShareUnlockPalette(ppalDIB);
-    if (psurfTmp) SURFACE_ShareUnlockSurface(psurfTmp);
-    if (hbmTmp) GreDeleteObject(hbmTmp);
-    if (pdc) DC_UnlockDc(pdc);
-    if (pvBits) ExFreePoolWithTag(pvBits, 'pmeT');
+    ExFreePoolWithTag(pbmiSafe, 'imBG');
 
-    return bResult;
+    return LinesCopied;
 }