https://bugs.kde.org/show_bug.cgi?id=525861
Bug ID: 525861
Summary: SIGSEGV in
DataControlOffer::retrieveData()/QHash::emplace:
use-after-free racing offer destruction against a
concurrent read
Classification: Frameworks and Libraries
Product: frameworks-kguiaddons
Version First 6.30.0
Reported In:
Platform: NixOS
OS: Linux
Status: REPORTED
Severity: crash
Priority: NOR
Component: general
Assignee: [email protected]
Reporter: [email protected]
Target Milestone: ---
Created attachment 196272
--> https://bugs.kde.org/attachment.cgi?id=196272&action=edit
systemd-coredump of the SIGSEGV crash (2026-09-17), zstd-compressed
## Summary
A Wayland `ext-data-control-v1` client (here, CopyQ's clipboard-monitor helper)
crashes with SIGSEGV inside `QHash<QString,QVariant>::emplace`, called from
`DataControlOffer::retrieveData()`. The coredump shows the crash's `this`
pointer pointing into memory that was already freed and reused elsewhere. It
looks like a UAF in kguiaddons' data-control client code.
## Environment
- kguiaddons: 6.30.0 (NixOS nixos-unstable, built 2026-09-14)
- Qt: 6.11.2
- Compositor: Hyprland 0.56.2
- Wayland protocol: ext-data-control-v1
- Triggering client: CopyQ 16.0.0's clipboard-monitor helper process (`copyq
--clipboard-access monitorClipboard`), via KSystemClipboard
## What's happening (from a coredump backtrace of both threads involved)
Two threads race on the same clipboard offer.
**Main (GUI) thread**, reading the current clipboard content:
```
QHash<QString, QVariant>::emplace<QVariant>(...) <- SIGSEGV here
DataControlOffer::retrieveData(QString const&, QMetaType) const
QMimeDataPrivate::retrieveTypedData(QString const&, QMetaType) const
QMimeData::data(QString const&) const
... (client's own clipboard-changed handler)
KSystemClipboard::changed(QClipboard::Mode)
QObject::event(QEvent*)
...
```
**`ClipboardThread`** (kguiaddons' dedicated Wayland dispatch thread), handling
a newer selection event at the same time:
```
DataControlDevice::ext_data_control_device_v1_selection(ext_data_control_offer_v1*)
QObject::~QObject() <- destroys the previous DataControlOffer
QObject::destroyed(QObject*)
...
wl_display_dispatch_queue_pending()
ClipboardThread::run()
```
Per the ext-data-control-v1 protocol, the client must destroy the previous
`ext_data_control_offer_v1` as soon as a new `selection` event arrives.
`DataControlDevice::ext_data_control_device_v1_selection()` does exactly that,
on its own dispatch thread, with no coordination against the GUI thread, which
may be reading data out of that same offer at the same moment via
`QMimeData::data()` -> `DataControlOffer::retrieveData()`.
When two clipboard or selection changes land close together, the GUI thread can
still be mid-read of offer N when the dispatch thread destroys it to make way
for offer N+1. The GUI thread then reads and writes into freed memory.
## Evidence this is a genuine use-after-free
At the crash, `rdi` (the `this` for `QHash::emplace`, the hash it's writing
into) pointed at:
```
0x59cb...2640: "ariantMap,QString)"
```
That's a fragment of an unrelated Qt method-signature string, not a `QHash`.
The memory that used to back the `DataControlOffer`'s cached `QMimeData` had
already been freed and reused for something else by the time this frame ran.
## Impact
The crashing process is a helper/monitor process that its supervisor respawns
automatically, so it isn't immediately destructive. But it recurs under
ordinary use: repeated crashes from the same clipboard-read code path showed up
on the same machine days apart.
## Suggested fix direction
Synchronize access to a `DataControlOffer` between the thread that destroys it
(on `selection`) and any thread reading its data at the same time. For example,
refcount the offer and defer destruction until in-flight `retrieveData()` calls
finish, or guard offer destruction and data retrieval with a mutex.
I have the full two-thread coredump backtrace locally and can paste more of it
if useful.
---
This report (crash analysis and write-up) was produced with the help of an AI
coding assistant, Claude Code. I reviewed the coredump evidence and backtraces
myself and confirmed the conclusions before filing.
--
You are receiving this mail because:
You are watching all bug changes.