Bug 508785

Summary: Heap use-after-free when copy/pasting a Krita layer, causes a SEGFAULT on X11
Product: [Applications] krita Reporter: crock_ranked001
Component: GeneralAssignee: Krita Bugs <krita-bugs-null>
Status: RESOLVED WAITINGFORINFO    
Severity: crash    
Priority: NOR    
Version First Reported In: 5.2.11   
Target Milestone: ---   
Platform: Debian stable   
OS: Linux   
See Also: https://bugs.kde.org/show_bug.cgi?id=502266
Latest Commit: Version Fixed/Implemented In:
Sentry Crash Report:
Attachments: AddressSanitizer output of Krita 5.2.11 compiled with asan

Description crock_ranked001 2025-08-26 21:29:52 UTC
Created attachment 184485 [details]
AddressSanitizer output of Krita 5.2.11 compiled with asan

SUMMARY
When copying and pasting a Krita layer, Krita crashes without any notice and logs (except for "KRITA DID NOT CLOSE CORRECTLY")

STEPS TO REPRODUCE
1. Copy a layer or a part of it
2. Paste as a new layer

OBSERVED RESULT
Visually, it just closes. When opening back it doesn't restore the previous session, so all progress is lost without backup.
The terminal output states "Segmentation Fault"

EXPECTED RESULT
Krita pastes a new layer and continues as supposed to.


SOFTWARE/OS VERSIONS
Linux: Debian 13 / 6.12.41+deb13-amd64 with Xfce4 4.20
Qt Version: 5.15.7
CPU: AMD Ryzen 5 5600H with Radeon Graphics
GPU: Cezanne [Radeon Vega Series / Radeon Vega Mobile Series]
RAM: 16 GB DDR4 3200MHz

ADDITIONAL INFORMATION
The SEGFAULT does not happen if the image source is obtained outside of Krita.
In some DEs (e.g. LXQt) the crash is not observed, as well as in the earlier version of Xfce4 (4.18).
The crash happens in every official 5.2+ version, as well as 5.3.0 prealpha and even 5.1.5

The ASAN debug build sheds some light on this, I suspect it is caused by race condition.
Comment 1 crock_ranked001 2025-08-28 19:20:56 UTC
Update, I decided to do some research on my own.
However, I'm not familiar with Qt, with the Krita ecosystem, so my assumptions might be wrong.
I think I found out that this code causes the crash (ui/kis_mimedata.cpp:263)):
    // Qt 4.8 way
    if (nodes.isEmpty() && data->hasFormat("application/x-krita-node-internal-pointer")) {
        QByteArray nodeXml = data->data("application/x-krita-node-internal-pointer");

        QDomDocument doc;
        doc.setContent(nodeXml);

        QDomElement root = doc.documentElement();
        qint64 pid = root.attribute("application_pid").toLongLong();
        forceCopy = root.attribute("force_copy").toInt();
        qint64 imagePointerValue = root.attribute("image_pointer_value").toLongLong();
        sourceImage = reinterpret_cast<KisImage*>(imagePointerValue);

        if (pid == QApplication::applicationPid()) {
            QDomElement e;
            for (e = root.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) {
                qint64 pointerValue = e.attribute("pointer_value").toLongLong();
                if (pointerValue) {
                    nodes << reinterpret_cast<KisNode*>(pointerValue);
                }
            }
        }
    }

The problem here is that in between copy and paste, on Xfce 4.20 an xcb event may sometimes be launched that destroys the KisMimeData object, which in turn frees all internal nodes. However, the node.data() is still in the DOM in the form of pointer_value and is a dangling pointer. Asan detects it early, and reports during this line:
nodes << reinterpret_cast<KisNode*>(pointerValue);
In the release build, it stays in the list, and when passed to dynamic_cast it crashes the app, due to dereferencing a freed pointer.
Interestingly, if you comment out the "Qt 4.8" (or in my case, add check for mimedata in the conditional), Krita DOES NOT crash!
I think there should be a proper DOM cleanup routine though, which would fix the issue (I think). Anyway, I hope this gets noticed, since this bug persists in upstream.
Comment 2 crock_ranked001 2025-09-07 16:29:15 UTC
Another update. After I applied the previous duct tape solutions, I found more oddities with Krita, luckily, observable. (Krita 5.2.11, Docker build with ASAN enabled).
The problem starts during the copy action. Several seconds after Ctrl+C or the likes, something happens internally, which makes Qt clear the clipboard.
In the debug build, the following message is fired 3 times:
    kf.imageformats.plugins.eps: Creating EPS files requires pdftops (from Poppler) or gs (from GhostScript)

After some time, the clipboard ownership is lost (checked with QClipboard::ownsClipboard() during KisNodeManager::pasteLayersFromClipboard()).
Interestingly, this only happens once when image selection (or its content) is renewed. Hitting Ctrl+C again, the EPS message doesn't appear, and as such the crash will not happen. Because of this, the Copy Selection to New Layer will never produce a crash, though the messages will appear.
So I want to update the steps to reproduce.

In any document:
1. Generate selection or use the entire canvas
2. Hit Ctrl+C or similar actions that trigger KisSelectionManager::copy()
3. Wait a few seconds
4. Hit Ctrl+V or similar actions that trigger KisSelectionManager::paste()
5. Krita pastes layers from a clipboard it doesn't own anymore, resulting in a SEGFAULT.

What's more is that Krita will still paste the image (applying the fix above), but it will be pasted in the middle of the document, probably due to a lack of data. I also noticed some horrible stuttering during the whole process, maybe it is related.

Lastly, I also sometimes get this during Ctrl+Z:
    SAFE ASSERT (krita): "!m_d->parent || !parent" in file /home/appimage/persistent/krita/libs/image/kis_paint_device.cc, line 1200
Comment 3 crock_ranked001 2025-09-09 18:49:09 UTC
Final update. With the help of a custom XFixes program, I found out that main culprit is xfsettingsd, which grabs selection ownership almost immediately after the first Ctrl+C. I guess, after porting it to 4.20, they did something different and now it behaves like it behaves.
Still, there isn't a single line of code that checks for updates in clipboard ownership in Krita, so I can't really say that this is purely Xfce's problem. Any app can do something similar, actually.

Here's the workaround for this one: set the XFSETTINGSD_NO_CLIPBOARD environment variable before xfsettingsd launches, and you're golden.