[education/kstars] kstars/auxiliary: Fix crash when a stale EkosLive dialog response arrives after the dialog was dismissed
Jasem Mutlaq <[email protected]>
| Newsgroups | gmane.comp.kde.cvs |
|---|---|
| Message-ID | <[email protected]> |
Git commit 2fb72b3135d957dd0b743b26dbb573b29dbc46d4 by Jasem Mutlaq, on behalf of Ilia Belov.
Committed on 28/07/2026 at 07:03.
Pushed by mutlaqja into branch 'master'.
Fix crash when a stale EkosLive dialog response arrives after the dialog was dismissed
`KSMessageBox` is a reused singleton; buttons of a dismissed dialog stay as its children until the next dialog calls `reset()`. `selectResponse()` matches a button by text and clicks it unconditionally, and `Message::processDialogResponse()` forwards remote `dialog_get_response` frames without validation. So a response sent after the dialog was already dismissed (closed on the desktop, timed out, or a duplicate tap) clicks a stale button, re-fires `accepted()`/`rejected()` and crashes KStars with SIGSEGV during widget teardown.
Reproduce:
1. Open the StellarMate app.
2. Trigger any Ekos dialog ("Cover the telescope…").
3. Dismiss it on the KStars desktop.
4. Tap a button for that dialog in the app → crash.
Fix: in `selectResponse()`, ignore responses when the dialog is not visible, and ignore duplicates while a response is in flight (`m_ResponseInProgress`, cleared on accepted/rejected/reset). Normal single-response behavior unchanged.
M +17 -0 kstars/auxiliary/ksmessagebox.cpp
M +5 -0 kstars/auxiliary/ksmessagebox.h
https://invent.kde.org/education/kstars/-/commit/2fb72b3135d957dd0b743b26dbb573b29dbc46d4
diff --git a/kstars/auxiliary/ksmessagebox.cpp b/kstars/auxiliary/ksmessagebox.cpp
index 65ba058bbe..cb4f0c51a4 100644
--- a/kstars/auxiliary/ksmessagebox.cpp
+++ b/kstars/auxiliary/ksmessagebox.cpp
@@ -46,12 +46,14 @@ KSMessageBox::KSMessageBox() : QMessageBox()
connect(this, &KSMessageBox::rejected, [this]()
{
+ m_ResponseInProgress = false;
m_ProgressTimer.stop();
Q_EMIT newMessage(QJsonObject());
});
connect(this, &KSMessageBox::accepted, [this]()
{
+ m_ResponseInProgress = false;
m_ProgressTimer.stop();
Q_EMIT newMessage(QJsonObject());
});
@@ -157,6 +159,7 @@ void KSMessageBox::setupTimeout(quint32 timeout)
void KSMessageBox::reset()
{
+ m_ResponseInProgress = false;
m_ProgressTimer.stop();
resetTimeout();
@@ -296,12 +299,26 @@ QJsonObject KSMessageBox::createMessageObject()
bool KSMessageBox::selectResponse(const QString &button)
{
+ // Ignore responses when no dialog is currently shown. The message box is a
+ // reused singleton, so the buttons of a previously dismissed dialog linger as
+ // children; clicking one of those would re-emit accepted()/rejected() and could
+ // tear down widgets while their signals are still being processed.
+ if (!isVisible())
+ return false;
+
+ // Ignore duplicate or re-entrant responses. animateClick() dismisses the dialog
+ // asynchronously, so a second response (e.g. a remote EkosLive client sending
+ // dialog_get_response repeatedly) could arrive before the dialog hides.
+ if (m_ResponseInProgress)
+ return false;
+
for (const auto oneButton : findChildren<QPushButton * >())
{
const QString buttonText = oneButton->text().remove("&");
if (button == buttonText)
{
+ m_ResponseInProgress = true;
oneButton->animateClick();
return true;
}
diff --git a/kstars/auxiliary/ksmessagebox.h b/kstars/auxiliary/ksmessagebox.h
index 0b8f8a71b9..079f90c242 100644
--- a/kstars/auxiliary/ksmessagebox.h
+++ b/kstars/auxiliary/ksmessagebox.h
@@ -63,6 +63,11 @@ class KSMessageBox: public QMessageBox
// Dialog timeout in seconds
quint32 m_Timeout {60};
+ // True while a button response is being processed. Guards against duplicate
+ // or stale programmatic responses (e.g. from a remote EkosLive client) that
+ // would otherwise click a button on an already-dismissed dialog.
+ bool m_ResponseInProgress {false};
+
static KSMessageBox *m_Instance;
void reset();