Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions net-view/operation/private/netmanagerthreadprivate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,27 @@ NetManagerThreadPrivate::~NetManagerThreadPrivate()
{
// 先断开所有信号,防止析构期间再有新任务(如singleShot)入队
disconnect();

// 断开 doInit() 中注册的 D-Bus 系统总线连接
// 必须在 m_thread->quit() 之前执行,否则 QDBusConnectionManager 线程
// 在全局清理时会访问已释放的工作线程对象,导致 use-after-free 崩溃
QDBusConnection::systemBus().disconnect("com.deepin.defender.netcheck", "/com/deepin/defender/netcheck", "org.freedesktop.DBus.Properties", "PropertiesChanged", this, SLOT(onNetCheckPropertiesChanged(QString, QVariantMap, QStringList)));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

上面的disconnect(); 不会断开所有跟它的连接么,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

QObject::disconnect()(第111行)只会断开 Qt QMetaObject 管理的 signal-slot 连接,即通过 connect(sender, signal, receiver, slot) 注册的那些连接,以及清除队列中的 QMetaObject::invokeMethod 调用。

QDBusConnection::systemBus().connect() 在 doInit() 中注册的 D-Bus 信号监听,是存储在 QDBusConnection 内部独立数据结构中的。它在底层做的是:

  1. 向 D-Bus 守护进程添加匹配规则(match rule)
  2. 在 QDBusConnection 内部维护一个回调注册表

QObject::disconnect() 感知不到这个注册表,因此无法断开这些 D-Bus 连接。

如果没有第 116-130 行的显式 QDBusConnection::systemBus().disconnect() 调用,当应用退出时 QDBusConnectionManager 全局清理并关闭 qt_default_system_bus 时,仍然会通过其内部注册表尝试向已销毁的 this 对象派发信号,从而导致 use-after-free 崩溃——这正是我们要修复的那个崩溃。

所以这两者是必须的,并不多余。

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

QDBusConnection::systemBus().connect()连接的槽函数,receiver是this,这样this->disconnect也不行么?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个 disconnect() 断开的是 this 作为 sender 的连接,而 QDBusConnection::systemBus().connect() 注册的信号钩子中,thisreceiver。两者作用方向不同。

关键崩溃点在 Qt6 qdbusintegrator.cpp:1140-1152QDBusConnectionPrivate::closeConnection() 在全局清理时会遍历内部的 signalHooks 表直接访问 hook.obj(即 this)。而这个表里的条目要通过 destroyed 信号以 BlockingQueuedConnection 投递到 D-Bus 线程来清理。如果退出时 D-Bus 线程事件循环已停止,objectDestroyed 回调不会执行,signalHooks 里就留下指向已释放对象的悬空指针。

所以两者解决不同层次的问题:this->disconnect() 清理 this 作为 sender 的连接;QDBusConnection::systemBus().disconnect() 主动从 signalHooks 中移除条目,确保 closeConnection() 不会踩到悬空指针。详见 Qt6 qdbusintegrator.cpp:1343hook.obj = receiverqdbusintegrator.cpp:1237-1251objectDestroyed 正常清理流程。

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

同上,这个 disconnect() 断开的是 this 作为 sender 的连接,D-Bus 的 signalHooksthisreceiver 存在 hook.obj 里。所以需要显式 QDBusConnection::systemBus().disconnect() 来清理 QDBusConnectionPrivate 内部的 signalHooks 表,详见上一条回复中的 Qt6 源码分析。

QDBusConnection::systemBus().disconnect("org.freedesktop.login1", "/org/freedesktop/login1", "org.freedesktop.login1.Manager", "PrepareForSleep", this, SLOT(onPrepareForSleep(bool)));
if (ConfigSetting::instance()->supportPortalPromp()) {
QDBusConnection::systemBus().disconnect("org.deepin.dde.Network1",
"/org/deepin/service/SystemNetwork",
"org.deepin.service.SystemNetwork",
"PortalDetected",
this,
SLOT(onPortalDetected(const QString &)));
}

if (m_flags.testFlags(NetType::NetManagerFlag::Net_Airplane)) {
QDBusConnection::systemBus().disconnect("org.deepin.dde.Bluetooth1", "/org/deepin/dde/Bluetooth1", "org.deepin.dde.Bluetooth1", "AdapterAdded", this, SLOT(getAirplaneModeEnabled()));
QDBusConnection::systemBus().disconnect("org.deepin.dde.Bluetooth1", "/org/deepin/dde/Bluetooth1", "org.deepin.dde.Bluetooth1", "AdapterRemoved", this, SLOT(getAirplaneModeEnabled()));
QDBusConnection::systemBus().disconnect("org.deepin.dde.AirplaneMode1", "/org/deepin/dde/AirplaneMode1", "org.freedesktop.DBus.Properties", "PropertiesChanged", this, SLOT(onAirplaneModeEnabledPropertiesChanged(QString, QVariantMap, QStringList)));
}

m_thread->quit();
// 增大等待时间至1000ms,避免50ms定时器回调等正在执行的任务被terminate强杀
m_thread->wait(QDeadlineTimer(1000));
Expand Down
Loading