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>
This commit is contained in:
Sergio Martins 2018-03-02 18:36:20 +00:00 committed by Sérgio Martins
parent e5a6e9bb80
commit c082d84d5d
4 changed files with 34 additions and 1 deletions

View File

@ -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)

View File

@ -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;

View File

@ -89,6 +89,7 @@ public:
#endif
#ifndef QT_NO_SHORTCUT
void updateShortcut();
void _q_buddyDeleted();
#endif
inline bool needTextControl() const {
return isTextLabel

View File

@ -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