Skip to content

fix: prevent crash from invalid indexes in window monitoring#1584

Merged
18202781743 merged 1 commit intolinuxdeepin:masterfrom
18202781743:treeland-adapter
Apr 30, 2026
Merged

fix: prevent crash from invalid indexes in window monitoring#1584
18202781743 merged 1 commit intolinuxdeepin:masterfrom
18202781743:treeland-adapter

Conversation

@18202781743
Copy link
Copy Markdown
Contributor

@18202781743 18202781743 commented Apr 30, 2026

Add validity checks for model indexes and window pointers to prevent
crashes when accessing data from window monitoring models. The crash
occurred in treeland environment due to accessing invalid indexes
or null window pointers, particularly in the data() methods of
AbstractWindowMonitor and RoleCombineModel, and when matching
windows in TaskManager::handleWindowAdded.

Log: Fixed a crash in the dock's window monitoring system by adding null
and index validity checks

Influence:

  1. Test dock stability under treeland by switching between multiple
    windows rapidly
  2. Verify dock does not crash when adding/removing windows dynamically
  3. Test with invalid model states (empty models, invalid indexes)
  4. Verify window matching still works correctly for active apps
  5. Test combination models with various role mappings

fix: 修复窗口监控中无效索引导致的崩溃问题

在窗口监控模型中增加模型索引和窗口指针的有效性检查,防止因访问
无效索引或空窗口指针导致崩溃。该崩溃在 treeland 环境下偶发,主要
AbstractWindowMonitorRoleCombineModel 的数据访问方法以及
TaskManager::handleWindowAdded 中的窗口匹配逻辑引起。

Log: 修复了 dock 窗口监控系统中因缺少空指针和索引有效性检查导致的崩溃
问题

Influence:

  1. 在 treeland 环境下快速切换多个窗口测试 dock 稳定性
  2. 验证动态添加/删除窗口时 dock 不会崩溃
  3. 测试空模型和无效索引等异常状态
  4. 验证活跃应用的窗口匹配功能正常
  5. 测试多种角色映射的组合模型

Summary by Sourcery

Prevent crashes in the dock window monitoring system by validating model indexes and tracked window pointers before accessing data.

Bug Fixes:

  • Guard window monitoring models against invalid QModelIndex access and null tracked window pointers to avoid crashes when querying data.
  • Ensure TaskManager only matches active app windows when the starting model index is valid, preventing crashes on empty or inconsistent models.

Enhancements:

  • Add defensive validity checks in RoleCombineModel and AbstractWindowMonitor to make window tracking and data retrieval more robust against inconsistent model states.

Chores:

  • Update SPDX copyright headers to cover years 2024–2026.

Add validity checks for model indexes and window pointers to prevent
crashes when accessing data from window monitoring models. The crash
occurred in treeland environment due to accessing invalid indexes
or null window pointers, particularly in the `data()` methods of
`AbstractWindowMonitor` and `RoleCombineModel`, and when matching
windows in `TaskManager::handleWindowAdded`.

Log: Fixed a crash in the dock's window monitoring system by adding null
and index validity checks

Influence:
1. Test dock stability under treeland by switching between multiple
windows rapidly
2. Verify dock does not crash when adding/removing windows dynamically
3. Test with invalid model states (empty models, invalid indexes)
4. Verify window matching still works correctly for active apps
5. Test combination models with various role mappings

fix: 修复窗口监控中无效索引导致的崩溃问题

在窗口监控模型中增加模型索引和窗口指针的有效性检查,防止因访问
无效索引或空窗口指针导致崩溃。该崩溃在 treeland 环境下偶发,主要
由 `AbstractWindowMonitor` 和 `RoleCombineModel` 的数据访问方法以及
`TaskManager::handleWindowAdded` 中的窗口匹配逻辑引起。

Log: 修复了 dock 窗口监控系统中因缺少空指针和索引有效性检查导致的崩溃
问题

Influence:
1. 在 treeland 环境下快速切换多个窗口测试 dock 稳定性
2. 验证动态添加/删除窗口时 dock 不会崩溃
3. 测试空模型和无效索引等异常状态
4. 验证活跃应用的窗口匹配功能正常
5. 测试多种角色映射的组合模型
@18202781743
Copy link
Copy Markdown
Contributor Author

#0 0x00007fdcf953b36f in dock::AbstractWindowMonitor::data (this=0x55c0203c0010, index=..., role=257) at /home/deepin/dde-shell/panels/dock/taskmanager/abstractwindowmonitor.cpp:86
#1 0x00007fdd06dcb44e in QSortFilterProxyModel::data (this=, index=, role=257) at ./src/corelib/itemmodels/qsortfilterproxymodel.cpp:2216
#2 0x00007fdcf954df24 in RoleCombineModel::data (this=0x55c020393950, index=..., role=257) at /home/deepin/dde-shell/panels/dock/taskmanager/rolecombinemodel.cpp:358
#3 0x00007fdcf9570aee in dock::DockCombineModel::data (this=0x55c020393950, index=..., role=257) at /home/deepin/dde-shell/panels/dock/taskmanager/dockcombinemodel.cpp:71
#4 0x00007fdd06da0f61 in QAbstractItemModel::match (this=, start=..., role=257, value=..., hits=1, flags=...) at ./src/corelib/itemmodels/qabstractitemmodel.cpp:2540
#5 0x00007fdcf958b540 in dock::TaskManager::handleWindowAdded (this=0x55c0203688f0, window=...) at /home/deepin/dde-shell/panels/dock/taskmanager/taskmanager.cpp:331
#6 0x00007fdcf959555f in QtPrivate::FunctorCall<QtPrivate::IndexesList<0>, QtPrivate::List<QPointerdock::AbstractWindow >, void, void (dock::TaskManager::)(QPointerdock::AbstractWindow)>::call(void
(dock::TaskManager::
)(QPointerdock::AbstractWindow), dock::TaskManager*, void**)::{lambda()#1}::operator()() const (__closure=0x7ffe8a00f4d0) at
/usr/include/x86_64-linux-gnu/qt6/QtCore/qobjectdefs_impl.h:152
#7 0x00007fdcf9596441 in QtPrivate::FunctorCallBase::call_internal<void, QtPrivate::FunctorCall<QtPrivate::IndexesList<0>, QtPrivate::List<QPointerdock::AbstractWindow >, void, void
(dock::TaskManager::)(QPointerdock::AbstractWindow)>::call(void (dock::TaskManager::)(QPointerdock::AbstractWindow), dock::TaskManager*, void**)::{lambda()#1}>(void**,
QtPrivate::FunctorCall<QtPrivate::IndexesList<0>, QtPrivate::List<QPointerdock::AbstractWindow >, void, void (dock::TaskManager::)(QPointerdock::AbstractWindow)>::call(void
(dock::TaskManager::
)(QPointerdock::AbstractWindow), dock::TaskManager*, void**)::{lambda()#1}&&) (args=0x7ffe8a00f680, fn=...) at /usr/include/x86_64-linux-gnu/qt6/QtCore/qobjectdefs_impl.h:65

#8 0x00007fdcf95955ee in QtPrivate::FunctorCall<QtPrivate::IndexesList<0>, QtPrivate::List<QPointerdock::AbstractWindow >, void, void (dock::TaskManager::)(QPointerdock::AbstractWindow)>::call
(f=(void (dock::TaskManager::
)(dock::TaskManager * const, QPointerdock::AbstractWindow)) 0x7fdcf958b34e <dock::TaskManager::handleWindowAdded(QPointerdock::AbstractWindow)>, o=0x55c0203688f0,
arg=0x7ffe8a00f680) at /usr/include/x86_64-linux-gnu/qt6/QtCore/qobjectdefs_impl.h:151
#9 0x00007fdcf9593f88 in QtPrivate::FunctionPointer<void (dock::TaskManager::)(QPointerdock::AbstractWindow)>::call<QtPrivate::List<QPointerdock::AbstractWindow >, void>
(f=(void (dock::TaskManager::
)(dock::TaskManager * const, QPointerdock::AbstractWindow)) 0x7fdcf958b34e <dock::TaskManager::handleWindowAdded(QPointerdock::AbstractWindow)>, o=0x55c0203688f0,
arg=0x7ffe8a00f680) at /usr/include/x86_64-linux-gnu/qt6/QtCore/qobjectdefs_impl.h:199
#10 0x00007fdcf9592ee9 in QtPrivate::QCallableObject<void (dock::TaskManager::)(QPointerdock::AbstractWindow), QtPrivate::List<QPointerdock::AbstractWindow >, void>::impl
(which=1, this_=0x55c020354860, r=0x55c0203688f0, a=0x7ffe8a00f680, ret=0x0) at /usr/include/x86_64-linux-gnu/qt6/QtCore/qobjectdefs_impl.h:570
#11 0x00007fdd06c3d46c in QtPrivate::QSlotObjectBase::call (a=0x7ffe8a00f680, r=0x55c0203688f0, this=0x55c020354860, this=, r=, a=)
at ./src/corelib/kernel/qobjectdefs_impl.h:486
--Type for more, q to quit, c to continue without paging--
#12 doActivate (sender=0x55c0203c0010, signal_index=26, argv=0x7ffe8a00f680) at ./src/corelib/kernel/qobject.cpp:4120
#13 0x00007fdcf94e5413 in dock::AbstractWindowMonitor::windowAdded (this=0x55c0203c0010, _t1=...)
at /home/deepin/dde-shell/build/panels/dock/taskmanager/dock-taskmanager_autogen/EWIEGA46WW/moc_abstractwindowmonitor.cpp:196
#14 0x00007fdcf959dae1 in dock::TreeLandWindowMonitor::handleForeignToplevelHandleAdded (this=0x55c0203c0010) at /home/deepin/dde-shell/panels/dock/taskmanager/treelandwindowmonitor.cpp:211
#15 0x00007fdcf95a1bff in QtPrivate::FunctorCall<QtPrivate::IndexesList<>, QtPrivate::List<>, void, void (dock::TreeLandWindowMonitor::
)()>::call(void (dock::TreeLandWindowMonitor::)(),
dock::TreeLandWindowMonitor
, void**)::{lambda()#1}::operator()() const (__closure=0x7ffe8a00f800) at /usr/include/x86_64-linux-gnu/qt6/QtCore/qobjectdefs_impl.h:152

#16 0x00007fdcf95a22e2 in QtPrivate::FunctorCallBase::call_internal<void, QtPrivate::FunctorCall<QtPrivate::IndexesList<>, QtPrivate::List<>, void, void (dock::TreeLandWindowMonitor::)()>::call(void
(dock::TreeLandWindowMonitor::
)(), dock::TreeLandWindowMonitor*, void**)::{lambda()#1}>(void**, QtPrivate::FunctorCall<QtPrivate::IndexesList<>, QtPrivate::List<>, void, void
(dock::TreeLandWindowMonitor::)()>::call(void (dock::TreeLandWindowMonitor::)(), dock::TreeLandWindowMonitor*, void**)::{lambda()#1}&&) (args=0x7ffe8a00f948, fn=...) at
/usr/include/x86_64-linux-gnu/qt6/QtCore/qobjectdefs_impl.h:65
#17 0x00007fdcf95a1c60 in QtPrivate::FunctorCall<QtPrivate::IndexesList<>, QtPrivate::List<>, void, void (dock::TreeLandWindowMonitor::)()>::call(void (dock::TreeLandWindowMonitor::)(),
dock::TreeLandWindowMonitor*, void**)

  (f=(void (dock::TreeLandWindowMonitor::*)(dock::TreeLandWindowMonitor * const)) 0x7fdcf959d804 <dock::TreeLandWindowMonitor::handleForeignToplevelHandleAdded()>, o=0x55c0203c0010, arg=0x7ffe8a00f948)
  at /usr/include/x86_64-linux-gnu/qt6/QtCore/qobjectdefs_impl.h:151

#18 0x00007fdcf95a10fc in QtPrivate::FunctionPointer<void (dock::TreeLandWindowMonitor::)()>::call<QtPrivate::List<>, void>(void (dock::TreeLandWindowMonitor::)(), dock::TreeLandWindowMonitor*, void**)
(f=(void (dock::TreeLandWindowMonitor::)(dock::TreeLandWindowMonitor * const)) 0x7fdcf959d804 dock::TreeLandWindowMonitor::handleForeignToplevelHandleAdded(), o=0x55c0203c0010, arg=0x7ffe8a00f948)
at /usr/include/x86_64-linux-gnu/qt6/QtCore/qobjectdefs_impl.h:199
#19 0x00007fdcf95a065f in QtPrivate::QCallableObject<void (dock::TreeLandWindowMonitor::
)(), QtPrivate::List<>, void>::impl(int, QtPrivate::QSlotObjectBase*, QObject*, void**, bool*)
(which=1, this_=0x55c020f6d000, r=0x55c0203c0010, a=0x7ffe8a00f948, ret=0x0) at /usr/include/x86_64-linux-gnu/qt6/QtCore/qobjectdefs_impl.h:570
#20 0x00007fdd06c3d46c in QtPrivate::QSlotObjectBase::call (a=0x7ffe8a00f948, r=0x55c0203c0010, this=0x55c020f6d000, this=, r=, a=)
at ./src/corelib/kernel/qobjectdefs_impl.h:486
#21 doActivate (sender=0x55c02264e9b0, signal_index=5, argv=0x7ffe8a00f948) at ./src/corelib/kernel/qobject.cpp:4120
#21 doActivate (sender=0x55c02264e9b0, signal_index=5, argv=0x7ffe8a00f948) at ./src/corelib/kernel/qobject.cpp:4120
ptimized out>, r=, a=)

@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented Apr 30, 2026

Reviewer's Guide

Adds defensive validity checks around model indexes and window pointers in the dock’s window monitoring and task management code to prevent crashes when accessing invalid data, especially in treeland environments.

Sequence diagram for TaskManager::handleWindowAdded with index validity checks

sequenceDiagram
    participant TM as TaskManager
    participant WM as AbstractWindow
    participant AAM as ActiveAppModel

    TM->>TM: handleWindowAdded(window)
    TM->>WM: id()
    WM-->>TM: winId

    alt activeAppModel exists and has rows
        TM->>AAM: rowCount()
        AAM-->>TM: count
        alt count > 0
            TM->>AAM: index(0, 0)
            AAM-->>TM: startIndex
            alt startIndex is valid
                TM->>AAM: match(startIndex, WinIdRole, winId)
                AAM-->>TM: res
            else startIndex is invalid
                TM-->>TM: skip match
            end
        else no rows
            TM-->>TM: skip match
        end
    else no activeAppModel
        TM-->>TM: skip match
    end
Loading

Class diagram for updated window monitoring and task management guards

classDiagram
    class RoleCombineModel {
        +QVariant data(QModelIndex index, int role) const
        +int columnCount(QModelIndex parent) const
        +QAbstractItemModel* sourceModel() const
        -QHash<QPair<int,int>, QPair<int,int>> m_indexMap
        -QHash<int,int> m_minorRolesMap
        -QAbstractItemModel* m_minor
    }

    class AbstractWindowMonitor {
        +QVariant data(QModelIndex index, int role) const
        +void requestWindowsView(QModelIndexList indexes) const
        -QVector<QPointer<AbstractWindow>> m_trackedWindows
    }

    class TaskManager {
        +void handleWindowAdded(QPointer<AbstractWindow> window)
        -QAbstractItemModel* m_activeAppModel
    }

    class AbstractWindow {
        +quint64 id() const
    }

    RoleCombineModel --> QAbstractItemModel : uses sourceModel
    RoleCombineModel --> QAbstractItemModel : uses m_minor

    AbstractWindowMonitor --> AbstractWindow : tracks via m_trackedWindows

    TaskManager --> AbstractWindow : parameter window
    TaskManager --> QAbstractItemModel : uses m_activeAppModel
Loading

File-Level Changes

Change Details Files
Harden RoleCombineModel::data() against invalid indexes and missing source models.
  • Return an empty QVariant when the provided model index is invalid or when the source model is null before doing any role-based work.
  • Introduce a local sourceIndex variable for non-minor roles and verify it is valid before calling data() on the source model.
panels/dock/taskmanager/rolecombinemodel.cpp
Make AbstractWindowMonitor::data() robust to out-of-range and null tracked windows.
  • Add an early return of an empty QVariant when the incoming model index is invalid.
  • Guard against negative or out-of-range row indices when accessing m_trackedWindows.
  • Fetch the tracked window with a default nullptr and bail out when it is null before switching on roles.
panels/dock/taskmanager/abstractwindowmonitor.cpp
Prevent TaskManager::handleWindowAdded from matching on an empty or invalid active app model.
  • Check that m_activeAppModel exists and has at least one row before attempting a match.
  • Create a startIndex from (0,0), verify it is valid, and only then call match() for TaskManager::WinIdRole.
panels/dock/taskmanager/taskmanager.cpp

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@deepin-ci-robot
Copy link
Copy Markdown

deepin pr auto review

这段代码 diff 主要针对 Qt 模型(Model)的数据访问安全性进行了改进,同时也更新了版权年份。以下是对修改内容的详细审查和改进建议:

1. 代码逻辑与安全性审查

优点:

  • 增强的空指针/索引检查:所有的修改都增加了对 QModelIndex 有效性(isValid())和模型是否存在(sourceModel())的检查。这极大地提高了代码的健壮性,防止了在模型数据不一致或为空时发生越界访问或空指针解引用崩溃。
  • 防御性编程:在 AbstractWindowMonitor::data 中,将 m_trackedWindows[pos](直接访问)改为 m_trackedWindows.value(pos, nullptr)(安全访问),并增加了对 pos < 0 的检查。这是非常好的防御性编程实践。
  • 版权更新:将版权年份范围更新为 2024 - 2026,符合维护规范。

潜在问题与改进建议:

A. panels/dock/taskmanager/abstractwindowmonitor.cpp

// ...
auto pos = index.row();
if (pos < 0 || pos >= m_trackedWindows.size())
    return QVariant();
auto window = m_trackedWindows.value(pos, nullptr);
if (!window)
    return QVariant();
// ...
  • 逻辑冗余
    • QVector::value(int, T) 方法本身在越界时会返回默认值(这里是 nullptr)。
    • 前面的 if (pos >= m_trackedWindows.size()) 已经确保了 pos 在有效范围内。
    • 因此,m_trackedWindows.value(pos, nullptr) 在此逻辑分支下永远不会返回 nullptr(除非容器内部存的就是 nullptr,但这通常不是预期的设计)。
  • 改进建议
    • 如果 m_trackedWindows 中不允许存储 nullptr,可以移除 if (!window) 的判断,或者保留它作为“不可能发生”情况的断言(Q_ASSERT(window))。
    • 代码可以简化为:
      if (!index.isValid() || pos < 0 || pos >= m_trackedWindows.size())
          return QVariant();
      auto window = m_trackedWindows[pos]; // 此时 pos 是绝对安全的
      // ...

B. panels/dock/taskmanager/rolecombinemodel.cpp

// ...
} else {
    auto sourceIndex = sourceModel()->index(index.row(), index.column());
    if (!sourceIndex.isValid()) {
        return QVariant();
    }
    return sourceModel()->data(sourceIndex, role);
}
// ...
  • 性能考量
    • sourceModel()->index(...) 是一个虚函数调用,有一定的开销。
    • 虽然检查 isValid() 是正确的,但在 Qt Model/View 架构中,如果传入的 index 是有效的(且行列数在范围内),通常 sourceModel()->index 返回的也是有效的。
    • 不过,考虑到代理模型(Proxy Model)的复杂性,保留这个检查是值得的,安全性优于微小的性能损耗。

C. panels/dock/taskmanager/taskmanager.cpp

// ...
if (m_activeAppModel && m_activeAppModel->rowCount() > 0) {
    auto startIndex = m_activeAppModel->index(0, 0);
    if (startIndex.isValid()) {
        res = m_activeAppModel->match(startIndex, TaskManager::WinIdRole, window->id());
    }
}
// ...
  • 逻辑优化
    • m_activeAppModel->rowCount() > 0 这个检查非常好,避免了在空模型上调用 match
    • index(0, 0)rowCount > 0 的情况下通常是有效的,但加上 isValid() 检查更安全。
  • 改进建议
    • 这段代码看起来是为了修复潜在的崩溃,修改是合理的。无需进一步优化。

2. 代码质量与规范

  • 格式:代码缩进和风格与原文件保持一致,符合 Qt/C++ 规范。
  • 注释:版权信息更新正确。

3. 总结

这段 diff 主要是为了修复潜在的越界访问和空指针崩溃问题。修改方向非常正确,属于典型的“防御性编程”改进。

最终建议:
唯一可以微调的地方是 abstractwindowmonitor.cpp 中的逻辑判断。如果容器保证不存空指针,可以简化代码以减少冗余检查。但从安全第一的角度来看,目前的代码也是完全可以接受的,不会导致功能错误。

评分: 优秀(主要提升了稳定性)

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey - I've left some high level feedback:

  • In AbstractWindowMonitor::data(), the combination of the explicit bounds check and using m_trackedWindows.value(pos, nullptr) is redundant; you can either rely on value() with a default and drop the size check, or keep the explicit check and use operator[] for a slightly clearer flow.
  • In TaskManager::handleWindowAdded(), consider adding a null check for the QPointer before calling window->id() to make the function robust against a potentially cleared QPointer.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In AbstractWindowMonitor::data(), the combination of the explicit bounds check and using m_trackedWindows.value(pos, nullptr) is redundant; you can either rely on value() with a default and drop the size check, or keep the explicit check and use operator[] for a slightly clearer flow.
- In TaskManager::handleWindowAdded(), consider adding a null check for the QPointer<AbstractWindow> before calling window->id() to make the function robust against a potentially cleared QPointer.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@deepin-ci-robot
Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

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

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 96c0307 into linuxdeepin:master Apr 30, 2026
11 of 12 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