Module: wine Branch: master Commit: 36366f129b1e456f4530ae98015646af499e280a URL: https://gitlab.winehq.org/wine/wine/-/commit/36366f129b1e456f4530ae98015646a...
Author: Zhiyi Zhang zzhang@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; }