Module: wine
Branch: master
Commit: 36366f129b1e456f4530ae98015646af499e280a
URL: https://gitlab.winehq.org/wine/wine/-/commit/36366f129b1e456f4530ae98015646…
Author: Zhiyi Zhang <zzhang(a)codeweavers.com>
Date: Mon Nov 6 16:13:22 2023 +0800
win32u: Find the correct DIB driver in windrv_CreateDC().
push_dc_driver() places drivers based on their priorities, so the newly created driver is not
necessarily on top. Thus in windrv_CreateDC(), find_dc_driver() should be used to find the DIB
driver instead of assuming the DIB driver is the top driver, which could be the path driver because
it has a higher priority.
The exact wrong code path was:
1. A path driver with priority 400 is created for a DC.
2. windrv_CreateDC() is called to create a window driver for the DC.
3. Then in dibdrv_CreateDC(), push_dc_driver() is called with 'dev' pointing to the top driver, which is the path driver.
4. push_dc_driver() updates 'dev' to point to the address of the next driver because DIB driver has a lower 300 priority.
5. The DIB driver is assigned to 'dev', which is not the original parameter passed into push_dc_driver().
6. In windrv_CreateDC(), get_dibdrv_pdev(*dev) is called, assuming the top driver is the DIB driver. But actually the top
driver that '*dev' points to is still the path driver.
The added tests can demonstrate the memory corruption before this fix is applied.
---
dlls/gdi32/tests/path.c | 18 ++++++++++++++++--
dlls/win32u/dibdrv/dc.c | 6 +++++-
2 files changed, 21 insertions(+), 3 deletions(-)
diff --git a/dlls/gdi32/tests/path.c b/dlls/gdi32/tests/path.c
index c653c22edeb..c34cb034d88 100644
--- a/dlls/gdi32/tests/path.c
+++ b/dlls/gdi32/tests/path.c
@@ -607,6 +607,7 @@ static void test_polydraw(void)
BOOL retb;
POINT pos;
HDC hdc = GetDC(0);
+ HWND hwnd;
MoveToEx( hdc, -20, -20, NULL );
@@ -620,7 +621,8 @@ static void test_polydraw(void)
{
/* PolyDraw is only available on Win2k and later */
win_skip("PolyDraw is not available\n");
- goto done;
+ ReleaseDC(0, hdc);
+ return;
}
expect(TRUE, retb);
GetCurrentPositionEx( hdc, &pos );
@@ -684,8 +686,20 @@ static void test_polydraw(void)
ok_path(hdc, "polydraw_path", polydraw_path, ARRAY_SIZE(polydraw_path));
GetCurrentPositionEx( hdc, &pos );
ok( pos.x == 80 && pos.y == 80, "wrong pos %ld,%ld\n", pos.x, pos.y );
-done:
ReleaseDC(0, hdc);
+
+ /* Test a special case that GDI path driver is created before window driver */
+ hwnd = CreateWindowA("static", NULL, 0, 0, 0, 0, 0, 0, 0, 0, NULL);
+ hdc = GetDC(hwnd);
+
+ BeginPath(hdc);
+ SetWindowPos(hwnd, 0, 0, 0, 100, 100, SWP_NOZORDER | SWP_NOMOVE | SWP_SHOWWINDOW);
+ retb = PolyDraw(hdc, polydraw_pts, polydraw_tps, 2);
+ ok(retb, "PolyDraw failed, error %#lx\n", GetLastError());
+ EndPath(hdc);
+
+ ReleaseDC(hwnd, hdc);
+ DestroyWindow(hwnd);
}
static void test_closefigure(void) {
diff --git a/dlls/win32u/dibdrv/dc.c b/dlls/win32u/dibdrv/dc.c
index 87e637553da..7fe4b765a78 100644
--- a/dlls/win32u/dibdrv/dc.c
+++ b/dlls/win32u/dibdrv/dc.c
@@ -871,6 +871,8 @@ static BOOL windrv_Chord( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
static BOOL windrv_CreateDC( PHYSDEV *dev, LPCWSTR device, LPCWSTR output, const DEVMODEW *devmode )
{
struct windrv_physdev *physdev = calloc( 1, sizeof(*physdev) );
+ PHYSDEV dibdrv;
+ DC *dc;
if (!physdev) return FALSE;
@@ -879,7 +881,9 @@ static BOOL windrv_CreateDC( PHYSDEV *dev, LPCWSTR device, LPCWSTR output, const
free( physdev );
return FALSE;
}
- physdev->dibdrv = get_dibdrv_pdev( *dev );
+ dc = get_physdev_dc( *dev );
+ dibdrv = find_dc_driver( dc, &dib_driver );
+ physdev->dibdrv = get_dibdrv_pdev( dibdrv );
push_dc_driver( dev, &physdev->dev, &window_driver );
return TRUE;
}