https://bugs.documentfoundation.org/show_bug.cgi?id=174257
Bug ID: 174257
Summary: Skia: VulkanWindowContext::initializeContext frees
uninitialized pNext chain when Vulkan backend creation
fails (no Vulkan ICD) → crash on first start
Product: LibreOffice
Version: 25.8.7.3 release
Hardware: x86-64 (AMD64)
OS: Windows (All)
Status: UNCONFIRMED
Severity: normal
Priority: medium
Component: LibreOffice
Assignee: [email protected]
Reporter: [email protected]
Description:
On Windows with no Vulkan driver (ICD) installed, a first start with a fresh
user profile tries Skia/Vulkan. `vulkan-1.dll` loads, but Vulkan backend
creation fails because no driver is available. On that failure path,
`skwindow::internal::VulkanWindowContext::initializeContext` frees a Vulkan
features `pNext` chain whose root is a stack local that is never initialized on
this path. `sk_free()` is called on whatever value is left in that stack slot.
What happens next depends on the leftover stack contents:
- If the value is null or harmless, startup falls back to Skia/Raster. This is
what we usually see on a plain Windows install.
- If the value is a live non-heap pointer, the free crashes. We hit this every
time when soffice runs under an application-virtualization layer that hooks
heap APIs, because the hook frames leave the process heap handle in that slot.
`RtlFreeHeap` faults and the crash guard sets `ForceSkiaRaster=true`, so the
second start works.
The bug is the uninitialized read and free in LibreOffice; the virtualization
layer only makes it show up reliably. Other injected DLLs (AV hooks, overlays,
compatibility shims) could cause the same crash.
Steps to Reproduce:
1. Use a Windows x64 machine without a Vulkan ICD, for example a VM without GPU
passthrough. `vulkan-1.dll` is present, `HKLM\SOFTWARE\Khronos\Vulkan\Drivers`
is absent, and no display adapter has a `VulkanDriverName` value.
2. Delete the user profile (`%APPDATA%\LibreOffice`) or start with
`-env:UserInstallation=file:///C:/Temp/lo-fresh`.
3. Start `soffice.exe`.
To check that the bad path runs, set a breakpoint on the free helper called at
`skialo!...initializeContext+0x5ad` and look at `[rcx+8]`. Its value is
whatever the stack slot held before. Under Application Verifier, or with code
that leaves a non-null pointer at that stack depth, it crashes.
Actual Results:
`sk_free()` is called on an uninitialized pointer, which can crash and abort
startup.
Expected Results:
Vulkan backend creation fails cleanly, and LibreOffice falls back to
Skia/Raster.
Reproducible: Always
User Profile Reset: Yes
Additional Info:
## Crash details
Build ID `30742500f2d3eb4366ac312fa33d3dcabdb3eba5`. Offsets are from the
release `skialo.dll`; we didn't have LibreOffice symbols.
Original exception (nested under the crash handler's `Application::Abort`):
```
ExceptionCode: c0000005 (Access violation) reading 0x000001ce7027ffff
ntdll!RtlFreeHeap+0x7d
(heap hook)
ucrtbase!free_base+0x1b
skialo!<pNext chain free helper> ; loops: next = node->pNext;
sk_free(node)
skialo!skwindow::internal::VulkanWindowContext::initializeContext+0x5b2
skialo!skwindow::internal::VulkanWindowContext::VulkanWindowContext+0x161
skialo!skwindow::MakeVulkanForWin+0xf0
vclplug_winlo!...
mergedlo!SkiaHelper::renderMethodToUse+0x3cf
mergedlo!SkiaHelper::isVCLSkiaEnabled+0x56
...
mergedlo!WorkWindow::ImplInit+0xe1
mergedlo!desktop_SplashScreen_get_implementation+0x92
...
mergedlo!soffice_main+0x2e5
```
The freed "node" is `0x1ce70280000`, which is the process heap handle
(`PEB.ProcessHeap`), not a heap block.
## Analysis (from disassembly of initializeContext)
- `+0x71..+0x11d`: allocates the shared context (`new` 0x538, `memset` 0) and
stores it in `fGlobalShared`.
- `+0x18b`: calls the Vulkan backend-context creation function. Its arguments
are the shared context's backend context, extensions at `[rbp+138h]`, and
features at `shared+0x50`. None of them point to `[rbp+10h]`.
- `+0x192`: `test al,al / je +0x57d` takes the failure path.
- `+0x57d`: frees the features chain at `shared+0x50`. This is fine because the
object was zeroed.
- `+0x585..+0x5a7`: resets `fGlobalShared`.
- `+0x5a9`: `lea rcx,[rbp+10h]; call <pNext chain free helper>`. This local is
never written on this path, so `[rbp+18h]` (`pNext`) holds stack garbage.
On the crashing run, the stack around `[rbp+18h]` still held values left by an
earlier `RtlAllocateHeap(ProcessHeap, …, 0x168)` call at the same depth. That
explains the heap handle value.
## Suggested fix
Value-initialize the local `VkPhysicalDeviceFeatures2`, or whichever struct
owns that `pNext` chain, at its declaration (`= {}`, or at least `pNext =
nullptr`). Alternatively, only free its chain on paths where it was filled in.
Check the other early returns in `initializeContext` too. This code may be
LibreOffice-specific (in `external/skia` patches); compare it with upstream
Skia's `tools/window/VulkanWindowContext.cpp`.
## Workaround
Set `SAL_SKIA=raster`, or `ForceSkiaRaster=true` / disable Skia in Tools →
Options → View, before the first start.
## Additional info
- With the workaround, or on the second start after the crash guard fires,
About shows `UI render: Skia/Raster`.
- A full-memory minidump (~560 MB) is available on request.
--
You are receiving this mail because:
You are the assignee for the bug.