https://bugs.winehq.org/show_bug.cgi?id=57394
Zhiyi Zhang zzhang@codeweavers.com changed:
What |Removed |Added ---------------------------------------------------------------------------- Status|NEW |RESOLVED Resolution|--- |WONTFIX
--- Comment #12 from Zhiyi Zhang zzhang@codeweavers.com --- This is just a buggy game. It crashes because of a STATUS_ACCESS_VIOLATION at 0x140106b2b.
First, to launch the game without the launcher, use `wine game.bin EasyFun width=3840 height=2160 version=0 no_loginsdk=1 app_name=AstralTaleUpgrade`
Then put a breakpoint in dispatch_exception() when STATUS_ACCESS_VIOLATION is encountered. You will see the game crash site is at 0x140106b2b under winedbg.
``` 140106b23 31 d2 XOR EDX,EDX 140106b25 45 31 c0 XOR R8D,R8D 140106b28 45 31 c9 XOR R9D,R9D 140106b2b ff d0 CALL RAX <- RAX is supposed to be a function pointer, the function prototype should have four parameters.
```
The RAX value is from a function at 0x1401067e0
``` undefined8 * FUN_1401067e0(undefined8 *param_1,longlong param_2)
{ ... DAT_14138fc90 = *(code **)(lVar9 + 0x2f0); ---> lVar9 is pointer to d3d9_device_vtbl, so DAT_14138fc90 is &d3d9_device_SetVertexShaderConstantF DAT_14138fc98 = *(code **)(lVar9 + 0x300); DAT_14138fca0 = *(code **)(lVar9 + 0x310); DAT_14138fca8 = DAT_14138fc90; ---> DAT_14138fca8 gets the same value from DAT_14138fc90, which is &d3d9_device_SetVertexShaderConstantF DAT_14138fcb0 = DAT_14138fc98; DAT_14138fcb8 = DAT_14138fca0; if ((DAT_14138fcf1 != '\0') && (uVar11 < 4)) { bVar12 = DAT_14138fcf0 == '\0'; ---> DAT_14138fcf0 is 1 if the d3d9 device is from Direct3DCreate9Ex() instead of Direct3DCreate9 puVar5 = (undefined8 *)(lVar9 + 0x4f0); ---> This is where it starts to go bad. Remember that lVar9 is a pointer to d3d9_device_vtbl
But d3d9_device_vtbl[0x4f0] is outside of the range of d3d9_device_vtbl. So it just points to some
data at &d3d9_device_vtbl[0x4f0]. With Wine's built-in d3d9 at Wine-9.0, d3d9_device_vtbl[0x4f0] points
to a string in the .rdata section. Obviously, executing a read-only string will trigger an access violation. if (bVar12) { puVar5 = (undefined8 *)(lVar9 + 0x478); } DAT_14138fca8 = (code *)*puVar5; puVar5 = (undefined8 *)(lVar9 + 0x480); if (!bVar12) { puVar5 = (undefined8 *)(lVar9 + 0x4f8); } DAT_14138fcb0 = (code *)*puVar5; puVar5 = (undefined8 *)(lVar9 + 0x488); if (!bVar12) { puVar5 = (undefined8 *)(lVar9 + 0x500); } DAT_14138fcb8 = (code *)*puVar5; (*DAT_14138fca8)(DAT_14138fc38,0,0,0); ---> This is at 0x140106b2b, where the access violation happens. (*DAT_14138fcb0)(DAT_14138fc38,0,0,0); (*DAT_14138fcb8)(DAT_14138fc38,0,0,0); } DAT_14138fcc0 = *(undefined8 *)(lVar9 + 0x368); DAT_14138fcc8 = *(undefined8 *)(lVar9 + 0x378); DAT_14138fcd0 = *(undefined8 *)(lVar9 + 0x388); DAT_14138fcd8 = *(undefined8 *)(lVar9 + 0x368); DAT_14138fce0 = *(undefined8 *)(lVar9 + 0x378); DAT_14138fce8 = *(undefined8 *)(lVar9 + 0x388); return param_1; } ```
So let's test if it's possible that d3d9_device_vtbl[0x4f0] can contain any valid code. ``` static void test_device_4f0(void) { HRESULT (WINAPI *p_d3d9_device_SetVertexShaderConstantF)(IDirect3DDevice9Ex *iface, UINT reg_idx, const float *data, UINT count); IDirect3D9 *d3d9 = (void *) 0xdeadbeef; IDirect3D9Ex *d3d9ex; HRESULT hr; ULONG ref; void **vtable;
hr = pDirect3DCreate9Ex(D3D_SDK_VERSION, &d3d9ex); ok(hr == D3D_OK, "Got hr %#lx.\n", hr);
hr = IDirect3D9Ex_QueryInterface(d3d9ex, &IID_IDirect3D9, (void **) &d3d9); ok(hr == S_OK, "Got hr %#lx.\n", hr); ok(d3d9 != NULL && d3d9 != (void *) 0xdeadbeef, "QueryInterface returned interface %p, expected != NULL && != 0xdeadbeef\n", d3d9); ref = getref((IUnknown *) d3d9ex); ok(ref == 2, "Unexpected refcount %lu.\n", ref); ref = getref((IUnknown *) d3d9); ok(ref == 2, "Unexpected refcount %lu.\n", ref); ok(d3d9 == (void *)d3d9ex, "Expected the same pointer.\n");
vtable = (void *)d3d9ex->lpVtbl; ok(vtable[0] == d3d9ex->lpVtbl->QueryInterface, "Got unexpected vtable.\n");
p_d3d9_device_SetVertexShaderConstantF = (void *)(vtable[0x4f0]);
hr = p_d3d9_device_SetVertexShaderConstantF(d3d9ex, 0, 0, 0); ok(hr == S_OK, "Got hr %#lx.\n", hr);
IDirect3D9_Release(d3d9); IDirect3D9Ex_Release(d3d9ex); }
```
The result on Windows is that an exception occurred. So the answer is NO. So is the same for vtable[0x478] ``` PS Y:> .\d3d9_test.exe d3d9ex d3d9ex.c:5224: this is the last test seen before the exception 0638:d3d9ex: unhandled exception c0000005 at 00007FFB5A88F7AD ```
So, I think this is an application bug. It worked on a previous Wine version most likely because the memory layout made d3d9_device_vtbl[0x4f0] point to some executable code. So an access violation might not happen. This is the case with DXVK 2.4.1 and Wine@git a6bf181dafd compiled with default compilation flags. It calls into some function in DXVK and ends up triggering a different exception but the exception is eventually handled. After b21813fa1, we added a new syscall so the memory layout changes a bit and d3d9_device_vtbl[0x4f0] doesn't point to executable code anymore, leading to a crash. The crash also happens on older versions of Wine such as wine-9.0 and builtin d3d9 for the same reason. Also, I can't even launch the game on Windows 11.
At this point, I don't think there is much we can do unless the game developer fixes their game.