FIX(client,ui): Crash by pressing Esc in nickname dialog

When having the dialog open that allows to set a custom nickname to
another client (UserLocalNicknameDialog) pressing Esc would crash
Mumble. This is due to the Esc key triggering the dialog's reject()
function in which the dialog's smart-pointer object is removed from a
global list and therefore goes out of scope.

This results in the dialog getting deleted, but since this is done in a
member function of the dialog, there is still code accessing the now
deleted object (in Qt's internals) and this is causing the segmentation
fault.

The fix is to use a custom deleter function for the dialog pointer that
instead of using delete on it, calls deleteLater() on it. Thus the
deletion of the object is left to Qt which can then do it at a point at
which it is done with it.

As this is something that could get useful in other locations as well,
the custom deleter function was outsourced into its own file.

Fixes #4666
This commit is contained in:
Robert Adam 2021-01-04 12:34:24 +01:00
parent 391ea23d0f
commit cc3c824499
6 changed files with 50 additions and 8 deletions

View File

@ -61,6 +61,7 @@ set(SHARED_SOURCES
"OSInfo.cpp"
"PasswordGenerator.cpp"
"PlatformCheck.cpp"
"QtUtils.cpp"
"SelfSignedCertificate.cpp"
"ServerAddress.cpp"
"ServerResolver.cpp"

10
src/QtUtils.cpp Normal file
View File

@ -0,0 +1,10 @@
// Copyright 2021 The Mumble Developers. All rights reserved.
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file at the root of the
// Mumble source tree or at <https://www.mumble.info/LICENSE>.
#include <QObject>
void deleteQObject(QObject *object) {
object->deleteLater();
}

20
src/QtUtils.h Normal file
View File

@ -0,0 +1,20 @@
// Copyright 2021 The Mumble Developers. All rights reserved.
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file at the root of the
// Mumble source tree or at <https://www.mumble.info/LICENSE>.
#ifndef MUMBLE_QTUTILS_H_
#define MUMBLE_QTUTILS_H_
class QObject;
/**
* A deleter function to be used for QObjects that must not be deleted using
* delete directly but rather by calling deleteLater() on them (and thus letting
* Qt perform the actual deletion).
*
* This function is intended to be used in smart-pointers holding QObjects.
*/
void deleteQObject(QObject *object);
#endif // MUMBLE_QTUTILS_H_

View File

@ -70,7 +70,7 @@ public:
qiIconMuteSuppressed;
QIcon qiTalkingOn, qiTalkingWhisper, qiTalkingShout, qiTalkingOff;
QMap< unsigned int, UserLocalVolumeDialog * > qmUserVolTracker;
std::unordered_map< unsigned int, std::unique_ptr< UserLocalNicknameDialog > > qmUserNicknameTracker;
std::unordered_map< unsigned int, NicknameDialogPtr > qmUserNicknameTracker;
/// "Action" for when there are no actions available
QAction *qaEmpty;

View File

@ -17,7 +17,7 @@
UserLocalNicknameDialog::UserLocalNicknameDialog(
unsigned int sessionId,
std::unordered_map< unsigned int, std::unique_ptr< UserLocalNicknameDialog > > &qmUserNicknameTracker)
std::unordered_map< unsigned int, NicknameDialogPtr > &qmUserNicknameTracker)
: QDialog(nullptr), m_clientSession(sessionId), m_qmUserNicknameTracker(qmUserNicknameTracker) {
setupUi(this);
@ -49,13 +49,14 @@ void UserLocalNicknameDialog::closeEvent(QCloseEvent *event) {
void UserLocalNicknameDialog::present(
unsigned int sessionId,
std::unordered_map< unsigned int, std::unique_ptr< UserLocalNicknameDialog > > &qmUserNicknameTracker) {
std::unordered_map< unsigned int, NicknameDialogPtr > &qmUserNicknameTracker) {
if (qmUserNicknameTracker.find(sessionId) != qmUserNicknameTracker.end()) {
qmUserNicknameTracker.at(sessionId)->show();
qmUserNicknameTracker.at(sessionId)->raise();
} else {
std::unique_ptr< UserLocalNicknameDialog > userNickname =
std::make_unique< UserLocalNicknameDialog >(sessionId, qmUserNicknameTracker);
// Make sure to use the custom deleter for QObjects that calls deleteLater() on them instead of using
// delete directly as the latter can lead to segmentation faults.
NicknameDialogPtr userNickname(new UserLocalNicknameDialog(sessionId, qmUserNicknameTracker), deleteQObject);
userNickname->show();
qmUserNicknameTracker.insert(std::make_pair(sessionId, std::move(userNickname)));
}

View File

@ -6,12 +6,22 @@
#ifndef MUMBLE_MUMBLE_USERNICKNAME_H_
#define MUMBLE_MUMBLE_USERNICKNAME_H_
#include "QtUtils.h"
#include <memory>
#include <unordered_map>
#include "ClientUser.h"
#include "ui_UserLocalNicknameDialog.h"
class UserLocalNicknameDialog;
/**
* A typedef for a unique pointer (std::unique_ptr) using the deleter-function dedicated for QObjects
* instead of using raw delete
*/
typedef std::unique_ptr< UserLocalNicknameDialog, decltype(&deleteQObject) > NicknameDialogPtr;
class UserLocalNicknameDialog : public QDialog, private Ui::UserLocalNicknameDialog {
Q_OBJECT
Q_DISABLE_COPY(UserLocalNicknameDialog);
@ -21,7 +31,7 @@ class UserLocalNicknameDialog : public QDialog, private Ui::UserLocalNicknameDia
/// The user's original nickname when entering the dialog.
QString m_originalNickname;
std::unordered_map< unsigned int, std::unique_ptr< UserLocalNicknameDialog > > &m_qmUserNicknameTracker;
std::unordered_map< unsigned int, NicknameDialogPtr > &m_qmUserNicknameTracker;
public slots:
void closeEvent(QCloseEvent *event);
@ -32,10 +42,10 @@ public slots:
public:
static void
present(unsigned int sessionId,
std::unordered_map< unsigned int, std::unique_ptr< UserLocalNicknameDialog > > &qmUserNicknameTracker);
std::unordered_map< unsigned int, NicknameDialogPtr > &qmUserNicknameTracker);
UserLocalNicknameDialog(
unsigned int sessionId,
std::unordered_map< unsigned int, std::unique_ptr< UserLocalNicknameDialog > > &qmUserNicknameTracker);
std::unordered_map< unsigned int, NicknameDialogPtr > &qmUserNicknameTracker);
};
#endif