[PATCH 4/4] ddraw/tests: Test invalid clipper objects (v2).
Stefan Dösinger
stefan at codeweavers.com
Wed Mar 13 07:18:34 CDT 2019
Signed-off-by: Stefan Dösinger <stefan at codeweavers.com>
---
Version 2: Adjust for different Wine behavior due to using the vtable to
identify broken clippers.
Add a comment that surface->SetClipper with an unreadable pointer
crashes. I don't know why I previously had the idea that this works.
---
dlls/ddraw/tests/ddraw1.c | 151 ++++++++++++++++++++++++++++++++++++
dlls/ddraw/tests/ddraw2.c | 158 ++++++++++++++++++++++++++++++++++++++
dlls/ddraw/tests/ddraw4.c | 157 +++++++++++++++++++++++++++++++++++++
dlls/ddraw/tests/ddraw7.c | 157 +++++++++++++++++++++++++++++++++++++
4 files changed, 623 insertions(+)
diff --git a/dlls/ddraw/tests/ddraw1.c b/dlls/ddraw/tests/ddraw1.c
index d0ed33dbc41..cf53cc1f945 100644
--- a/dlls/ddraw/tests/ddraw1.c
+++ b/dlls/ddraw/tests/ddraw1.c
@@ -12293,6 +12293,156 @@ static void test_alphatest(void)
DestroyWindow(window);
}
+static void test_clipper_refcount(void)
+{
+ IDirectDrawSurface *surface;
+ IDirectDrawClipper *clipper, *clipper2;
+ DDSURFACEDESC surface_desc;
+ IDirectDraw *ddraw;
+ ULONG refcount;
+ HWND window;
+ HRESULT hr;
+ BOOL changed;
+ const IDirectDrawClipperVtbl *orig_vtbl;
+
+ window = create_window();
+ ddraw = create_ddraw();
+ ok(!!ddraw, "Failed to create a ddraw object.\n");
+ hr = IDirectDraw_SetCooperativeLevel(ddraw, window, DDSCL_NORMAL);
+ ok(hr == DD_OK, "Got unexpected hr %#x.\n", hr);
+
+ memset(&surface_desc, 0, sizeof(surface_desc));
+ surface_desc.dwSize = sizeof(surface_desc);
+ surface_desc.dwFlags = DDSD_CAPS;
+ surface_desc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
+ hr = IDirectDraw_CreateSurface(ddraw, &surface_desc, &surface, NULL);
+ ok(hr == DD_OK, "Got unexpected hr %#x.\n", hr);
+
+ hr = IDirectDraw_CreateClipper(ddraw, 0, &clipper, NULL);
+ ok(SUCCEEDED(hr), "Failed to create clipper, hr %#x.\n", hr);
+ refcount = get_refcount((IUnknown *)clipper);
+ ok(refcount == 1, "Got unexpected refcount %u.\n", refcount);
+
+ /* Show that clipper validation doesn't somehow happen through per-clipper vtable
+ * pointers. */
+ hr = IDirectDraw_CreateClipper(ddraw, 0, &clipper2, NULL);
+ ok(SUCCEEDED(hr), "Failed to create clipper, hr %#x.\n", hr);
+ ok(clipper->lpVtbl == clipper2->lpVtbl, "Got different clipper vtables %p and %p.\n",
+ clipper->lpVtbl, clipper2->lpVtbl);
+ orig_vtbl = clipper->lpVtbl;
+ IDirectDrawClipper_Release(clipper2);
+
+ /* Surfaces hold a reference to clippers. No surprises there. */
+ hr = IDirectDrawSurface_SetClipper(surface, clipper);
+ ok(SUCCEEDED(hr), "Failed to set clipper, hr %#x.\n", hr);
+ refcount = get_refcount((IUnknown *)clipper);
+ ok(refcount == 2, "Got unexpected refcount %u.\n", refcount);
+
+ hr = IDirectDrawSurface_GetClipper(surface, &clipper2);
+ ok(SUCCEEDED(hr), "Failed to get clipper, hr %#x.\n", hr);
+ ok(clipper == clipper2, "Got clipper %p, expected %p.\n", clipper2, clipper);
+ refcount = IDirectDrawClipper_Release(clipper2);
+ ok(refcount == 2, "Got unexpected refcount %u.\n", refcount);
+
+ hr = IDirectDrawSurface_SetClipper(surface, NULL);
+ ok(SUCCEEDED(hr), "Failed to set clipper, hr %#x.\n", hr);
+ refcount = get_refcount((IUnknown *)clipper);
+ ok(refcount == 1, "Got unexpected refcount %u.\n", refcount);
+
+ hr = IDirectDrawSurface_SetClipper(surface, clipper);
+ ok(SUCCEEDED(hr), "Failed to set clipper, hr %#x.\n", hr);
+ refcount = get_refcount((IUnknown *)clipper);
+ ok(refcount == 2, "Got unexpected refcount %u.\n", refcount);
+
+ refcount = IDirectDrawSurface_Release(surface);
+ ok(!refcount, "%u references left.\n", refcount);
+ refcount = get_refcount((IUnknown *)clipper);
+ ok(refcount == 1, "Got unexpected refcount %u.\n", refcount);
+
+ /* SetClipper with an invalid pointer crashes. */
+
+ /* Clipper methods work with a broken vtable, with the exception of Release. */
+ clipper->lpVtbl = (void *)0xdeadbeef;
+ refcount = orig_vtbl->AddRef(clipper);
+ todo_wine ok(refcount == 2, "Got unexpected refcount %u.\n", refcount);
+ refcount = orig_vtbl->Release(clipper);
+ ok(!refcount, "Got unexpected refcount %u.\n", refcount);
+
+ clipper->lpVtbl = orig_vtbl;
+ refcount = orig_vtbl->Release(clipper);
+ todo_wine ok(refcount == 1, "Got unexpected refcount %u.\n", refcount);
+
+ /* Fix the refcount difference because Wine did not increase the ref in the
+ * AddRef call above. */
+ if (refcount)
+ {
+ refcount = IDirectDrawClipper_Release(clipper);
+ ok(!refcount, "Got unexpected refcount %u.\n", refcount);
+ }
+
+ /* Steal the reference and see what happens - releasing the surface works fine.
+ * The clipper is destroyed and not kept alive by a hidden refcount - trying to
+ * release it after the GetClipper call is likely to crash, and certain to crash
+ * if we allocate and zero as much heap memory as we can get. */
+ hr = IDirectDraw_CreateSurface(ddraw, &surface_desc, &surface, NULL);
+ ok(hr == DD_OK, "Got unexpected hr %#x.\n", hr);
+ hr = IDirectDraw_CreateClipper(ddraw, 0, &clipper, NULL);
+ ok(SUCCEEDED(hr), "Failed to create clipper, hr %#x.\n", hr);
+ hr = IDirectDrawSurface_SetClipper(surface, clipper);
+ ok(SUCCEEDED(hr), "Failed to set clipper, hr %#x.\n", hr);
+
+ IDirectDrawClipper_Release(clipper);
+ IDirectDrawClipper_Release(clipper);
+
+ hr = IDirectDrawSurface_GetClipper(surface, &clipper2);
+ ok(SUCCEEDED(hr), "Failed to get clipper, hr %#x.\n", hr);
+ ok(clipper == clipper2, "Got clipper %p, expected %p.\n", clipper2, clipper);
+
+ /* Show that invoking the Release method does not crash, but don't get the
+ * vtable through the clipper pointer because it is no longer pointing to
+ * valid memory. */
+ refcount = orig_vtbl->Release(clipper);
+ ok(!refcount, "%u references left.\n", refcount);
+
+ refcount = IDirectDrawSurface_Release(surface);
+ ok(!refcount, "%u references left.\n", refcount);
+
+ /* It looks like the protection against invalid thispointers is part of
+ * the IDirectDrawClipper method implementation, not IDirectDrawSurface. */
+ clipper = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 0x1000);
+ ok(!!clipper, "failed to allocate memory\n");
+
+ /* Assigning the vtable to our fake clipper does NOT make a difference on
+ * native - there is a different member of the clipper implementation struct
+ * that is used to determine if a clipper is valid. */
+ clipper->lpVtbl = orig_vtbl;
+
+ refcount = orig_vtbl->AddRef(clipper);
+ todo_wine ok(!refcount, "Got refcount %u.\n", refcount);
+ refcount = orig_vtbl->AddRef((IDirectDrawClipper *)(ULONG_PTR)0xdeadbeef);
+ ok(!refcount, "Got refcount %u.\n", refcount);
+
+ changed = 0x1234;
+ hr = orig_vtbl->IsClipListChanged(clipper, &changed);
+ todo_wine ok(hr == DDERR_INVALIDPARAMS, "Got unexpected hr %#x.\n", hr);
+ todo_wine ok(changed == 0x1234, "'changed' changed: %x.\n", changed);
+
+ changed = 0x1234;
+ hr = orig_vtbl->IsClipListChanged((IDirectDrawClipper *)(ULONG_PTR)0xdeadbeef, &changed);
+ ok(hr == DDERR_INVALIDPARAMS, "Got unexpected hr %#x.\n", hr);
+ ok(changed == 0x1234, "'changed' changed: %x.\n", changed);
+
+ /* Nope, we can't initialize our fake clipper. */
+ hr = orig_vtbl->Initialize(clipper, ddraw, 0);
+ todo_wine ok(hr == DDERR_INVALIDPARAMS, "Got unexpected hr %#x.\n", hr);
+
+ HeapFree(GetProcessHeap(), 0, clipper);
+
+ refcount = IDirectDraw_Release(ddraw);
+ ok(!refcount, "%u references left.\n", refcount);
+ DestroyWindow(window);
+}
+
START_TEST(ddraw1)
{
DDDEVICEIDENTIFIER identifier;
@@ -12401,4 +12551,5 @@ START_TEST(ddraw1)
test_killfocus();
test_gdi_surface();
test_alphatest();
+ test_clipper_refcount();
}
diff --git a/dlls/ddraw/tests/ddraw2.c b/dlls/ddraw/tests/ddraw2.c
index 37238e162fb..7bf6dca422a 100644
--- a/dlls/ddraw/tests/ddraw2.c
+++ b/dlls/ddraw/tests/ddraw2.c
@@ -13552,6 +13552,163 @@ static void test_alphatest(void)
DestroyWindow(window);
}
+static void test_clipper_refcount(void)
+{
+ IDirectDrawSurface *surface;
+ IDirectDrawClipper *clipper, *clipper2;
+ DDSURFACEDESC surface_desc;
+ IDirectDraw2 *ddraw;
+ IDirectDraw *ddraw1;
+ ULONG refcount;
+ HWND window;
+ HRESULT hr;
+ BOOL changed;
+ const IDirectDrawClipperVtbl *orig_vtbl;
+
+ window = create_window();
+ ddraw = create_ddraw();
+ ok(!!ddraw, "Failed to create a ddraw object.\n");
+ hr = IDirectDraw2_SetCooperativeLevel(ddraw, window, DDSCL_NORMAL);
+ ok(hr == DD_OK, "Got unexpected hr %#x.\n", hr);
+
+ memset(&surface_desc, 0, sizeof(surface_desc));
+ surface_desc.dwSize = sizeof(surface_desc);
+ surface_desc.dwFlags = DDSD_CAPS;
+ surface_desc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
+ hr = IDirectDraw2_CreateSurface(ddraw, &surface_desc, &surface, NULL);
+ ok(hr == DD_OK, "Got unexpected hr %#x.\n", hr);
+
+ hr = IDirectDraw2_CreateClipper(ddraw, 0, &clipper, NULL);
+ ok(SUCCEEDED(hr), "Failed to create clipper, hr %#x.\n", hr);
+ refcount = get_refcount((IUnknown *)clipper);
+ ok(refcount == 1, "Got unexpected refcount %u.\n", refcount);
+
+ /* Show that clipper validation doesn't somehow happen through per-clipper vtable
+ * pointers. */
+ hr = IDirectDraw2_CreateClipper(ddraw, 0, &clipper2, NULL);
+ ok(SUCCEEDED(hr), "Failed to create clipper, hr %#x.\n", hr);
+ ok(clipper->lpVtbl == clipper2->lpVtbl, "Got different clipper vtables %p and %p.\n",
+ clipper->lpVtbl, clipper2->lpVtbl);
+ orig_vtbl = clipper->lpVtbl;
+ IDirectDrawClipper_Release(clipper2);
+
+ /* Surfaces hold a reference to clippers. No surprises there. */
+ hr = IDirectDrawSurface_SetClipper(surface, clipper);
+ ok(SUCCEEDED(hr), "Failed to set clipper, hr %#x.\n", hr);
+ refcount = get_refcount((IUnknown *)clipper);
+ ok(refcount == 2, "Got unexpected refcount %u.\n", refcount);
+
+ hr = IDirectDrawSurface_GetClipper(surface, &clipper2);
+ ok(SUCCEEDED(hr), "Failed to get clipper, hr %#x.\n", hr);
+ ok(clipper == clipper2, "Got clipper %p, expected %p.\n", clipper2, clipper);
+ refcount = IDirectDrawClipper_Release(clipper2);
+ ok(refcount == 2, "Got unexpected refcount %u.\n", refcount);
+
+ hr = IDirectDrawSurface_SetClipper(surface, NULL);
+ ok(SUCCEEDED(hr), "Failed to set clipper, hr %#x.\n", hr);
+ refcount = get_refcount((IUnknown *)clipper);
+ ok(refcount == 1, "Got unexpected refcount %u.\n", refcount);
+
+ hr = IDirectDrawSurface_SetClipper(surface, clipper);
+ ok(SUCCEEDED(hr), "Failed to set clipper, hr %#x.\n", hr);
+ refcount = get_refcount((IUnknown *)clipper);
+ ok(refcount == 2, "Got unexpected refcount %u.\n", refcount);
+
+ refcount = IDirectDrawSurface_Release(surface);
+ ok(!refcount, "%u references left.\n", refcount);
+ refcount = get_refcount((IUnknown *)clipper);
+ ok(refcount == 1, "Got unexpected refcount %u.\n", refcount);
+
+ /* SetClipper with an invalid pointer crashes. */
+
+ /* Clipper methods work with a broken vtable, with the exception of Release. */
+ clipper->lpVtbl = (void *)0xdeadbeef;
+ refcount = orig_vtbl->AddRef(clipper);
+ todo_wine ok(refcount == 2, "Got unexpected refcount %u.\n", refcount);
+ refcount = orig_vtbl->Release(clipper);
+ ok(!refcount, "Got unexpected refcount %u.\n", refcount);
+
+ clipper->lpVtbl = orig_vtbl;
+ refcount = orig_vtbl->Release(clipper);
+ todo_wine ok(refcount == 1, "Got unexpected refcount %u.\n", refcount);
+
+ /* Fix the refcount difference because Wine did not increase the ref in the
+ * AddRef call above. */
+ if (refcount)
+ {
+ refcount = IDirectDrawClipper_Release(clipper);
+ ok(!refcount, "Got unexpected refcount %u.\n", refcount);
+ }
+
+ /* Steal the reference and see what happens - releasing the surface works fine.
+ * The clipper is destroyed and not kept alive by a hidden refcount - trying to
+ * release it after the GetClipper call is likely to crash, and certain to crash
+ * if we allocate and zero as much heap memory as we can get. */
+ hr = IDirectDraw2_CreateSurface(ddraw, &surface_desc, &surface, NULL);
+ ok(hr == DD_OK, "Got unexpected hr %#x.\n", hr);
+ hr = IDirectDraw2_CreateClipper(ddraw, 0, &clipper, NULL);
+ ok(SUCCEEDED(hr), "Failed to create clipper, hr %#x.\n", hr);
+ hr = IDirectDrawSurface_SetClipper(surface, clipper);
+ ok(SUCCEEDED(hr), "Failed to set clipper, hr %#x.\n", hr);
+
+ IDirectDrawClipper_Release(clipper);
+ IDirectDrawClipper_Release(clipper);
+
+ hr = IDirectDrawSurface_GetClipper(surface, &clipper2);
+ ok(SUCCEEDED(hr), "Failed to get clipper, hr %#x.\n", hr);
+ ok(clipper == clipper2, "Got clipper %p, expected %p.\n", clipper2, clipper);
+
+ /* Show that invoking the Release method does not crash, but don't get the
+ * vtable through the clipper pointer because it is no longer pointing to
+ * valid memory. */
+ refcount = orig_vtbl->Release(clipper);
+ ok(!refcount, "%u references left.\n", refcount);
+
+ refcount = IDirectDrawSurface_Release(surface);
+ ok(!refcount, "%u references left.\n", refcount);
+
+ /* It looks like the protection against invalid thispointers is part of
+ * the IDirectDrawClipper method implementation, not IDirectDrawSurface. */
+ clipper = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 0x1000);
+ ok(!!clipper, "failed to allocate memory\n");
+
+ /* Assigning the vtable to our fake clipper does NOT make a difference on
+ * native - there is a different member of the clipper implementation struct
+ * that is used to determine if a clipper is valid. */
+ clipper->lpVtbl = orig_vtbl;
+
+ refcount = orig_vtbl->AddRef(clipper);
+ todo_wine ok(!refcount, "Got refcount %u.\n", refcount);
+ refcount = orig_vtbl->AddRef((IDirectDrawClipper *)(ULONG_PTR)0xdeadbeef);
+ ok(!refcount, "Got refcount %u.\n", refcount);
+
+ changed = 0x1234;
+ hr = orig_vtbl->IsClipListChanged(clipper, &changed);
+ todo_wine ok(hr == DDERR_INVALIDPARAMS, "Got unexpected hr %#x.\n", hr);
+ todo_wine ok(changed == 0x1234, "'changed' changed: %x.\n", changed);
+
+ changed = 0x1234;
+ hr = orig_vtbl->IsClipListChanged((IDirectDrawClipper *)(ULONG_PTR)0xdeadbeef, &changed);
+ ok(hr == DDERR_INVALIDPARAMS, "Got unexpected hr %#x.\n", hr);
+ ok(changed == 0x1234, "'changed' changed: %x.\n", changed);
+
+ /* Nope, we can't initialize our fake clipper. */
+ hr = IDirectDraw2_QueryInterface(ddraw, &IID_IDirectDraw, (void **)&ddraw1);
+ ok(SUCCEEDED(hr), "Failed to get ddraw1 interface, hr %#x.\n", hr);
+
+ hr = orig_vtbl->Initialize(clipper, ddraw1, 0);
+ todo_wine ok(hr == DDERR_INVALIDPARAMS, "Got unexpected hr %#x.\n", hr);
+
+ IDirectDraw_Release(ddraw1);
+
+ HeapFree(GetProcessHeap(), 0, clipper);
+
+ refcount = IDirectDraw2_Release(ddraw);
+ ok(!refcount, "%u references left.\n", refcount);
+ DestroyWindow(window);
+}
+
+
START_TEST(ddraw2)
{
DDDEVICEIDENTIFIER identifier;
@@ -13668,4 +13825,5 @@ START_TEST(ddraw2)
test_killfocus();
test_gdi_surface();
test_alphatest();
+ test_clipper_refcount();
}
diff --git a/dlls/ddraw/tests/ddraw4.c b/dlls/ddraw/tests/ddraw4.c
index b60606fca3a..51052fb3568 100644
--- a/dlls/ddraw/tests/ddraw4.c
+++ b/dlls/ddraw/tests/ddraw4.c
@@ -15798,6 +15798,162 @@ static void test_alphatest(void)
DestroyWindow(window);
}
+static void test_clipper_refcount(void)
+{
+ IDirectDrawSurface4 *surface;
+ IDirectDrawClipper *clipper, *clipper2;
+ DDSURFACEDESC2 surface_desc;
+ IDirectDraw4 *ddraw;
+ IDirectDraw *ddraw1;
+ ULONG refcount;
+ HWND window;
+ HRESULT hr;
+ BOOL changed;
+ const IDirectDrawClipperVtbl *orig_vtbl;
+
+ window = create_window();
+ ddraw = create_ddraw();
+ ok(!!ddraw, "Failed to create a ddraw object.\n");
+ hr = IDirectDraw4_SetCooperativeLevel(ddraw, window, DDSCL_NORMAL);
+ ok(hr == DD_OK, "Got unexpected hr %#x.\n", hr);
+
+ memset(&surface_desc, 0, sizeof(surface_desc));
+ surface_desc.dwSize = sizeof(surface_desc);
+ surface_desc.dwFlags = DDSD_CAPS;
+ surface_desc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
+ hr = IDirectDraw4_CreateSurface(ddraw, &surface_desc, &surface, NULL);
+ ok(hr == DD_OK, "Got unexpected hr %#x.\n", hr);
+
+ hr = IDirectDraw4_CreateClipper(ddraw, 0, &clipper, NULL);
+ ok(SUCCEEDED(hr), "Failed to create clipper, hr %#x.\n", hr);
+ refcount = get_refcount((IUnknown *)clipper);
+ ok(refcount == 1, "Got unexpected refcount %u.\n", refcount);
+
+ /* Show that clipper validation doesn't somehow happen through per-clipper vtable
+ * pointers. */
+ hr = IDirectDraw4_CreateClipper(ddraw, 0, &clipper2, NULL);
+ ok(SUCCEEDED(hr), "Failed to create clipper, hr %#x.\n", hr);
+ ok(clipper->lpVtbl == clipper2->lpVtbl, "Got different clipper vtables %p and %p.\n",
+ clipper->lpVtbl, clipper2->lpVtbl);
+ orig_vtbl = clipper->lpVtbl;
+ IDirectDrawClipper_Release(clipper2);
+
+ /* Surfaces hold a reference to clippers. No surprises there. */
+ hr = IDirectDrawSurface4_SetClipper(surface, clipper);
+ ok(SUCCEEDED(hr), "Failed to set clipper, hr %#x.\n", hr);
+ refcount = get_refcount((IUnknown *)clipper);
+ ok(refcount == 2, "Got unexpected refcount %u.\n", refcount);
+
+ hr = IDirectDrawSurface4_GetClipper(surface, &clipper2);
+ ok(SUCCEEDED(hr), "Failed to get clipper, hr %#x.\n", hr);
+ ok(clipper == clipper2, "Got clipper %p, expected %p.\n", clipper2, clipper);
+ refcount = IDirectDrawClipper_Release(clipper2);
+ ok(refcount == 2, "Got unexpected refcount %u.\n", refcount);
+
+ hr = IDirectDrawSurface4_SetClipper(surface, NULL);
+ ok(SUCCEEDED(hr), "Failed to set clipper, hr %#x.\n", hr);
+ refcount = get_refcount((IUnknown *)clipper);
+ ok(refcount == 1, "Got unexpected refcount %u.\n", refcount);
+
+ hr = IDirectDrawSurface4_SetClipper(surface, clipper);
+ ok(SUCCEEDED(hr), "Failed to set clipper, hr %#x.\n", hr);
+ refcount = get_refcount((IUnknown *)clipper);
+ ok(refcount == 2, "Got unexpected refcount %u.\n", refcount);
+
+ refcount = IDirectDrawSurface4_Release(surface);
+ ok(!refcount, "%u references left.\n", refcount);
+ refcount = get_refcount((IUnknown *)clipper);
+ ok(refcount == 1, "Got unexpected refcount %u.\n", refcount);
+
+ /* SetClipper with an invalid pointer crashes. */
+
+ /* Clipper methods work with a broken vtable, with the exception of Release. */
+ clipper->lpVtbl = (void *)0xdeadbeef;
+ refcount = orig_vtbl->AddRef(clipper);
+ todo_wine ok(refcount == 2, "Got unexpected refcount %u.\n", refcount);
+ refcount = orig_vtbl->Release(clipper);
+ ok(!refcount, "Got unexpected refcount %u.\n", refcount);
+
+ clipper->lpVtbl = orig_vtbl;
+ refcount = orig_vtbl->Release(clipper);
+ todo_wine ok(refcount == 1, "Got unexpected refcount %u.\n", refcount);
+
+ /* Fix the refcount difference because Wine did not increase the ref in the
+ * AddRef call above. */
+ if (refcount)
+ {
+ refcount = IDirectDrawClipper_Release(clipper);
+ ok(!refcount, "Got unexpected refcount %u.\n", refcount);
+ }
+
+ /* Steal the reference and see what happens - releasing the surface works fine.
+ * The clipper is destroyed and not kept alive by a hidden refcount - trying to
+ * release it after the GetClipper call is likely to crash, and certain to crash
+ * if we allocate and zero as much heap memory as we can get. */
+ hr = IDirectDraw4_CreateSurface(ddraw, &surface_desc, &surface, NULL);
+ ok(hr == DD_OK, "Got unexpected hr %#x.\n", hr);
+ hr = IDirectDraw4_CreateClipper(ddraw, 0, &clipper, NULL);
+ ok(SUCCEEDED(hr), "Failed to create clipper, hr %#x.\n", hr);
+ hr = IDirectDrawSurface4_SetClipper(surface, clipper);
+ ok(SUCCEEDED(hr), "Failed to set clipper, hr %#x.\n", hr);
+
+ IDirectDrawClipper_Release(clipper);
+ IDirectDrawClipper_Release(clipper);
+
+ hr = IDirectDrawSurface4_GetClipper(surface, &clipper2);
+ ok(SUCCEEDED(hr), "Failed to get clipper, hr %#x.\n", hr);
+ ok(clipper == clipper2, "Got clipper %p, expected %p.\n", clipper2, clipper);
+
+ /* Show that invoking the Release method does not crash, but don't get the
+ * vtable through the clipper pointer because it is no longer pointing to
+ * valid memory. */
+ refcount = orig_vtbl->Release(clipper);
+ ok(!refcount, "%u references left.\n", refcount);
+
+ refcount = IDirectDrawSurface4_Release(surface);
+ ok(!refcount, "%u references left.\n", refcount);
+
+ /* It looks like the protection against invalid thispointers is part of
+ * the IDirectDrawClipper method implementation, not IDirectDrawSurface. */
+ clipper = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 0x1000);
+ ok(!!clipper, "failed to allocate memory\n");
+
+ /* Assigning the vtable to our fake clipper does NOT make a difference on
+ * native - there is a different member of the clipper implementation struct
+ * that is used to determine if a clipper is valid. */
+ clipper->lpVtbl = orig_vtbl;
+
+ refcount = orig_vtbl->AddRef(clipper);
+ todo_wine ok(!refcount, "Got refcount %u.\n", refcount);
+ refcount = orig_vtbl->AddRef((IDirectDrawClipper *)(ULONG_PTR)0xdeadbeef);
+ ok(!refcount, "Got refcount %u.\n", refcount);
+
+ changed = 0x1234;
+ hr = orig_vtbl->IsClipListChanged(clipper, &changed);
+ todo_wine ok(hr == DDERR_INVALIDPARAMS, "Got unexpected hr %#x.\n", hr);
+ todo_wine ok(changed == 0x1234, "'changed' changed: %x.\n", changed);
+
+ changed = 0x1234;
+ hr = orig_vtbl->IsClipListChanged((IDirectDrawClipper *)(ULONG_PTR)0xdeadbeef, &changed);
+ ok(hr == DDERR_INVALIDPARAMS, "Got unexpected hr %#x.\n", hr);
+ ok(changed == 0x1234, "'changed' changed: %x.\n", changed);
+
+ /* Nope, we can't initialize our fake clipper. */
+ hr = IDirectDraw4_QueryInterface(ddraw, &IID_IDirectDraw, (void **)&ddraw1);
+ ok(SUCCEEDED(hr), "Failed to get ddraw1 interface, hr %#x.\n", hr);
+
+ hr = orig_vtbl->Initialize(clipper, ddraw1, 0);
+ todo_wine ok(hr == DDERR_INVALIDPARAMS, "Got unexpected hr %#x.\n", hr);
+
+ IDirectDraw_Release(ddraw1);
+
+ HeapFree(GetProcessHeap(), 0, clipper);
+
+ refcount = IDirectDraw4_Release(ddraw);
+ ok(!refcount, "%u references left.\n", refcount);
+ DestroyWindow(window);
+}
+
START_TEST(ddraw4)
{
DDDEVICEIDENTIFIER identifier;
@@ -15929,4 +16085,5 @@ START_TEST(ddraw4)
test_sysmem_draw();
test_gdi_surface();
test_alphatest();
+ test_clipper_refcount();
}
diff --git a/dlls/ddraw/tests/ddraw7.c b/dlls/ddraw/tests/ddraw7.c
index 3339b32d200..c199994d520 100644
--- a/dlls/ddraw/tests/ddraw7.c
+++ b/dlls/ddraw/tests/ddraw7.c
@@ -15599,6 +15599,162 @@ static void test_alphatest(void)
DestroyWindow(window);
}
+static void test_clipper_refcount(void)
+{
+ IDirectDrawSurface7 *surface;
+ IDirectDrawClipper *clipper, *clipper2;
+ DDSURFACEDESC2 surface_desc;
+ IDirectDraw7 *ddraw;
+ IDirectDraw *ddraw1;
+ ULONG refcount;
+ HWND window;
+ HRESULT hr;
+ BOOL changed;
+ const IDirectDrawClipperVtbl *orig_vtbl;
+
+ window = create_window();
+ ddraw = create_ddraw();
+ ok(!!ddraw, "Failed to create a ddraw object.\n");
+ hr = IDirectDraw7_SetCooperativeLevel(ddraw, window, DDSCL_NORMAL);
+ ok(hr == DD_OK, "Got unexpected hr %#x.\n", hr);
+
+ memset(&surface_desc, 0, sizeof(surface_desc));
+ surface_desc.dwSize = sizeof(surface_desc);
+ surface_desc.dwFlags = DDSD_CAPS;
+ surface_desc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
+ hr = IDirectDraw7_CreateSurface(ddraw, &surface_desc, &surface, NULL);
+ ok(hr == DD_OK, "Got unexpected hr %#x.\n", hr);
+
+ hr = IDirectDraw7_CreateClipper(ddraw, 0, &clipper, NULL);
+ ok(SUCCEEDED(hr), "Failed to create clipper, hr %#x.\n", hr);
+ refcount = get_refcount((IUnknown *)clipper);
+ ok(refcount == 1, "Got unexpected refcount %u.\n", refcount);
+
+ /* Show that clipper validation doesn't somehow happen through per-clipper vtable
+ * pointers. */
+ hr = IDirectDraw7_CreateClipper(ddraw, 0, &clipper2, NULL);
+ ok(SUCCEEDED(hr), "Failed to create clipper, hr %#x.\n", hr);
+ ok(clipper->lpVtbl == clipper2->lpVtbl, "Got different clipper vtables %p and %p.\n",
+ clipper->lpVtbl, clipper2->lpVtbl);
+ orig_vtbl = clipper->lpVtbl;
+ IDirectDrawClipper_Release(clipper2);
+
+ /* Surfaces hold a reference to clippers. No surprises there. */
+ hr = IDirectDrawSurface7_SetClipper(surface, clipper);
+ ok(SUCCEEDED(hr), "Failed to set clipper, hr %#x.\n", hr);
+ refcount = get_refcount((IUnknown *)clipper);
+ ok(refcount == 2, "Got unexpected refcount %u.\n", refcount);
+
+ hr = IDirectDrawSurface7_GetClipper(surface, &clipper2);
+ ok(SUCCEEDED(hr), "Failed to get clipper, hr %#x.\n", hr);
+ ok(clipper == clipper2, "Got clipper %p, expected %p.\n", clipper2, clipper);
+ refcount = IDirectDrawClipper_Release(clipper2);
+ ok(refcount == 2, "Got unexpected refcount %u.\n", refcount);
+
+ hr = IDirectDrawSurface7_SetClipper(surface, NULL);
+ ok(SUCCEEDED(hr), "Failed to set clipper, hr %#x.\n", hr);
+ refcount = get_refcount((IUnknown *)clipper);
+ ok(refcount == 1, "Got unexpected refcount %u.\n", refcount);
+
+ hr = IDirectDrawSurface7_SetClipper(surface, clipper);
+ ok(SUCCEEDED(hr), "Failed to set clipper, hr %#x.\n", hr);
+ refcount = get_refcount((IUnknown *)clipper);
+ ok(refcount == 2, "Got unexpected refcount %u.\n", refcount);
+
+ refcount = IDirectDrawSurface7_Release(surface);
+ ok(!refcount, "%u references left.\n", refcount);
+ refcount = get_refcount((IUnknown *)clipper);
+ ok(refcount == 1, "Got unexpected refcount %u.\n", refcount);
+
+ /* SetClipper with an invalid pointer crashes. */
+
+ /* Clipper methods work with a broken vtable, with the exception of Release. */
+ clipper->lpVtbl = (void *)0xdeadbeef;
+ refcount = orig_vtbl->AddRef(clipper);
+ todo_wine ok(refcount == 2, "Got unexpected refcount %u.\n", refcount);
+ refcount = orig_vtbl->Release(clipper);
+ ok(!refcount, "Got unexpected refcount %u.\n", refcount);
+
+ clipper->lpVtbl = orig_vtbl;
+ refcount = orig_vtbl->Release(clipper);
+ todo_wine ok(refcount == 1, "Got unexpected refcount %u.\n", refcount);
+
+ /* Fix the refcount difference because Wine did not increase the ref in the
+ * AddRef call above. */
+ if (refcount)
+ {
+ refcount = IDirectDrawClipper_Release(clipper);
+ ok(!refcount, "Got unexpected refcount %u.\n", refcount);
+ }
+
+ /* Steal the reference and see what happens - releasing the surface works fine.
+ * The clipper is destroyed and not kept alive by a hidden refcount - trying to
+ * release it after the GetClipper call is likely to crash, and certain to crash
+ * if we allocate and zero as much heap memory as we can get. */
+ hr = IDirectDraw7_CreateSurface(ddraw, &surface_desc, &surface, NULL);
+ ok(hr == DD_OK, "Got unexpected hr %#x.\n", hr);
+ hr = IDirectDraw7_CreateClipper(ddraw, 0, &clipper, NULL);
+ ok(SUCCEEDED(hr), "Failed to create clipper, hr %#x.\n", hr);
+ hr = IDirectDrawSurface7_SetClipper(surface, clipper);
+ ok(SUCCEEDED(hr), "Failed to set clipper, hr %#x.\n", hr);
+
+ IDirectDrawClipper_Release(clipper);
+ IDirectDrawClipper_Release(clipper);
+
+ hr = IDirectDrawSurface7_GetClipper(surface, &clipper2);
+ ok(SUCCEEDED(hr), "Failed to get clipper, hr %#x.\n", hr);
+ ok(clipper == clipper2, "Got clipper %p, expected %p.\n", clipper2, clipper);
+
+ /* Show that invoking the Release method does not crash, but don't get the
+ * vtable through the clipper pointer because it is no longer pointing to
+ * valid memory. */
+ refcount = orig_vtbl->Release(clipper);
+ ok(!refcount, "%u references left.\n", refcount);
+
+ refcount = IDirectDrawSurface7_Release(surface);
+ ok(!refcount, "%u references left.\n", refcount);
+
+ /* It looks like the protection against invalid thispointers is part of
+ * the IDirectDrawClipper method implementation, not IDirectDrawSurface. */
+ clipper = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 0x1000);
+ ok(!!clipper, "failed to allocate memory\n");
+
+ /* Assigning the vtable to our fake clipper does NOT make a difference on
+ * native - there is a different member of the clipper implementation struct
+ * that is used to determine if a clipper is valid. */
+ clipper->lpVtbl = orig_vtbl;
+
+ refcount = orig_vtbl->AddRef(clipper);
+ todo_wine ok(!refcount, "Got refcount %u.\n", refcount);
+ refcount = orig_vtbl->AddRef((IDirectDrawClipper *)(ULONG_PTR)0xdeadbeef);
+ ok(!refcount, "Got refcount %u.\n", refcount);
+
+ changed = 0x1234;
+ hr = orig_vtbl->IsClipListChanged(clipper, &changed);
+ todo_wine ok(hr == DDERR_INVALIDPARAMS, "Got unexpected hr %#x.\n", hr);
+ todo_wine ok(changed == 0x1234, "'changed' changed: %x.\n", changed);
+
+ changed = 0x1234;
+ hr = orig_vtbl->IsClipListChanged((IDirectDrawClipper *)(ULONG_PTR)0xdeadbeef, &changed);
+ ok(hr == DDERR_INVALIDPARAMS, "Got unexpected hr %#x.\n", hr);
+ ok(changed == 0x1234, "'changed' changed: %x.\n", changed);
+
+ /* Nope, we can't initialize our fake clipper. */
+ hr = IDirectDraw7_QueryInterface(ddraw, &IID_IDirectDraw, (void **)&ddraw1);
+ ok(SUCCEEDED(hr), "Failed to get ddraw1 interface, hr %#x.\n", hr);
+
+ hr = orig_vtbl->Initialize(clipper, ddraw1, 0);
+ todo_wine ok(hr == DDERR_INVALIDPARAMS, "Got unexpected hr %#x.\n", hr);
+
+ IDirectDraw_Release(ddraw1);
+
+ HeapFree(GetProcessHeap(), 0, clipper);
+
+ refcount = IDirectDraw7_Release(ddraw);
+ ok(!refcount, "%u references left.\n", refcount);
+ DestroyWindow(window);
+}
+
START_TEST(ddraw7)
{
DDDEVICEIDENTIFIER2 identifier;
@@ -15742,4 +15898,5 @@ START_TEST(ddraw7)
test_gdi_surface();
test_multiply_transform();
test_alphatest();
+ test_clipper_refcount();
}
--
2.19.2
More information about the wine-devel
mailing list