【Bug】Qoder CN 桌面版(Windows)登录状态文件 ACL 自愈逻辑永不收敛,持续占用 CPU

【Bug】Qoder CN 桌面版(Windows)登录状态文件 ACL 自愈逻辑永不收敛,导致每秒数次文件重写 + icacls/PowerShell 进程风暴,持续占用 CPU

摘要

Qoder CN Windows 桌面端的主进程服务 AuthAppStatusMainService 会把登录态原子写入私有状态文件 .qoder-app-status.json,并在写入后做"内容 + ACL 权限"对账(reconcile)。在中文/工作组环境的 Windows 11 上,新建文件会被系统自动附加 NT AUTHORITY\SYSTEM、BUILTIN\Administrators(或登录会话 SID S-1-5-5-*)等显式 ACE,而应用设置 ACL 用的 icacls /inheritance:r /grant:r <user>:(F) 无法删除这些显式 ACE,导致应用自身的"ACL 是否私有"判定(isAclPrivate,要求所有 ACE 必须且只能是当前用户一条)恒为 false。于是对账永远判定不安全 → 重新原子写 → 文件 watcher 又触发对账 → 死循环、永不收敛。

该循环每秒在磁盘上产生 3~5 次"建目录/写文件/改名/删除",并每秒 spawn 约 1 个 icacls.exe + 4~5 个 powershell.exe 子进程,造成:

  • 早期表现为 Windows Defender(Antimalware Service Executable / MsMpEng.exe)CPU 持续高占用(实时扫描大量新建文件);

  • 将 .qoder-cn 加入 Defender 排除项后,Defender 占用降到 ~0,但 Qoder CN 主进程自身仍持续占用约 50%~60% 单核 CPU(实测 PID 采样 6 秒 CPU=3.47s),且 PowerShell 的 AMSI 扫描开销不受路径排除影响。

  • 完全退出 Qoder CN 桌面端后,一切恢复正常。

环境

  • 产品:Qoder CN 桌面版(productId qoder-cn,Electron),package version 0.2.1

  • 操作系统:Windows 11 中文版(工作组环境,用户 YJM\huaca)

  • 安装路径:C:\Program Files\Qoder\Qoder CN\(更新后;旧版本在 D:\Program Files\Qoder CN\,两版均复现)

  • 配置/状态目录:C:\Users\huaca\.qoder-cn\

  • 备注:该问题与"国内版 CN / 国际版"无关——两版本共用同一套 Windows ACL 实现,仅状态目录名(.qoder-cn / .qoder)和域名不同。CLI 版不运行该 Electron 主进程服务,不复现。

复现

  1. 在 Windows 11(中文版/工作组或域账户均可)上安装并登录 Qoder CN 桌面版,保持应用运行。

  2. 观察 C:\Users\<user>\.qoder-cn\ 目录:持续出现并删除形如
    ..qoder-app-status.json.<uuid>.private\ 的临时目录,内含 .qoder-app-status.json(约 283 字节)。
    实测 8 秒内出现 26 个不同临时目录(约 3~4 个/秒)。

  3. 进程监控:6 秒采样窗口内新增 icacls.exe 7 个、用于 ACL 检查的 powershell.exe 27 个。

  4. CPU:Qoder CN 主进程持续约 50%~60% 单核;未排除目录时 MsMpEng.exe 同样高占用。退出应用后立即恢复。

根因(基于 app.asar 反编译定位)

主进程 AuthAppStatusMainService(依赖 NativePrivateFileMainService)逻辑:

  • 定时(默认 5s)+ 文件 watcher 触发 reconcileNow();

  • inspectFile() 读取目标文件,校验 kind==="regular" 且 permissions!=="insecure" 且 contents===desired.contents,任一不满足即 requestWrite() 重新原子写。

Windows 实现(类 $4e / 平台工厂 e8e):

  • 原子写:创建临时目录 .qoder-app-status.json.<uuid>.private\ → 在其中写 .qoder-app-status.json → applyAcl() 调 icacls <path> /inheritance:r /grant:r <DOMAIN>\<user>:(F) → rename 为正式文件 → 删除临时目录。

  • ACL 校验 isAclPrivate():用 PowerShell Get-Acl 读取 ACE,判定条件为
    所有 ACE 必须逐条满足 identity === <DOMAIN>\<user>(忽略大小写)且 AccessControlType==="Allow" 且 IsInherited===false,即"只允许当前用户一条 ACE"。

问题:在 Windows 上,新建文件时系统会自动附加显式(非继承)ACE。实测:

对目标文件 C:\Users\huaca\.qoder-cn\.qoder-app-status.json:

NT AUTHORITY\SYSTEM   Allow  IsInherited=False  FullControl
S-1-5-5-0-211315      Allow  IsInherited=False  ReadAndExecute,Synchronize   (登录会话 SID)
yjm\huaca             Allow  IsInherited=False  FullControl

对照实验(在仅授权当前用户的目录内新建文件):

新建文件后 ACL:
  NT AUTHORITY\SYSTEM      Allow  inh=False
  BUILTIN\Administrators   Allow  inh=False
  yjm\huaca                Allow  inh=False
执行应用所用命令  icacls <file> /inheritance:r /grant:r "YJM\huaca:(F)"  之后:
  NT AUTHORITY\SYSTEM      Allow  inh=False   ← 仍在
  BUILTIN\Administrators   Allow  inh=False   ← 仍在
  yjm\huaca                Allow  inh=False

即:

  • /inheritance:r 只移除继承 ACE;

  • /grant:r 只替换同账户的授权;

  • 二者都不会删除 SYSTEM / Administrators / 登录会话 SID 这些显式 ACE。

因此 isAclPrivate() 恒返回 false(permissions:"insecure")→ reconcile 恒触发重写。文件内容第一次就写对了(临时文件与正式文件逐字节一致,283 字节,snapshot_at 停在启动时刻),纯粹卡在 ACL 判定,且 5s 内任何手动 ACL 修正都会被下一次重写覆盖。

建议修复方向

  1. 放宽 isAclPrivate() 判定:不应要求"仅当前用户一条 ACE"。NT AUTHORITY\SYSTEM、BUILTIN\Administrators、当前登录会话 SID(S-1-5-5-*)是 Windows 新建文件的标准/系统 ACE,应视为安全;只要不存在 Everyone / BUILTIN\Users / Authenticated Users / Anonymous 等宽泛主体的 Allow,即可判定为私有。

  2. 或在设置 ACL 时显式移除系统 ACE(icacls /remove:g "NT AUTHORITY\SYSTEM" ...),但移除 SYSTEM 可能有副作用,不推荐。

  3. 或在 Windows 上将此类"系统自带 ACE 导致的不匹配"归类为可接受/已降级(unverified)而非 insecure,避免无限重写——核心是不要因为无法满足的 ACL 条件而进入写风暴。

影响面

所有 Windows(尤其中文版/工作组或域账户)桌面端用户都会命中,表现为:持续磁盘 IO、每秒数个短命进程(icacls/powershell)、CPU 长期偏高、笔记本风扇与耗电明显;用户难以自行定位。

附:关键实测数据

  • 8 秒内 26 个临时目录(~3~4 个/秒);

  • 6 秒内 spawn icacls.exe ×7、ACL 检查 powershell.exe ×27;

  • Qoder CN 主进程 6 秒 CPU=3.47s(≈58% 单核);

  • 目标文件与临时文件内容逐字节一致(283 字节),证明仅 ACL 判定失败。

您好!

关于您反馈的问题,建议您点击 Qoder 界面右上角的「问题上报」按钮进行提交,并发送下反馈码。我们会尽快分析并排查处理。感谢您的支持!