https://bugs.kde.org/show_bug.cgi?id=524908
Bug ID: 524908
Summary: kdeconnectd crashes in Notification::show() when a
notification icon download finishes after the
KNotification has already been closed (null QPointer
dereference in KNotification::sendEvent)
Classification: Applications
Product: kdeconnect
Version First 26.08.0
Reported In:
Platform: Arch Linux
OS: Linux
Status: REPORTED
Severity: crash
Priority: NOR
Component: common
Assignee: [email protected]
Reporter: [email protected]
CC: [email protected]
Target Milestone: ---
Created attachment 195585
--> https://bugs.kde.org/attachment.cgi?id=195585&action=edit
Core
## Description
### SUMMARY
kdeconnectd segfaults with `KNotification::sendEvent(this=0x0)` when a
`FileTransferJob` that is downloading a phone notification's icon/image
completes after the corresponding `KNotification` has already been closed.
`Notification::m_notification` is a `QPointer<KNotification>` and the
`KNotification` is created with `KNotification::CloseOnTimeout`, so it deletes
itself when the notification server closes it, which nulls the `QPointer`.
`Notification::show()` then dereferences it without a check:
```cpp
// plugins/notifications/notification.cpp (v26.08.0, unchanged on master)
void Notification::show()
{
m_ready = true;
Q_EMIT ready();
if (!m_silent) {
m_notification->sendEvent(); // <-- m_notification can be null here
}
}
```
`show()` is reached from the `FileTransferJob::result` lambda in
`Notification::loadIcon()`. `applyIcon()`, called on the same path, does guard
against `m_notification` being null, but `show()` does not.
### STEPS TO REPRODUCE
1. Pair an Android phone (here: Pixel 10 Pro, KDE Connect Android, protocol v8)
with the "Receive notifications" plugin enabled on the desktop.
2. Have an app on the phone post a notification and then update it with a
large image payload (Google Home "Person seen" camera alerts do this: the
notification appears first, the camera snapshot arrives as an update a few
seconds later).
3. Let the desktop copy of the notification close (timeout or dismiss) while
the image is still being transferred.
Timing-dependent; I have hit it once in the first day of use. An 8-second
window between the notification being shown and the crash was observed.
### OBSERVED RESULT
kdeconnectd dies with SIGSEGV and, because it is started as an XDG autostart
unit, is not restarted — all KDE Connect functionality is silently gone until
the next login.
Kernel: `kdeconnectd[1852]: segfault at 10 ip 00007f77c99adde0 ... error 4 in
libKF6Notifications.so.6.29.0`
### EXPECTED RESULT
The stale icon download is ignored (or the notification is re-sent) instead of
crashing the daemon. A null check on `m_notification` in `Notification::show()`
would avoid the crash.
### SOFTWARE/OS VERSIONS
- KDE Connect: 26.08.0 (Arch package kdeconnect 26.08.0-1)
- KDE Frameworks: 6.29.0 (kcoreaddons 6.29.0-1, knotifications 6.29.0-1)
- Qt: 6.11.2 (qt6-base 6.11.2-2)
- glib2: 2.88.3-1
- Kernel: 7.1.9-arch1-2
- Session: Hyprland (Wayland), Omarchy 4.0.0.alpha
- Notification server: quickshell (org.freedesktop.Notifications), not Plasma
- Phone: Pixel 10 Pro, KDE Connect Android (protocol version 8)
### ADDITIONAL INFORMATION
Backtrace of the crashing (main) thread, symbolized with gdb against the Arch
debuginfod server. Frame 3 shows `this=0x0`. All other threads were idle in
their event loops.
```
#0 std::__uniq_ptr_impl<KNotification::Private, ...>::_M_ptr (this=0x10) at
/usr/include/c++/16/bits/unique_ptr.h:192
#1 std::unique_ptr<KNotification::Private, ...>::get (this=0x10) at
/usr/include/c++/16/bits/unique_ptr.h:491
#2 std::unique_ptr<KNotification::Private, ...>::operator-> (this=0x10) at
/usr/include/c++/16/bits/unique_ptr.h:484
#3 KNotification::sendEvent (this=0x0) at
knotifications-6.29.0/src/knotification.cpp:511
#4 QtPrivate::QSlotObjectBase::call (...) at
qtbase/src/corelib/kernel/qobjectdefs_impl.h:462
#5 doActivate<false> (sender=0x561955c9a950 [FileTransferJob], signal_index=6,
...) at qtbase/src/corelib/kernel/qobject.cpp:4393
#6 QMetaObject::activate<void, KJob*, KJob::QPrivateSignal>
(sender=0x561955c9a950, ..., local_signal_index=3, ...) at qobjectdefs.h:320
#7 KJob::result (this=0x561955c9a950, ...) at
kcoreaddons/build/src/lib/KF6CoreAddons_autogen/include/moc_kjob.cpp:475
#8 KJob::finishJob (this=0x561955c9a950, emitResult=true) at
kcoreaddons-6.29.0/src/lib/jobs/kjob.cpp:115
#9 QtPrivate::QSlotObjectBase::call (...) at qobjectdefs_impl.h:462
#10 doActivate<false> (sender=0x561955a86c50 [QNetworkReplyImpl],
signal_index=12, ...) at qobject.cpp:4393
#11 QNetworkReply::finished (this=<optimized out>) at
Network_autogen/include/moc_qnetworkreply.cpp:330
#12 QNetworkReplyImplPrivate::finished (this=0x561955f01930) at
qtbase/src/network/access/qnetworkreplyimpl.cpp:629
#13 QNetworkAccessBackend::finished (this=0x561955e51e60) at
qtbase/src/network/access/qnetworkaccessbackend.cpp:746
#14 QNetworkAccessFileBackend::uploadReadyReadSlot (this=0x561955e51e60) at
qtbase/src/network/access/qnetworkaccessfilebackend.cpp:169
#15 doActivate<false> (sender=0x56195598c220, signal_index=3, ...) at
qobject.cpp:4405
#16 QtPrivate::QSlotObjectBase::call (...) at qobjectdefs_impl.h:462
#17 doActivate<false> (sender=0x5619556ad700, signal_index=3, ...) at
qobject.cpp:4393
#18 QtPrivate::QSlotObjectBase::call (...) at qobjectdefs_impl.h:462
#19 doActivate<false> (sender=0x561955d8ca70, signal_index=8, ...) at
qobject.cpp:4393
#20 doActivate<false> (sender=0x5619559497f0, signal_index=8, ...) at
qobject.cpp:4405
#21 QAbstractSocket::disconnectFromHost (this=0x5619559497f0) at
qtbase/src/network/socket/qabstractsocket.cpp:2737
#22 QAbstractSocketPrivate::canReadNotification (this=<optimized out>) at
qabstractsocket.cpp:647
#23 QAbstractSocketEngine::readNotification (this=<optimized out>) at
qabstractsocketengine.cpp:123
#24 QReadNotifier::event (this=<optimized out>, e=<optimized out>) at
qnativesocketengine.cpp:1247
#25 QApplicationPrivate::notify_helper(QObject*, QEvent*) () from
/usr/lib/libQt6Widgets.so.6
#26 QCoreApplication::notifyInternal2 (receiver=0x5619558ee370, ...) at
qcoreapplication.cpp:1126
#27 QCoreApplication::sendEvent (...) at qcoreapplication.cpp:1571
#28 socketNotifierSourceDispatch (source=0x56195527d620) at
qeventdispatcher_glib.cpp:78
#29 g_main_dispatch (context=0x7f77bc000f00) at ../glib/glib/gmain.c:3591
#30 g_main_context_iteration () from /usr/lib/libglib-2.0.so.0
#31 QEventDispatcherGlib::processEvents(...) from /usr/lib/libQt6Core.so.6
#32 QEventLoop::exec(...) from /usr/lib/libQt6Core.so.6
#33 QCoreApplication::exec() from /usr/lib/libQt6Core.so.6
#34 main (kdeconnectd)
```
The sender in frame 5 was confirmed to be a `FileTransferJob` by reading its
vtable pointer in the core (`vtable for FileTransferJob + 16` in
libkdeconnectcore.so.26); the sender in frame 10 is a `QNetworkReplyImpl`
(the `file://` PUT that FileTransferJob uses to write the payload to disk).
Frames 21-22 are the payload socket from the phone reaching end-of-stream,
which is what completed the transfer.
Registers at the fault: `rdi = 0x0` (the `this` pointer), faulting instruction
`mov 0x10(%rdi),%rax` (loading `d` from a null KNotification).
Sequence observed on this machine:
- 14:31:32 — the notification from the phone ("Home" app, camera alert) was
displayed on the desktop without an image.
- 14:31:40 — kdeconnectd crashed while finishing a payload transfer from the
phone, in the `FileTransferJob::result` handler for that notification's icon.
Not related to earlier reports 447059 (crash in Device::reloadPlugins) or
410397 (fixed in 1.3.5); the stack is different.
Proposed fix (one line):
```cpp
void Notification::show()
{
m_ready = true;
Q_EMIT ready();
if (!m_silent && m_notification) {
m_notification->sendEvent();
}
}```
--
You are receiving this mail because:
You are watching all bug changes.