| Summary: | Heap use-after-free when copy/pasting a Krita layer, causes a SEGFAULT on X11 | ||
|---|---|---|---|
| Product: | [Applications] krita | Reporter: | crock_ranked001 |
| Component: | General | Assignee: | 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 | ||
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.
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
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. |
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.