summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSergio Martins <sergio.martins@kdab.com>2018-03-02 18:36:20 +0000
committerSérgio Martins <sergio.martins@kdab.com>2018-03-08 22:36:49 +0000
commitc082d84d5d7fdbfb64de18f06a8ddbc77f2e6de4 (patch)
tree1d881e4d7610fb0f5d94ac6b8f586886c45db46b
parente5a6e9bb80fb2427228f70488b8839b4aa0b4261 (diff)
Fix QLabel crash if buddy's lifetime is too short
If buddy is deleted then QLabel will still try to deliver the shortcut event to it, in QLabel::event(), and cras. Besides connecting to destroyed() which alone fixes the crash, also hardened it and guarded against dereferencing null buddy, in the unlikely event of someone adding features to QLabel. The second part is suitable for backporting to the LTS branches. Task-Id: QTBUG-66841 Change-Id: Ib6a36a9f99cee31afe1f88eb77f5b6cafb8b1086 Reviewed-by: Olivier Goffart (Woboq GmbH) <ogoffart@woboq.com>
-rw-r--r--src/widgets/widgets/qlabel.cpp19
-rw-r--r--src/widgets/widgets/qlabel.h3
-rw-r--r--src/widgets/widgets/qlabel_p.h1
-rw-r--r--tests/auto/widgets/widgets/qlabel/tst_qlabel.cpp12
4 files changed, 34 insertions, 1 deletions
diff --git a/src/widgets/widgets/qlabel.cpp b/src/widgets/widgets/qlabel.cpp
index ee4825d3e5..046acfeea9 100644
--- a/src/widgets/widgets/qlabel.cpp
+++ b/src/widgets/widgets/qlabel.cpp
@@ -963,7 +963,9 @@ bool QLabel::event(QEvent *e)
if (type == QEvent::Shortcut) {
QShortcutEvent *se = static_cast<QShortcutEvent *>(e);
if (se->shortcutId() == d->shortcutId) {
- QWidget * w = d->buddy;
+ QWidget *w = d->buddy;
+ if (!w)
+ return QFrame::event(e);
if (w->focusPolicy() != Qt::NoFocus)
w->setFocus(Qt::ShortcutFocusReason);
#if QT_CONFIG(abstractbutton)
@@ -1163,7 +1165,15 @@ void QLabelPrivate::updateLabel()
void QLabel::setBuddy(QWidget *buddy)
{
Q_D(QLabel);
+
+ if (d->buddy)
+ disconnect(d->buddy, SIGNAL(destroyed()), this, SLOT(_q_buddyDeleted()));
+
d->buddy = buddy;
+
+ if (buddy)
+ connect(buddy, SIGNAL(destroyed()), this, SLOT(_q_buddyDeleted()));
+
if (d->isTextLabel) {
if (d->shortcutId)
releaseShortcut(d->shortcutId);
@@ -1204,6 +1214,13 @@ void QLabelPrivate::updateShortcut()
shortcutId = q->grabShortcut(QKeySequence::mnemonic(text));
}
+
+void QLabelPrivate::_q_buddyDeleted()
+{
+ Q_Q(QLabel);
+ q->setBuddy(nullptr);
+}
+
#endif // QT_NO_SHORTCUT
#if QT_CONFIG(movie)
diff --git a/src/widgets/widgets/qlabel.h b/src/widgets/widgets/qlabel.h
index e1cc333a1c..2f5db5a7d3 100644
--- a/src/widgets/widgets/qlabel.h
+++ b/src/widgets/widgets/qlabel.h
@@ -158,6 +158,9 @@ private:
#endif
Q_PRIVATE_SLOT(d_func(), void _q_linkHovered(const QString &))
+#ifndef QT_NO_SHORTCUT
+ Q_PRIVATE_SLOT(d_func(), void _q_buddyDeleted())
+#endif
friend class QTipLabel;
friend class QMessageBoxPrivate;
friend class QBalloonTip;
diff --git a/src/widgets/widgets/qlabel_p.h b/src/widgets/widgets/qlabel_p.h
index e05a5b5c35..59188563a9 100644
--- a/src/widgets/widgets/qlabel_p.h
+++ b/src/widgets/widgets/qlabel_p.h
@@ -89,6 +89,7 @@ public:
#endif
#ifndef QT_NO_SHORTCUT
void updateShortcut();
+ void _q_buddyDeleted();
#endif
inline bool needTextControl() const {
return isTextLabel
diff --git a/tests/auto/widgets/widgets/qlabel/tst_qlabel.cpp b/tests/auto/widgets/widgets/qlabel/tst_qlabel.cpp
index 500ec2e1fa..84071962bc 100644
--- a/tests/auto/widgets/widgets/qlabel/tst_qlabel.cpp
+++ b/tests/auto/widgets/widgets/qlabel/tst_qlabel.cpp
@@ -178,9 +178,11 @@ void tst_QLabel::setBuddy()
test_label= new QLabel( test_box );
test_label->setText( "&Test with a buddy" );
QWidget *test_edit = new QLineEdit( test_box );
+ QWidget *test_edit2 = new QLineEdit( test_box );
QVBoxLayout *layout = new QVBoxLayout(test_box);
layout->addWidget(test_label);
layout->addWidget(test_edit);
+ layout->addWidget(test_edit2);
test_box->show();
qApp->setActiveWindow(test_box);
QVERIFY(test_box->isActiveWindow());
@@ -190,6 +192,16 @@ void tst_QLabel::setBuddy()
QVERIFY( !test_edit->hasFocus() );
QTest::keyClick( test_box, 't', Qt::AltModifier );
QVERIFY( test_edit->hasFocus() );
+
+ // Setting a new buddy should disconnect the old one's destroyed() signal
+ test_label->setBuddy(test_edit2);
+ delete test_edit;
+ QCOMPARE(test_label->buddy(), test_edit2);
+
+ // And deleting our own buddy should disconnect and not crash
+ delete test_edit2;
+ QTest::keyClick(test_box, 't', Qt::AltModifier );
+
delete test_box;
}
#endif