Skip to content

fix: prevent null pointer dereference by disconnecting textureChanged signal#637

Merged
18202781743 merged 1 commit into
linuxdeepin:masterfrom
18202781743:master
Jun 11, 2026
Merged

fix: prevent null pointer dereference by disconnecting textureChanged signal#637
18202781743 merged 1 commit into
linuxdeepin:masterfrom
18202781743:master

Conversation

@18202781743

Copy link
Copy Markdown
Contributor
  1. The crash occurred during scene graph cleanup when the textureChanged
    signal
    was triggered after preprocessNode had already been cleared
  2. Added explicit disconnection of textureChanged signal connection in
    clearPreprocessNode to prevent accessing freed preprocessNode
  3. This ensures the signal handler doesn't execute after node cleanup

Influence:

  1. Test scene graph cleanup by closing/quitting the application
  2. Verify no crashes occur when transitioning between different viewport
    states
  3. Test viewport rendering with dynamic content updates
  4. Validate no memory leaks from dangling signal connections

fix: 通过断开textureChanged信号连接防止空指针解引用

  1. 崩溃发生在场景图清理过程中,textureChanged信号在preprocessNode被清除
    后触发
  2. 在clearPreprocessNode中添加显式的信号连接断开操作,防止访问已释放
    的preprocessNode
  3. 确保信号处理函数在节点清理后不会执行

Influence:

  1. 通过关闭/退出应用测试场景图清理过程
  2. 验证在不同视口状态切换时不会发生崩溃
  3. 测试带动态内容更新的视口渲染
  4. 验证没有因悬空信号连接导致的内存泄漏

signal

1. The crash occurred during scene graph cleanup when the textureChanged
signal
   was triggered after preprocessNode had already been cleared
2. Added explicit disconnection of textureChanged signal connection in
   clearPreprocessNode to prevent accessing freed preprocessNode
3. This ensures the signal handler doesn't execute after node cleanup

Influence:
1. Test scene graph cleanup by closing/quitting the application
2. Verify no crashes occur when transitioning between different viewport
states
3. Test viewport rendering with dynamic content updates
4. Validate no memory leaks from dangling signal connections

fix: 通过断开textureChanged信号连接防止空指针解引用

1. 崩溃发生在场景图清理过程中,textureChanged信号在preprocessNode被清除
后触发
2. 在clearPreprocessNode中添加显式的信号连接断开操作,防止访问已释放
的preprocessNode
3. 确保信号处理函数在节点清理后不会执行

Influence:
1. 通过关闭/退出应用测试场景图清理过程
2. 验证在不同视口状态切换时不会发生崩溃
3. 测试带动态内容更新的视口渲染
4. 验证没有因悬空信号连接导致的内存泄漏
@18202781743 18202781743 requested review from BLumia and mhduiy June 11, 2026 02:31

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Sorry @18202781743, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@18202781743

Copy link
Copy Markdown
Contributor Author

Thread 1 (Thread 0x7f921d2006c0 (LWP 3649)):
#0 operator() (__closure=0x7f9180203f60) at ./src/dquickitemviewport.cpp:455
#1 operator() (_closure=) at /usr/include/x86_64-linux-gnu/qt6/QtCore/qobjectdefs_impl.h:141
#2 QtPrivate::FunctorCallBase::call_internal<void, QtPrivate::FunctorCall<QtPrivate::IndexesList<>, QtPrivate::List<>, void, Dtk::Quick::DQuickItemViewport::updatePaintNode(QSGNode*, QQuickItem::UpdatePaintNodeData*)::<lambda()> >::call(Dtk::Quick::DQuickItemViewport::updatePaintNode(QSGNode*, QQuickItem::UpdatePaintNodeData*)::<lambda()>&, void**)::<lambda()> > (fn=..., args=) at /usr/include/x86_64-linux-gnu/qt6/QtCore/qobjectdefs_impl.h:65
#3 QtPrivate::FunctorCall<QtPrivate::IndexesList<>, QtPrivate::List<>, void, Dtk::Quick::DQuickItemViewport::updatePaintNode(QSGNode*, QQuickItem::UpdatePaintNodeData*)::<lambda()> >::call (arg=, f=...) at /usr/include/x86_64-linux-gnu/qt6/QtCore/qobjectdefs_impl.h:140
#4 QtPrivate::FunctorCallable<Dtk::Quick::DQuickItemViewport::updatePaintNode(QSGNode*, QQuickItem::UpdatePaintNodeData*)::<lambda()> >::call<QtPrivate::List<>, void> (arg=, f=...) at /usr/include/x86_64-linux-gnu/qt6/QtCore/qobjectdefs_impl.h:362
#5 QtPrivate::QCallableObject<Dtk::Quick::DQuickItemViewport::updatePaintNode(QSGNode*, QQuickItem::UpdatePaintNodeData*)::<lambda()>, QtPrivate::List<>, void>::impl(int, QtPrivate::QSlotObjectBase *, QObject *, void **, bool *) (which=1, this
=0x7f9180203f50, r=, a=, ret=) at /usr/include/x86_64-linux-gnu/qt6/QtCore/qobjectdefs_impl.h:572
#6 0x00007f925596346c in ?? () from /lib/x86_64-linux-gnu/libQt6Core.so.6
#7 0x00007f9255963763 in ?? () from /lib/x86_64-linux-gnu/libQt6Core.so.6
#8 0x00007f9255963763 in ?? () from /lib/x86_64-linux-gnu/libQt6Core.so.6
#9 0x00007f9257842847 in QSGAbstractRenderer::setRootNode(QSGRootNode*) () from /lib/x86_64-linux-gnu/libQt6Quick.so.6
#10 0x00007f925786af20 in QSGRootNode::~QSGRootNode() () from /lib/x86_64-linux-gnu/libQt6Quick.so.6
#11 0x00007f925786af7d in QSGRootNode::~QSGRootNode() () from /lib/x86_64-linux-gnu/libQt6Quick.so.6
#12 0x00007f9257867421 in QSGNode::destroy() () from /lib/x86_64-linux-gnu/libQt6Quick.so.6
#13 0x00007f925786744b in QSGNode::~QSGNode() () from /lib/x86_64-linux-gnu/libQt6Quick.so.6
#14 0x00007f92578678fd in QSGOpacityNode::~QSGOpacityNode() () from /lib/x86_64-linux-gnu/libQt6Quick.so.6
#15 0x00007f9257867421 in QSGNode::destroy() () from /lib/x86_64-linux-gnu/libQt6Quick.so.6
#16 0x00007f925786744b in QSGNode::~QSGNode() () from /lib/x86_64-linux-gnu/libQt6Quick.so.6
#17 0x00007f925786760d in QSGTransformNode::~QSGTransformNode() () from /lib/x86_64-linux-gnu/libQt6Quick.so.6
#18 0x00007f925781ac0e in QQuickWindowPrivate::cleanupNodes() () from /lib/x86_64-linux-gnu/libQt6Quick.so.6
#19 0x00007f9257821be4 in QQuickWindowPrivate::updateDirtyNodes() () from /lib/x86_64-linux-gnu/libQt6Quick.so.6
#20 0x00007f9257816e98 in QQuickWindowPrivate::syncSceneGraph() () from /lib/x86_64-linux-gnu/libQt6Quick.so.6

@deepin-ci-robot

Copy link
Copy Markdown
Contributor

deepin pr auto review

你好!我是CodeGeeX,你的智能编程助手。我已仔细审查了你提供的 Git Diff 输入。

这次代码变更主要做了两件事:一是更新了版权声明的时间范围;二是针对 DQuickItemViewportPrivate::clearPreprocessNode 函数,增加了断开 textureChanged 信号连接的逻辑,以修复在场景图清理过程中可能发生的空指针解引用问题。

以下是我对该代码变更的详细审查意见:

1. 语法逻辑

  • 逻辑正确性:在清除 preprocessNode 时断开相关信号连接,逻辑是完全正确的。这符合 Qt 中“谁监听,谁断开”或“谁持有,谁清理”的原则,有效避免了悬空指针(Dangling Pointer)导致的崩溃。
  • Qt 信号槽机制:使用 QObject::disconnect(connection) 传入 QMetaObject::Connection 对象来断开连接,是 Qt 5 引入的非常安全且推荐的做法,即使该连接已经断开,再次调用 disconnect 也不会引发错误。

2. 代码质量

  • 注释清晰:添加的注释非常棒,清楚地解释了为什么要在这里断开信号(避免场景图清理时信号触发导致空指针解引用),这对于后续维护非常有帮助。
  • 状态重置textureChangedConnection = {};QMetaObject::Connection 对象重置为无效状态,保证了对象状态的一致性,是非常好的编程习惯。

3. 代码性能

  • 性能影响极小QObject::disconnect 的底层实现主要是从内部链表中移除节点,时间复杂度为 O(1),开销极低。相比于可能引发的崩溃 Bug,这点性能损耗完全可以忽略不计。

4. 代码安全

  • 修复了潜在的安全漏洞:原代码在将 preprocessNode 置空后,如果 textureChanged 信号由于场景图的异步清理或多线程渲染机制被触发,槽函数可能会尝试访问已经为 nullptrpreprocessNode,从而导致空指针解引用。此次修复从根源上切断了危险调用,提升了代码的安全性。

💡 改进建议

虽然当前的修复已经很好了,但在代码的健壮性一致性方面,还有进一步优化的空间:

建议一:增加 updateUsePreprocess 相关逻辑的联动检查
clearPreprocessNode 被调用意味着 preprocessNode 被清空了,这可能会影响 updateUsePreprocess() 的计算结果。建议确认:在断开连接并清空节点后,是否需要触发一次状态更新?(这取决于你的业务逻辑,仅作提醒)。

建议二:确保“成对出现”的连接与断开逻辑
建议全局检查 textureChangedConnection 建立(connect)的地方。如果 clearPreprocessNode 负责断开,那么在重新创建 preprocessNode 并建立连接时,是否处理了 textureChangedConnection 已有旧值的情况?推荐在 connect 之前也做一次断开或检查,防止潜在的重复连接。

建议三:考虑使用 Lambda 捕获的上下文安全(如果槽函数是 Lambda)
如果 textureChanged 对应的槽函数是用 Lambda 表达式编写的,除了断开信号,建议检查 Lambda 中是否还通过 this 指针捕获了其他可能被提前销毁的对象。

优化后的代码示例(考虑了连接前的安全断开):

void DQuickItemViewportPrivate::clearPreprocessNode(PreprocessNode *oldNode)
{
    Q_ASSERT(load(preprocessNode) == oldNode);
    preprocessNode = nullptr;

    // 断开 textureChanged 信号连接,避免在场景图清理过程中
    // 信号触发时访问已清除的 preprocessNode 导致空指针解引用
    if (textureChangedConnection) {
        QObject::disconnect(textureChangedConnection);
        textureChangedConnection = {};
    }
}

// 假设这是建立连接的地方,建议保持对称逻辑
void DQuickItemViewportPrivate::setupPreprocessNode(PreprocessNode *newNode)
{
    // 如果存在旧连接,先安全断开
    if (textureChangedConnection) {
        QObject::disconnect(textureChangedConnection);
        textureChangedConnection = {};
    }
    
    preprocessNode = newNode;
    
    // 建立新连接
    if (newNode && textureProvider) { // 伪代码,视实际情况而定
        textureChangedConnection = QObject::connect(textureProvider, &SomeClass::textureChanged, 
                                                    this, &DQuickItemViewportPrivate::onTextureChanged);
    }
}

总结:这是一个质量很高的 Bug 修复提交,逻辑清晰,注释到位,安全性和代码质量都得到了提升。采纳上述建议可以让相关模块的代码更加稳健。

@18202781743

Copy link
Copy Markdown
Contributor Author

崩溃摘要

位置: src/dquickitemviewport.cpp:455-456
信号: SIGSEGV (段错误) - 空指针解引用
进程: dde-shell PID 2681

根本原因

这是一个竞态条件导致的空指针解引用:

  1. lambda捕获问题: 在 updatePaintNode 中创建的lambda(行451-476)通过信号连接到 tp->textureChanged
  2. 生命周期不匹配:
  • 场景图清理时(cleanupNodes),PreprocessNode 被销毁
  • PreprocessNode::~PreprocessNode() 调用 clearPreprocessNode(this)
  • 这将 d->preprocessNode 设置为 nullptr(行134)
  1. 信号未断开:
  • 但是,lambda与 tp->textureChanged 的连接仍然存在
  • 如果在清理过程中 textureChanged 信号被触发,lambda会被调用
  1. 空指针访问:
  • lambda中 load(d->preprocessNode) 返回 nullptr
  • Q_ASSERT(preNode) 在Release版本中不生效
  • 代码继续访问 preNode->imageNode(行456),导致空指针解引用

崩溃堆栈

#0 operator() at ./src/dquickitemviewport.cpp:455
preNode = 0x0 (空指针!)
#1-#5 Qt信号槽机制
#6 QSGAbstractRenderer::setRootNode
#7-#8 QSGRootNode::~QSGRootNode
#13 QQuickWindowPrivate::cleanupNodes
#14 QQuickWindowPrivate::updateDirtyNodes
#15 QQuickWindowPrivate::syncSceneGraph

@deepin-ci-robot

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: 18202781743, BLumia

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@18202781743 18202781743 merged commit f34a84c into linuxdeepin:master Jun 11, 2026
23 of 24 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants