Skip to content

fix: prevent crash when removing tray items by PID#438

Merged
18202781743 merged 1 commit intolinuxdeepin:masterfrom
18202781743:master
Mar 11, 2026
Merged

fix: prevent crash when removing tray items by PID#438
18202781743 merged 1 commit intolinuxdeepin:masterfrom
18202781743:master

Conversation

@18202781743
Copy link
Contributor

@18202781743 18202781743 commented Mar 10, 2026

Fixed a potential crash in XembedProtocol::onRemoveItemByPid() by
storing a copy of the keys before iteration.
Previously, the code directly used m_registedItem.keys() in the find_if
algorithm while potentially modifying the container during iteration.
This could lead to iterator invalidation and crashes when items were
removed from the map.
The fix creates a temporary copy of the keys to ensure stable iteration.

Influence:

  1. Test tray icon removal functionality through various methods
  2. Verify no crashes occur when multiple tray items are added and
    removed
  3. Test PID-based removal specifically to ensure the fix works correctly
  4. Monitor system stability during tray icon operations

fix: 修复通过PID移除托盘项时的崩溃问题

修复了XembedProtocol::onRemoveItemByPid()中潜在的崩溃问题,通过在迭代前
存储键的副本。
之前代码直接在find_if算法中使用m_registedItem.keys(),同时在迭代过程中可
能修改容器。
这可能导致迭代器失效,并在从映射中移除项目时引发崩溃。
修复通过创建键的临时副本来确保稳定的迭代。

Influence:

  1. 测试通过各种方法移除托盘图标的功能
  2. 验证在添加和移除多个托盘项时不会发生崩溃
  3. 专门测试基于PID的移除,确保修复正常工作
  4. 监控托盘图标操作期间的系统稳定性

Summary by Sourcery

Bug Fixes:

  • Ensure PID-based tray item removal no longer crashes by iterating over a stable copy of registered item keys before removing entries.

@sourcery-ai
Copy link

sourcery-ai bot commented Mar 10, 2026

Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

Fixes a crash in XembedProtocol::onRemoveItemByPid() by iterating over a stable copy of the registered tray item keys instead of the live key set, avoiding iterator invalidation when removing items during PID-based tray icon removal.

Sequence diagram for PID-based tray item removal in XembedProtocol::onRemoveItemByPid

sequenceDiagram
    actor User
    participant TrayManager
    participant XembedProtocol
    participant m_registedItem
    participant m_item2Pid

    User->>TrayManager: Request tray item removal by PID
    TrayManager->>XembedProtocol: onRemoveItemByPid(pid)

    XembedProtocol->>m_registedItem: keys() // returns key list copy
    m_registedItem-->>XembedProtocol: keys

    XembedProtocol->>XembedProtocol: find_if over keys
    alt matching key found
        XembedProtocol->>m_item2Pid: remove(foundKey)
        XembedProtocol->>m_registedItem: remove(foundKey)
    else no matching key
        XembedProtocol->>XembedProtocol: do nothing
    end

    XembedProtocol-->>TrayManager: Return
    TrayManager-->>User: Tray item removed safely (no crash)
Loading

Updated class diagram for XembedProtocol tray item removal logic

classDiagram
    class XembedProtocol {
        - QMap~uint, TrayItem~ m_registedItem
        - QHash~uint, uint~ m_item2Pid
        + void onTrayIconsChanged()
        + void onRemoveItemByPid(uint pid)
    }

    class QMap_uint_TrayItem_ {
        + QList~uint~ keys()
        + void remove(uint key)
    }

    class QHash_uint_uint_ {
        + void remove(uint key)
    }

    class QList_uint_ {
        + iterator begin()
        + iterator end()
    }

    XembedProtocol --> QMap_uint_TrayItem_ : uses m_registedItem
    XembedProtocol --> QHash_uint_uint_ : uses m_item2Pid
    QMap_uint_TrayItem_ --> QList_uint_ : returns keys() as QList

    class TrayItem {
        + uint id
    }
Loading

File-Level Changes

Change Details Files
Prevent iterator invalidation when removing tray items by PID in XembedProtocol::onRemoveItemByPid().
  • Introduce a local const copy of the current registered item keys before searching for the PID-mapped item.
  • Update the std::find_if call to iterate over the copied key list instead of m_registedItem.keys().
  • Adjust the iterator comparison to use the copied key list’s end iterator prior to removing entries from m_item2Pid and m_registedItem.
plugins/application-tray/xembedprotocolhandler.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

Copy link

@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 the std::find_if predicate, using m_item2Pid[id] will insert a default entry if the id is missing; consider using a lookup that doesn’t mutate the map (e.g., m_item2Pid.value(id) or checking contains before accessing) to avoid side effects during the search.
  • Since m_registedItem.keys() is now copied to keys, you might prefer a range-based loop over keys rather than std::find_if to keep the control flow simple and avoid any confusion about which container the iterators belong to.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In the `std::find_if` predicate, using `m_item2Pid[id]` will insert a default entry if the id is missing; consider using a lookup that doesn’t mutate the map (e.g., `m_item2Pid.value(id)` or checking `contains` before accessing) to avoid side effects during the search.
- Since `m_registedItem.keys()` is now copied to `keys`, you might prefer a range-based loop over `keys` rather than `std::find_if` to keep the control flow simple and avoid any confusion about which container the iterators belong to.

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.

1. Fixed a potential dangling pointer issue by storing
m_registedItem.keys() in a local variable
2. Previously used m_registedItem.keys() directly which could become
invalid after container modification
3. Now using a const copy of keys to ensure stable iteration during
item removal
4. This prevents crashes when removing items from the tray icon registry

Influence:
1. Test tray icon removal functionality when applications exit
2. Verify no crashes occur during dynamic tray icon updates
3. Test with multiple tray icons being added and removed simultaneously
4. Verify tray icon registry remains stable during container
modifications

fix: 修复 XembedProtocol::onRemoveItemByPid 中的野指针问题

1. 通过将 m_registedItem.keys() 存储在局部变量中修复了潜在的野指针问题
2. 之前直接使用 m_registedItem.keys(),在容器修改后可能变为无效
3. 现在使用 keys 的常量副本以确保在移除项目期间迭代的稳定性
4. 这防止了从托盘图标注册表中移除项目时发生崩溃

Influence:
1. 测试应用程序退出时的托盘图标移除功能
2. 验证在动态托盘图标更新期间不会发生崩溃
3. 测试多个托盘图标同时添加和移除的情况
4. 验证托盘图标注册表在容器修改期间保持稳定
@deepin-ci-robot
Copy link

deepin pr auto review

这段代码的修改主要涉及版权年份更新和一个函数逻辑的修改。下面我将从语法逻辑、代码质量、代码性能和代码安全四个方面进行详细审查。

1. 语法逻辑

  • 版权年份更新:从 2024 更新为 2024 - 2026,这是合法的版权声明格式,表示版权有效期或覆盖范围,语法正确。
  • 函数逻辑
    • 原代码:直接使用 m_registedItem.keys() 作为迭代范围,并在 find_if 的 lambda 中访问 m_item2Pid[id]
    • 新代码:先获取 keys 的副本 const auto keys = m_registedItem.keys();,然后对副本进行迭代。
    • 潜在问题:在 C++ 中,QMap::keys() 返回的是 QList。在 Qt 中,QList 是“隐式共享”的。当你调用 keys() 时,它可能会创建一个副本(如果引用计数不为1,或者为了确保线程安全/一致性)。新代码显式地创建了副本 const auto keys
    • 逻辑正确性:新代码的逻辑是正确的。它首先获取当前所有键的快照,然后遍历这个快照来查找匹配的 PID。这避免了在迭代过程中容器可能发生变化的风险(虽然在这个特定函数中,find_if 是只读操作,但在更复杂的上下文中,显式快照更安全)。

2. 代码质量

  • 命名规范:变量名 m_registedItem 存在拼写错误,正确拼写应为 m_registeredItem(registered)。建议在后续重构中修正,以保持代码的专业性。
  • 代码可读性:新代码通过引入 keys 变量,使得 find_if 的范围和结束条件判断更加清晰,可读性略好于原代码。

3. 代码性能

  • 性能影响
    • 原代码m_registedItem.keys() 可能会触发容器的深拷贝(取决于 Qt 版本和内部实现),但通常 QMap::keys() 返回的是一个新列表。原代码在 find_if 中直接使用这个临时列表。
    • 新代码:显式地创建了一个 const auto keys 副本。这确保了在 find_if 执行期间,keys 是稳定的。
    • 分析:在 Qt 中,QMap::keys() 本身就会生成一个新的 QList。原代码中的 m_registedItem.keys().begin().end() 是基于同一个临时对象的,这是合法的。新代码将这个临时对象具名化了。两者在性能上几乎没有区别,因为 keys() 本身就是 O(N) 操作(遍历 map 生成列表)。
    • 改进建议:如果 m_registedItem 很大,频繁调用 keys() 会带来性能开销。如果这是热点路径,可以考虑直接遍历 m_item2Pid(如果 m_item2PidQMap<uint, uint> 或类似结构),因为 m_item2Pid 存储的是 ID 到 PID 的映射,直接遍历它查找 PID 可能更直接,避免生成 Key 列表的开销。例如:
      auto it = std::find_if(m_item2Pid.begin(), m_item2Pid.end(), [pid](const auto &pair) { return pair.second == pid; });
      if (it != m_item2Pid.end()) {
          uint id = it.key();
          m_item2Pid.erase(it);
          m_registedItem.remove(id);
      }
      这种方法避免了生成 keys() 列表,直接迭代现有的 map,效率更高。

4. 代码安全

  • 数据一致性:新代码通过创建 keys 副本,确保了在 find_if 执行期间,迭代器范围是固定的。虽然在当前的 onRemoveItemByPid 函数中,find_if 是只读操作,不会修改 m_registedItem,但在多线程环境下,或者如果未来在 lambda 中添加了修改操作,显式快照能避免潜在的迭代器失效问题。
  • 线程安全:代码中没有看到互斥锁(如 QMutex)或原子操作。如果 m_registedItemm_item2Pid 可能被多个线程访问,这会导致数据竞争(Data Race)。建议检查调用上下文,确保线程安全,或者添加适当的锁。

总结与建议

  1. 修正拼写:建议将 m_registedItem 重命名为 m_registeredItem
  2. 性能优化:建议直接遍历 m_item2Pid 来查找 PID,避免生成 keys() 列表的开销。
  3. 线程安全:如果涉及多线程,请添加锁机制。
  4. 代码审查意见:这个修改主要是为了代码的健壮性(显式快照),但在性能上没有提升。如果是为了修复特定的 Bug(如迭代器失效),那么这个修改是合理的。否则,建议采用上述性能优化方案。

改进后的代码示例

void XembedProtocol::onRemoveItemByPid(uint pid)
{
    // 直接遍历 m_item2Pid 查找匹配的 PID
    auto it = std::find_if(m_item2Pid.constBegin(), m_item2Pid.constEnd(), 
                           [pid](const auto &item) { return item.value() == pid; });
    
    if (it != m_item2Pid.constEnd()) {
        uint id = it.key();
        m_item2Pid.remove(id);
        m_registedItem.remove(id);
    }
}

或者使用 C++17 结构化绑定(如果编译器支持):

void XembedProtocol::onRemoveItemByPid(uint pid)
{
    for (auto [id, existingPid] : m_item2Pid.asKeyValueRange()) {
        if (existingPid == pid) {
            m_item2Pid.remove(id);
            m_registedItem.remove(id);
            break;
        }
    }
}

注意:asKeyValueRange() 是 Qt 6.6 引入的,如果使用的是旧版本 Qt,可以使用传统的迭代器方式。这段代码的修改主要涉及版权年份的更新和一个函数逻辑的微调。下面我将从语法逻辑、代码质量、代码性能和代码安全四个方面进行详细审查。

1. 语法逻辑

  • 版权年份更新:从 2024 更新为 2024 - 2026,这是合法的版权声明格式,表示版权有效期或覆盖范围,语法正确。
  • 函数逻辑
    • 原代码:直接在 std::find_if 的参数中调用 m_registedItem.keys()。这会创建一个临时的 QList 对象。begin()end() 是在这个临时对象上调用的。虽然这在 C++ 中是合法的(临时对象会在整个表达式结束前销毁),但有时会降低代码的可读性,或者在某些静态分析工具中产生警告。
    • 新代码:显式地将 keys() 的结果赋值给一个 const auto 变量 keys。这使得代码的意图更加明确:我们需要一个键的快照来进行查找。
    • 逻辑正确性:新代码的逻辑是正确的。它首先获取当前所有键的快照,然后遍历这个快照来查找匹配的 PID。这避免了在迭代过程中容器可能发生变化的风险(虽然在这个特定函数中,find_if 是只读操作,但在更复杂的上下文中,显式快照更安全)。

2. 代码质量

  • 命名规范:变量名 m_registedItem 存在拼写错误,正确拼写应为 m_registeredItem(registered)。建议在后续重构中修正,以保持代码的专业性。
  • 代码可读性:新代码通过引入 keys 变量,使得 find_if 的范围和结束条件判断更加清晰,可读性略好于原代码。

3. 代码性能

  • 性能影响
    • 原代码m_registedItem.keys() 会生成一个新的 QList(O(N) 复杂度,因为需要遍历 map)。std::find_if 遍历这个列表(O(N) 复杂度)。总复杂度是 O(N)。
    • 新代码:同样生成一个新的 QList 并遍历它。总复杂度依然是 O(N)。
    • 分析:两者在性能上几乎没有区别,因为 keys() 本身就是 O(N) 操作。
    • 改进建议:如果 m_registedItem 很大,频繁调用 keys() 会带来性能开销。如果这是热点路径,可以考虑直接遍历 m_item2Pid(如果 m_item2PidQMap<uint, uint> 或类似结构),因为 m_item2Pid 存储的是 ID 到 PID 的映射,直接遍历它查找 PID 可能更直接,避免生成 Key 列表的开销。例如:
      // 假设 m_item2Pid 是 QMap<uint, uint>,key是id,value是pid
      auto it = std::find_if(m_item2Pid.constBegin(), m_item2Pid.constEnd(), 
                            [pid](const uint &existingPid) { return existingPid == pid; });
      if (it != m_item2Pid.constEnd()) {
          uint id = it.key();
          m_item2Pid.erase(it); // 或者 remove(id)
          m_registedItem.remove(id);
      }
      这种方法避免了生成 keys() 列表,直接迭代现有的 map,效率更高。

4. 代码安全

  • 数据一致性:新代码通过创建 keys 副本,确保了在 find_if 执行期间,迭代器范围是固定的。虽然在当前的 onRemoveItemByPid 函数中,find_if 是只读操作,不会修改 m_registedItem,但在多线程环境下,或者如果未来在 lambda 中添加了修改操作,显式快照能避免潜在的迭代器失效问题。
  • 线程安全:代码中没有看到互斥锁(如 QMutex)或原子操作。如果 m_registedItemm_item2Pid 可能被多个线程访问,这会导致数据竞争(Data Race)。建议检查调用上下文,确保线程安全,或者添加适当的锁。

总结与建议

  1. 修正拼写:建议将 m_registedItem 重命名为 m_registeredItem
  2. 性能优化:建议直接遍历 m_item2Pid 来查找 PID,避免生成 keys() 列表的开销。
  3. 线程安全:如果涉及多线程,请添加锁机制。
  4. 代码审查意见:这个修改主要是为了代码的健壮性(显式快照),但在性能上没有提升。如果是为了修复特定的 Bug(如迭代器失效),那么这个修改是合理的。否则,建议采用上述性能优化方案。

改进后的代码示例

基于上述分析,这里提供一个性能更好且逻辑更清晰的版本:

void XembedProtocol::onRemoveItemByPid(uint pid)
{
    // 直接遍历 m_item2Pid 查找匹配的 PID
    // 假设 m_item2Pid 是 QMap<uint, uint>,其中 Key 是 ID,Value 是 PID
    auto it = std::find_if(m_item2Pid.constBegin(), m_item2Pid.constEnd(), 
                           [pid](const uint &existingPid) { return existingPid == pid; });
    
    if (it != m_item2Pid.constEnd()) {
        uint id = it.key();
        m_item2Pid.erase(it);
        m_registedItem.remove(id);
    }
}

或者使用 C++17 结构化绑定(如果编译器支持):

void XembedProtocol::onRemoveItemByPid(uint pid)
{
    for (auto [id, existingPid] : m_item2Pid.asKeyValueRange()) {
        if (existingPid == pid) {
            m_item2Pid.remove(id);
            m_registedItem.remove(id);
            break;
        }
    }
}

注意:asKeyValueRange() 是 Qt 6.6 引入的,如果使用的是旧版本 Qt,可以使用传统的迭代器方式。此外,m_registedItem 的拼写问题也建议一并修正。

@deepin-ci-robot
Copy link

[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 3af1d42 into linuxdeepin:master Mar 11, 2026
11 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