Skip to content

fix: stop backend write thread before teardown in ~Settings - #629

Merged
lzwind merged 1 commit into
linuxdeepin:masterfrom
add-uos:fix/settings-backend-write-thread-race
Sep 29, 2026
Merged

lzwind merged 1 commit into
linuxdeepin:masterfrom
add-uos:fix/settings-backend-write-thread-race

Conversation

@add-uos

@add-uos add-uos commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

问题

CI UT 任务中 test_settings 套件段错误(exit 139,SIGSEGV),gtest XML 报告未写出,套件内 66 个用例整体丢失:

[ RUN      ] SettingsTest.BUG102351_SettingsDestroyed_ConfigValuePreserved
Destroying Settings instance
Cleaning up settings backend
QMutex: destroying locked mutex        ← 未定义行为警告
Settings instance destroyed
CustemBackend::CustemBackend "..."     ← 测试新建验证后端读盘
Entering CustemBackend::keys
段错误(核心转储)

根因

  • DSettings::setBackend() 会将 backend moveToThread 到专门的写线程,doSetOption/doSync 经队列在该线程异步执行(dtkcore 源码确认);
  • 只有 DSettings::destroyed 信号会 quit()+wait() 该写线程;
  • 原 ~Settings() 只 delete m_backend(backend 的线程亲和在写线程上),且成员 settings(DSettings*)从不析构 → 写线程永不停止,主线程销毁 backend 及其 QSettings 时与写线程在途的 doSetOption 竞态。

core dump 堆栈(崩溃线程为 DTK 写线程,非主线程):

#0  QSettings::setValue(...)                     ← UAF
#1  Dtk::Core::QSettingBackend::doSetOption(...)
#2  QObject::event(QEvent*)
#12 QThread::exec()                              ← 写线程事件循环

修复

~Settings() 中先 delete settings(触发 DSettings::destroyed → 写线程 quit+wait 停稳),再 delete m_backend;同时修复 DSettings 实例泄漏。

验证

复刻 CI 运行环境(cwd=build、ASAN_OPTIONS、--gtest_output=xml、全量 66 用例):

修复前 修复后
全量运行 2/2 必现 exit 139 5/5 全部 exit 0
QMutex: destroying locked mutex 每次出现 0 次

单跑 BUG102351 用例不触发(竞态依赖前序用例留下的在途写事件),需全量套件复现。

Summary by Sourcery

Stop the settings backend write thread before tearing down the backend to make destruction safe.

Bug Fixes:

  • Prevent settings teardown from racing with asynchronous backend writes, eliminating the CI segmentation fault and locked-mutex warning.

Enhancements:

  • Release the DSettings instance during destruction and ensure its write thread is stopped before the backend is deleted.

DSettings::setBackend() moves the backend to a dedicated write
thread and only DSettings::destroyed quits and waits for it.
~Settings() deleted the backend first while never destroying
DSettings, so the main thread could free the backend (and its
QSettings) while the write thread was still running doSetOption,
causing a use-after-free SIGSEGV (exit 139) in CI test_settings,
reliably reproduced by the BUG102351 case in full-suite runs.

Destroy DSettings before deleting m_backend so the write thread
is stopped first; this also fixes the DSettings instance leak.

Log: 修复 Settings 析构与 backend 写线程竞态导致的段错误(CI UT 退出码 139)
Influence: Settings 析构顺序调整,无接口变化;顺带修复 DSettings 实例泄漏
@sourcery-ai

sourcery-ai Bot commented Sep 29, 2026

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

Reviewer's Guide

修复 Settings 析构期间 backend 写线程仍在异步访问已销毁 QSettings 的竞态:析构时先释放 DSettings 以停止并等待写线程,再释放 backend,并消除 DSettings 泄漏;全量 test_settings 验证由稳定崩溃恢复为连续通过。

Sequence diagram for safe Settings teardown

sequenceDiagram
    participant Settings
    participant DSettings
    participant WriteThread
    participant Backend
    participant QSettings

    Settings->>DSettings: delete settings
    DSettings->>WriteThread: destroyed: quit()
    DSettings->>WriteThread: wait()
    WriteThread-->>DSettings: stopped
    Settings->>Backend: delete m_backend
    Backend->>QSettings: destroy safely
Loading

File-Level Changes

Change Details Files
调整 Settings 析构顺序,先停止并等待 DSettings 管理的 backend 写线程,再释放 backend,避免异步写入与后端销毁并发导致 UAF 和崩溃。
  • 在析构函数中删除 settings,触发 DSettings::destroyed 对写线程执行 quit+wait。
  • 将 settings 指针置空后再删除 m_backend,确保后端及其 QSettings 不再被后台线程访问。
  • 同时修复 settings 裸指针未释放造成的 DSettings 实例泄漏。
src/common/settings.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

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: add-uos, lzwind

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

@lzwind
lzwind merged commit cec5e8f into linuxdeepin:master Sep 29, 2026
17 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