From d58b2f3b5afb45bb2fcf354f3ba073a79b5c41b3 Mon Sep 17 00:00:00 2001 From: yeshanshan Date: Fri, 15 May 2026 18:39:28 +0800 Subject: [PATCH] fix: resolve potential crash and resource management issues in network module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Add proper destructor for DccNetwork to disconnect signals before object destruction 2. Fix potential null pointer dereference in root() method by adding null check for m_manager 3. Clean up VPN state update timer in NetManagerThreadPrivate destructor to prevent dangling timer callbacks 4. Increase thread wait timeout from 200ms to 1000ms in destructor to avoid forced thread termination during active operations 5. Replace QTimer::singleShot with a member timer for VPN state updates to prevent callback accumulation and ensure proper cleanup 6. Add mutex protection for NetworkController singleton instance and free operations to fix thread-safety issues Log: Fixed potential crashes and resource leaks in network management; improved thread safety for singleton access Influence: 1. Test network plugin initialization and destruction cycles repeatedly 2. Verify DccNetwork object creation and destruction does not cause signal-slot errors 3. Test VPN connection state changes and verify no duplicate timer callbacks 4. Test rapid VPN state transitions to confirm proper timer reset behavior 5. Test NetworkController::instance() and free() from multiple threads 6. Verify thread termination during active network operations completes gracefully 7. Check for any memory leaks or dangling pointers after repeated initialization fix: 修复网络模块中的潜在崩溃和资源管理问题 1. 为 DccNetwork 添加正确的析构函数,在对象销毁前断开信号连接 2. 修复 root() 方法中潜在的空指针解引用问题,添加对 m_manager 的空值检查 3. 清理 NetManagerThreadPrivate 析构函数中的 VPN 状态更新定时器,防止悬 空定时器回调 4. 将析构函数中的线程等待超时从 200ms 增加到 1000ms,避免正在执行的任务 被强制终止 5. 用成员定时器替换 QTimer::singleShot 进行 VPN 状态更新,防止回调累积并 确保正确清理 6. 为 NetworkController 单例的 instance() 和 free() 操作添加互斥锁保护, 解决线程安全问题 Log: 修复了网络管理中的潜在崩溃和资源泄漏问题,改善了单例访问的线程安 全性 Influence: 1. 反复测试网络插件的初始化和销毁周期 2. 验证 DccNetwork 对象的创建和销毁不会导致信号槽错误 3. 测试 VPN 连接状态变化,确认没有重复的定时器回调 4. 测试快速切换 VPN 状态,验证定时器重置行为正确 5. 从多个线程测试 NetworkController::instance() 和 free() 6. 验证线程在活跃网络操作期间能优雅地终止 7. 检查重复初始化后是否有内存泄漏或悬空指针 PMS: BUG-353781 --- dcc-network/operation/dccnetwork.cpp | 11 ++++++++-- dcc-network/operation/dccnetwork.h | 3 ++- .../private/netmanagerthreadprivate.cpp | 22 +++++++++++++++---- .../private/netmanagerthreadprivate.h | 1 + src/networkcontroller.cpp | 11 +++++++--- 5 files changed, 38 insertions(+), 10 deletions(-) diff --git a/dcc-network/operation/dccnetwork.cpp b/dcc-network/operation/dccnetwork.cpp index 644a1cf3..887dbb06 100644 --- a/dcc-network/operation/dccnetwork.cpp +++ b/dcc-network/operation/dccnetwork.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2024 - 2027 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2024 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "dccnetwork.h" @@ -33,9 +33,16 @@ DccNetwork::DccNetwork(QObject *parent) QMetaObject::invokeMethod(this, "init", Qt::QueuedConnection); } +DccNetwork::~DccNetwork() +{ + if (m_manager) { + disconnect(m_manager, &NetManager::request, this, &DccNetwork::request); + } +} + NetItem *DccNetwork::root() const { - return m_manager->root(); + return m_manager ? m_manager->root() : nullptr; } bool DccNetwork::CheckPasswordValid(const QString &key, const QString &password) diff --git a/dcc-network/operation/dccnetwork.h b/dcc-network/operation/dccnetwork.h index 73582afd..6e587987 100644 --- a/dcc-network/operation/dccnetwork.h +++ b/dcc-network/operation/dccnetwork.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2024 - 2027 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2024 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef DCCNETWORK_H @@ -23,6 +23,7 @@ class DccNetwork : public QObject public: explicit DccNetwork(QObject *parent = nullptr); + ~DccNetwork() override; NetItem *root() const; Q_INVOKABLE static bool CheckPasswordValid(const QString &key, const QString &password); diff --git a/net-view/operation/private/netmanagerthreadprivate.cpp b/net-view/operation/private/netmanagerthreadprivate.cpp index 6f80ecb3..efa5ecb9 100644 --- a/net-view/operation/private/netmanagerthreadprivate.cpp +++ b/net-view/operation/private/netmanagerthreadprivate.cpp @@ -98,6 +98,7 @@ NetManagerThreadPrivate::NetManagerThreadPrivate() , m_netCheckAvailable(false) , m_isSleeping(false) , m_showPageTimer(nullptr) + , m_vpnStateUpdateTimer(nullptr) , m_supportWireless(false) { moveToThread(m_thread); @@ -106,12 +107,15 @@ NetManagerThreadPrivate::NetManagerThreadPrivate() NetManagerThreadPrivate::~NetManagerThreadPrivate() { + // 先断开所有信号,防止析构期间再有新任务(如singleShot)入队 + disconnect(); m_thread->quit(); - m_thread->wait(QDeadlineTimer(200)); + // 增大等待时间至1000ms,避免50ms定时器回调等正在执行的任务被terminate强杀 + m_thread->wait(QDeadlineTimer(1000)); if (m_thread->isRunning()) { m_thread->terminate(); } - m_thread->wait(QDeadlineTimer(200)); + m_thread->wait(QDeadlineTimer(500)); delete m_thread; } @@ -416,6 +420,9 @@ void NetManagerThreadPrivate::doInit() } Q_EMIT itemAdded("Root", vpnControlItem); + m_vpnStateUpdateTimer = new QTimer(this); + m_vpnStateUpdateTimer->setSingleShot(true); + m_vpnStateUpdateTimer->setInterval(55); auto updateVPNConnectionState = [this]() { auto itemList = NetworkController::instance()->vpnController()->items(); NetType::NetDeviceStatus state = NetType::DS_Disconnected; @@ -431,8 +438,10 @@ void NetManagerThreadPrivate::doInit() } Q_EMIT dataChanged(DataChanged::VPNConnectionStateChanged, "NetVPNControlItem", QVariant::fromValue(state)); }; - auto vpnConnectionStateChanged = [this, updateVPNConnectionState] { - QTimer::singleShot(50, this, updateVPNConnectionState); + connect(m_vpnStateUpdateTimer, &QTimer::timeout, this, updateVPNConnectionState); + auto vpnConnectionStateChanged = [this] { + // 使用成员定时器,重复触发时自动重置计时,防止多次触发累积 + m_vpnStateUpdateTimer->start(); }; auto vpnItemChanged = [this, vpnConnectionStateChanged] { @@ -596,6 +605,11 @@ void NetManagerThreadPrivate::clearData() delete m_autoScanTimer; m_autoScanTimer = nullptr; } + if (m_vpnStateUpdateTimer) { + m_vpnStateUpdateTimer->stop(); + delete m_vpnStateUpdateTimer; + m_vpnStateUpdateTimer = nullptr; + } if (m_secretAgent) { delete m_secretAgent; m_secretAgent = nullptr; diff --git a/net-view/operation/private/netmanagerthreadprivate.h b/net-view/operation/private/netmanagerthreadprivate.h index 803b8f62..326554a3 100644 --- a/net-view/operation/private/netmanagerthreadprivate.h +++ b/net-view/operation/private/netmanagerthreadprivate.h @@ -299,6 +299,7 @@ protected Q_SLOTS: QMap m_detailsItemsMap; // 存储 NetworkDetails 指针到唯一ID的映射 QString m_showPageCmd; QTimer *m_showPageTimer; + QTimer *m_vpnStateUpdateTimer; QString m_newVPNuuid; bool m_supportWireless; }; diff --git a/src/networkcontroller.cpp b/src/networkcontroller.cpp index f8c3a899..502e9166 100644 --- a/src/networkcontroller.cpp +++ b/src/networkcontroller.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2018 - 2022 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2018 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -18,6 +18,8 @@ #include "wirelessdevice.h" #include "connectivityhandler.h" +#include + // const static QString networkService = "org.deepin.dde.Network1"; // const static QString networkPath = "/org/deepin/dde/Network1"; static QString localeName; @@ -102,10 +104,12 @@ void NetworkController::onDeviceAdded(QList device) NetworkController::~NetworkController() = default; +// 文件级互斥锁,instance() 和 free() 共用,防止数据竞争 +Q_GLOBAL_STATIC(QMutex, s_networkControllerMutex) + NetworkController *NetworkController::instance() { - static QMutex m; - QMutexLocker locker(&m); + QMutexLocker locker(s_networkControllerMutex()); if (!m_networkController) { m_networkController = new NetworkController; }; @@ -114,6 +118,7 @@ NetworkController *NetworkController::instance() void NetworkController::free() { + QMutexLocker locker(s_networkControllerMutex()); if (m_networkController) { m_networkController->deleteLater(); m_networkController = nullptr;