https://bugs.kde.org/show_bug.cgi?id=528237

--- Comment #2 from Gordon <[email protected]> ---
Created attachment 198061
  --> https://bugs.kde.org/attachment.cgi?id=198061&action=edit
Crashlog with dbg

Attached a backtrace with symbols: kwin-backtrace-20261104-132430.txt

Getting it took a detour, so a note on what you're looking at. This box runs
CachyOS
and they don't publish debug symbols for their kwin rebuild (their debuginfod
404s on
the build-id, and there is no kwin-debug package anywhere in their repos). So I
replaced kwin with Arch's unmodified extra/kwin 6.8.2-2, same upstream version,
and
took symbols from debuginfod.archlinux.org. It crashed identically on that
build, so
what you're reading is a stock upstream kwin. Qt, libc and Mesa are still
CachyOS
builds with no symbols published, which is why those frames are bare. I can
swap
qt6-base the same way if you want the event loop resolved, but I doubt it
matters here.

While I was at it I found a much simpler reproducer than the VM I described in
the
original report. No virtualisation needed at all. With the session running on
one GPU,
unbind a second GPU from its driver:

    echo 0000:08:00.0 | sudo tee /sys/bus/pci/drivers/i915/unbind

0000:08:00.0 is an Arc A770 on i915 here, desktop is on an RX 9060 XT (amdgpu).
Kills
kwin_wayland every single time. libvirt does the same thing internally when it
attaches
a <hostdev managed='yes'/> GPU to a guest, which is how I hit this in the first
place.

Short version: findCompatibleRenderDevice() gets called with device == nullptr.

    #0  operator()<std::unique_ptr<KWin::RenderDevice> > (...) at
src/core/gpumanager.cpp:177
    177         if (device->bustype == DRM_BUS_PLATFORM) {
    ...
    #10 KWin::GpuManager::findCompatibleRenderDevice
(this=this@entry=0x5577640325c0,
            device=device@entry=0x0) at src/core/gpumanager.cpp:190
    #11 KWin::GpuManager::updateCompatibilityMap (this=0x5577640325c0) at
src/core/gpumanager.cpp:150
            device = 0x0
            devices = std::vector of length 2, capacity 2 = {0x5577674846e0,
0x0}
    #12 KWin::GpuManager::handleUdevEvent (this=<optimized out>) at
src/core/gpumanager.cpp:289
            devNum = 1

The kernel log says the same thing, "segfault at c", and offsetof(drmDevice,
bustype)
is 12.

I had assumed this would turn out to be one of the unguarded eglDisplay()
derefs in the
comparator further down, but it isn't. It's the drmGetDevices2() pair at the
top of
updateCompatibilityMap(), lines 139-150, which is the same in master right now:

    int numberOfDevices = drmGetDevices2(0, nullptr, 0);   // counts 2
    if (numberOfDevices <= 0) {
        return;
    }
    std::vector<drmDevicePtr> devices;
    devices.resize(numberOfDevices);                       // 2 slots, both
null
    if (drmGetDevices2(0, devices.data(), numberOfDevices) < 0) {   // fills 1,
returns 1
        qCWarning(KWIN_CORE, "drmGetDevices2 failed: %s", strerror(errno));
        return;
    }
    for (drmDevicePtr device : devices) {                  // devices[1] is
still null
        RenderDevice *render = findCompatibleRenderDevice(device);

The GPU goes away between the counting call and the filling call. That isn't a
narrow
window in this case, it's the normal situation, because the whole path runs off
handleUdevEvent() reacting to the remove action for the primary node of the
device that
just vanished (frame #12, devNum = 1, so card1, so the GPU being unbound). The
second
call fills fewer entries than it was asked for and returns that smaller count,
but only
"< 0" is checked, so the rest of the vector keeps its null initialiser and gets
iterated
into findCompatibleRenderDevice(). devices = {0x5577674846e0, 0x0} in frame #11
is
exactly that, two slots and one of them filled.

Using the return value should be enough:

    std::vector<drmDevicePtr> devices(numberOfDevices);
    const int count = drmGetDevices2(0, devices.data(), numberOfDevices);
    if (count < 0) {
        qCWarning(KWIN_CORE, "drmGetDevices2 failed: %s", strerror(errno));
        return;
    }
    devices.resize(count);

That has the side benefit of giving drmFreeDevices() at the end the real count
rather
than the inflated one.

I can test patches if that helps, the repro takes about half a minute.

-- 
You are receiving this mail because:
You are watching all bug changes.

Reply via email to