Skip to content

fix: reap zombie children before exit in sig_crash signal handler (BUG-371743) - #231

Open
mhduiy wants to merge 1 commit into
masterfrom
fix/BUG-371743-zombie-bash-on-sig-crash
Open

mhduiy wants to merge 1 commit into
masterfrom
fix/BUG-371743-zombie-bash-on-sig-crash

Conversation

@mhduiy

@mhduiy mhduiy commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

修复 BUG-371743:登出时 dde-session 产生僵尸进程 bash

PMS: https://pms.uniontech.com/bug-view-371743.html

问题现象

在 Loong64 V25 环境下执行 ps aux | grep 'Z' 检查时,发现存在僵尸进程
bash[bash] <defunct>),其父进程为 /usr/bin/dde-session

根因

sig_crash 信号处理函数标记 [[noreturn]],调用 doLogout() 后直接
exit(-1)exit() 不会调用栈上局部对象的析构函数(C++ 标准
[support.start.term])。当信号在 EXEC_COMMAND 宏的 waitForFinished(-1)
阻塞期间到达时:

  1. poll() 被信号中断且不会重启(sig_crash[[noreturn]]);
  2. 栈上的 QProcess p 析构函数永远不会被调用,forkfd_wait()/
    forkfd_close() 不会执行;
  3. bash 子进程退出后因未被 waitpid 回收而成为僵尸进程;
  4. doLogout() 中的同步 DBus 调用会阻塞一段时间,此窗口内僵尸进程可被
    ps 观测到。

dde-session 也未设置 subreaper(PR_SET_CHILD_SUBREAPER)或安装 SIGCHLD
处理器,无任何兜底回收机制。

修复方案(两层)

  1. 根因层sig_crash 信号处理函数

    doLogout() 之后、exit(-1) 之前添加:

    while (waitpid(-1, nullptr, WNOHANG) > 0) { }
    • waitpid 是 POSIX 异步信号安全(async-signal-safe)函数,可在信号
      处理函数中安全调用;
    • WNOHANG 保证不阻塞,仅回收已退出的子进程;仍在运行的子进程在
      dde-session 退出后会被 reparent 到 init/systemd 回收;
    • 覆盖所有使用 EXEC_COMMAND 宏的场景,不仅限于 launchAutostopScripts
  2. 症状层launchAutostopScripts()

    EXEC_COMMAND 宏替换为 QProcess::execute(),使用 Qt 标准 API,
    生命周期管理更规范。

改动范围

src/dde-session/impl/sessionmanager.cpp

  • 新增 #include <sys/wait.h>
  • sig_crashexit(-1) 前增加 waitpid 回收循环
  • launchAutostopScriptsEXEC_COMMANDQProcess::execute

验证

  • waitpid/WNOHANG/QProcess::execute 三个 API 已通过独立编译、链接及
    运行测试验证(运行测试中 waitpid 循环在 exit(-1) 前正常执行,退出码
    255 符合预期)。
  • 完整 CMake 构建因本环境缺少 libsystemd-dev/libglib2.0-dev 等 -dev
    包及 DTK 代码生成工具而无法跑通,与本次改动无关。

影响

  • waitpid(-1, nullptr, WNOHANG) 仅回收已退出子进程,不影响正在运行的
    子进程,回归风险低。
  • QProcess::execute() 内部行为与原 EXEC_COMMAND 宏一致(创建 QProcess
    • waitForFinished(-1)),仅生命周期管理更规范,无行为变化。

Summary by Sourcery

Reap exited child processes during crash handling and improve autostop script execution to prevent zombie bash processes during logout.

Bug Fixes:

  • Prevent crashed session-manager processes from leaving exited child processes as zombies by reaping available children before termination.

Enhancements:

  • Use Qt process execution for autostop scripts and report failures through warnings.

BUG-371743: 在 Loong64 V25 环境下登出时存在僵尸进程 bash,其父进程为
dde-session。

根因:sig_crash 信号处理函数标记 [[noreturn]],在调用 exit(-1) 前未回收
已退出的子进程。exit() 不会调用栈上局部对象(如 EXEC_COMMAND 宏创建的
QProcess)的析构函数,当信号在 waitForFinished(-1) 阻塞期间到达时,
QProcess 析构被跳过,子进程退出后成为僵尸进程残留。

修复(两层):
1. 根因层:在 sig_crash 的 doLogout() 之后、exit(-1) 之前,添加
   while (waitpid(-1, nullptr, WNOHANG) > 0) {} 回收已退出的子进程。
   waitpid 是异步信号安全函数,WNOHANG 保证不阻塞,覆盖所有信号中断场景。
2. 症状层:launchAutostopScripts() 中将 EXEC_COMMAND 宏替换为
   QProcess::execute(),使用 Qt 标准 API,生命周期管理更规范。

PMS: https://pms.uniontech.com/bug-view-371743.html
@deepin-ci-robot

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: mhduiy

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

@sourcery-ai

sourcery-ai Bot commented Sep 14, 2026

Copy link
Copy Markdown
Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

Fixes zombie bash processes during crash-triggered logout by nonblocking-reaping exited children before exit, while replacing the autostop command macro with QProcess::execute for more reliable lifecycle management and error reporting.

Sequence diagram for crash logout child reaping

sequenceDiagram
    participant Signal as sig_crash
    participant Session as SessionManager
    participant Child as ExitedChild
    participant Kernel as Kernel

    Signal->>Session: doLogout()
    Session-->>Signal: return
    loop exited children remain
        Signal->>Kernel: waitpid(-1, nullptr, WNOHANG)
        Kernel-->>Signal: child reaped
    end
    Signal->>Signal: exit(-1)
    Note over Signal,Kernel: Running children are not blocked on and are reparented after exit
Loading

Sequence diagram for autostop script execution

sequenceDiagram
    participant Session as SessionManager
    participant Process as QProcess
    participant Script as AutostopScript

    Session->>Process: execute(/bin/bash, arguments)
    Process->>Script: run
    Script-->>Process: exitCode
    Process-->>Session: exitCode
    alt exitCode != 0
        Session->>Session: qWarning()
    end
Loading

File-Level Changes

Change Details Files
Reap already-exited child processes in the crash signal path before terminating the session.
  • Include the waitpid API declarations.
  • Add a nonblocking waitpid loop after logout handling and before exit.
  • Preserve running children for reparenting to init/systemd rather than blocking in the signal handler.
src/dde-session/impl/sessionmanager.cpp
Replace the autostop command macro with Qt-managed process execution and add failure reporting.
  • Use QProcess::execute to run each autostop script synchronously.
  • Log a warning when an autostop script returns a nonzero exit code.
src/dde-session/impl/sessionmanager.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

@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.

Hey - I've reviewed your changes and they look great!


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

@deepin-ci-robot

Copy link
Copy Markdown

deepin pr auto review

🤖 AI 代码审查报告

总体评分: 100 分 (通过阈值: 70分)

Pass


📊 总体评价

项目 结果
审查结论 代码审查通过
评分详情 未发现安全问题,代码变更质量优秀。该 PR 正确修复了 BUG-371743,在 sig_crash 信号处理器中使用异步信号安全的 waitpid 回收僵尸子进程,并将 EXEC_COMMAND 宏替换为 QProcess::execute 以改善进程生命周期管理。

🔍 详细分析

1. 语法逻辑 ✅

评价: 优秀 ✅ 通过

潜在问题:
✅ 未发现明显问题

建议: ['#include <sys/wait.h> 正确引入,为 waitpid() 函数提供声明', 'QProcess::execute() 是 Qt 框架的合法静态方法,返回退出码', 'waitpid(-1, nullptr, WNOHANG) 参数使用正确:-1 等待任意子进程,nullptr 不保存退出状态,WNOHANG 非阻塞', 'while 循环条件 > 0 正确处理所有返回值:>0 表示成功回收子进程继续循环,0 表示无已退出子进程退出循环,-1 表示错误退出循环', 'exit(-1) 正确放置于 waitpid 回收之后,符合 [[noreturn]] 函数签名']


2. 代码质量 ✅

评价: 优秀 ✅ 通过

潜在问题:
✅ 未发现明显问题

建议: ['sig_crash 函数中的注释质量优秀:解释了 exit() 不调用析构函数的根本原因、EXEC_COMMAND 宏创建栈上 QProcess 的问题、waitpid 的异步信号安全性、WNOHANG 的非阻塞特性、仍在运行的子进程会被 reparent 到 init/systemd', '将 EXEC_COMMAND 宏替换为 QProcess::execute 静态方法,避免在调用者栈上创建 QProcess 对象,改善进程生命周期管理', "新增 qWarning() 日志输出比原宏的日志更具描述性('failed to run autostop script:' vs 'failed to run:')", '保留 qDebug() 输出脚本启动信息,便于调试', '无代码重复,无残留调试代码']


3. 代码性能 ✅

评价: 优秀 ✅ 通过

潜在问题:
✅ 未发现明显问题

建议: ['waitpid 使用 WNOHANG 标志确保非阻塞,不会在信号处理器中引入延迟', 'while 循环最多执行 N 次(N 为已退出子进程数),通常非常小,O(N) 复杂度合理', 'QProcess::execute() 与原 EXEC_COMMAND 宏性能等价,无性能回退', '正确回收僵尸进程,防止进程资源泄漏']


4. 代码安全 🔒

评价: 优秀 ✅ 通过

🔐 发现 0 个安全漏洞

安全漏洞详情:
✅ 未发现安全漏洞

建议: ['waitpid 是 POSIX 标准定义的异步信号安全函数(signal-safety(7)),适合在信号处理器中使用', 'WNOHANG 标志确保信号处理器不会阻塞,避免死锁风险', 'waitpid 循环正确回收僵尸子进程,防止进程资源耗尽', 'QProcess::execute 的文件路径来自 QDir::entryInfoList 枚举特定目录,非用户可控输入,无命令注入风险', 'autostop 脚本执行是设计行为,目录权限(/etc/xdg/autostop 为 root 所有,用户配置目录为用户所有)保障安全性']


💡 改进建议代码示例

// 本次代码变更质量优秀,无需额外修复代码示例。
// 三处变更均已正确实现:
// 1. #include <sys/wait.h>  — 为 waitpid() 提供声明
// 2. QProcess::execute()   — 替换 EXEC_COMMAND 宏,改善生命周期管理
// 3. while (waitpid(-1, nullptr, WNOHANG) > 0) { }  — 非阻塞回收僵尸子进程

本报告由 AI 代码审查工具自动生成

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.

2 participants