Skip to content

fix(windows-ime): 静态链接 CRT 防 host 进程 MSVCP140 劫持(修 QQ 切微软拼音崩溃) - #287

Merged
appergb merged 2 commits into
mainfrom
fix/windows-ime-static-crt
May 6, 2026
Merged

fix(windows-ime): 静态链接 CRT 防 host 进程 MSVCP140 劫持(修 QQ 切微软拼音崩溃)#287
appergb merged 2 commits into
mainfrom
fix/windows-ime-static-crt

Conversation

@appergb

@appergbappergb commented May 6, 2026

Copy link
Copy Markdown
Collaborator

User description

用户反馈

Windows 11 上 QQ (QQEX.exe v9.9.26.44725) 切到微软拼音时崩溃:

```
异常代码:0xc0000005 (STATUS_ACCESS_VIOLATION)
故障模块:D:\program\QQ\versions\\resources\app\MSVCP140.dll
偏移:0x13080
```

根因(DLL Hell)

  1. QQ 把私有 MSVCP140.dll 放在 app/ 目录,DLL 搜索顺序优先于系统
  2. ctfmon 切微软拼音 → 把已注册 TSF DLL 全部加载进 QQ 进程
  3. 我们 `OpenLessIme.dll` 用 MSBuild 默认 `RuntimeLibrary=MultiThreadedDLL` —— 动态链接 MSVCP140
  4. 在 QQ 进程内被绑给 QQ 自带的旧版 MSVCP140 → 调 `std::wstring/thread/mutex` 跑的是 QQ 那份 → ABI 漂移 → AV

修复(surgical)

`OpenLessIme.vcxproj` 4 处 `ItemDefinitionGroup` 加 `MultiThreaded(Debug)`:

  • DLL 自带 MSVCP / VCRUNTIME / UCRT 副本
  • QQ 私有 CRT 劫持不到我们
  • DLL 体积 +~300 KB(可接受)
  • DLL 内部 STL 不能跨边界共享 —— 我们 IPC 走 named pipe + JSON,不受影响

不动的部分(已合规)

  • TSF 接口本身已是 COM C ABI(vtable + COM-typed 参数)
  • 重量级 ASR/LLM/polish 早已在 OpenLess.exe 主进程做,DLL 只做 TSF text service hooks + IPC

QQ 客观脆弱性(已整理详细报告,用于 TSRC 上报)

  • app/ 目录 MSVCP140.dll 劫持系统 CRT
  • 版本目录嵌套混乱(崩溃报告里 9.9.26.44725 vs 路径里 9.9.29-47354)
  • 无 IME 崩溃隔离机制(任何第三方 IME 崩 = QQ 全进程崩)
  • 自带 CRT 与系统 CRT 行为漂移

Test plan

  • CI `Windows Tauri checks` + `pr_agent_job`
  • 真机 Windows:QQ 切微软拼音不再崩
  • OpenLessIme.dll 体积 +~300 KB(确认在可接受范围)

PR Type

Bug fix, Enhancement


Description

  • 静态链接 OpenLessIme.dll 的 CRT

  • 避免宿主进程 DLL 劫持崩溃

  • 保留线程库通知用于 CRT 初始化


Diagram Walkthrough

flowchart LR
A["QQ / Office 宿主进程"] -- "加载 TSF DLL" --> B["OpenLessIme.dll"]
B -- "改为 /MT 静态 CRT" --> C["自带 MSVCP/VCRUNTIME/UCRT"]
A -- "app/ 目录私有 DLL 劫持" --> D["旧版 MSVCP140.dll"]
C -- "不再依赖宿主 CRT" --> E["避免 ABI 漂移与崩溃"]
B -- "保留线程通知" --> F["CRT 线程本地初始化/清理"]
Loading

File Walkthrough

Relevant files
Bug fix
dllmain.cpp
Preserve thread notifications for static CRT

openless-all/app/windows-ime/src/dllmain.cpp

  • Removed DisableThreadLibraryCalls during process attach
  • Added comment explaining static CRT thread attach/detach needs
  • Prevents TLS initialization and cleanup issues in host input threads
+5/-1
Configuration changes
OpenLessIme.vcxproj
Switch DLL build to static CRT

openless-all/app/windows-ime/OpenLessIme.vcxproj

  • Set RuntimeLibrary to MultiThreadedDebug for Debug builds
  • Set RuntimeLibrary to MultiThreaded for Release builds
  • Applied changes for both Win32 and x64 configurations
  • Documented why static CRT avoids host-side MSVCP140.dll hijacking
+9/-0

用户反馈:在 Windows 11 切到微软拼音时 QQ (QQEX.exe v9.9.26.44725) 崩
0xc0000005,故障模块 D:\program\QQ\versions\<ver>\resources\app\
MSVCP140.dll @ 偏移 0x13080。
根因(DLL Hell 经典场景):
1. QQ 把私有版本的 MSVCP140.dll 放在 app/ 目录,DLL 搜索顺序优先于系统
2. ctfmon 切微软拼音 → 把所有已注册 TSF DLL 加载进 QQ 进程
3. 我们 OpenLessIme.dll 此前用 MSBuild 默认 RuntimeLibrary(=
MultiThreadedDLL,动态链接 MSVCP140.dll/VCRUNTIME140.dll/UCRT)
4. 在 QQ 进程内被绑给 QQ 自带的旧版 MSVCP140 → 调 std::wstring /
std::thread / std::mutex 等跑的是 QQ 那份 → ABI 行为漂移 → AV
修复:
- vcxproj 4 处 ItemDefinitionGroup(Debug/Release × Win32/x64)显式声明
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>(Release)/
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>(Debug)。
- DLL 自带 CRT 副本,QQ 私有 MSVCP140 劫持不到。
- DLL 体积 +~300 KB;DLL 内部 STL 不能跨边界共享,但我们 IPC 走 named
pipe + JSON(C ABI 友好),不受影响。
不动的部分:
- TSF 接口本身已是 COM C ABI(vtable + COM-typed 参数),无需改
- 重量级 ASR/LLM/polish 早已在 OpenLess.exe 主进程,DLL 只做 TSF text
service hooks + IPC(已合规"组件极简,IPC 通信"原则)
QQ 客观脆弱性已整理详细报告(桌面 PDF/DOCX + GitHub issue),用于上报
TSRC:app/ 目录下 MSVCP140 劫持系统 CRT、版本号目录嵌套混乱、无 IME
崩溃隔离机制、CRT 漂移。

@chatgpt-codex-connectorchatgpt-codex-connectorBot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:80de07851c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;OPENLESSIME_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Remove DisableThreadLibraryCalls when using /MT

With the Release builds now linked to the static CRT (MultiThreaded), this DLL still calls DisableThreadLibraryCalls(instance) in windows-ime/src/dllmain.cpp; the Win32 API documentation explicitly says not to call DisableThreadLibraryCalls from a DLL linked to the static CRT because the CRT needs thread attach/detach notifications. In host processes that create threads after loading the TSF DLL, suppressing those callbacks can break CRT per-thread initialization/cleanup and reintroduce crashes or leaks in exactly the arbitrary-host-process scenario this change is meant to harden, so remove that call when using /MT (same issue applies to the Win32 Release setting).

Useful? React with 👍 / 👎.

@github-actions

github-actionsBot commented May 6, 2026

Copy link
Copy Markdown
Contributor

PR Reviewer Guide 🔍

(Review updated until commit https://github.com/appergb/openless/commit/901e9dfe3f2595ecf02d92c7f33de79205e07ff6)

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 1 🔵⚪⚪⚪⚪
🧪 No relevant tests
🔒 No security concerns identified
⚡ No major issues detected

Codex P1 (PR #287):DLL 改用 /MT 静态链接 CRT 后,仍调
DisableThreadLibraryCalls(instance) 会让静态 CRT 拿不到 DLL_THREAD_DETACH
通知,per-thread TLS 初始化/清理断裂 — 在 host 进程(QQ / Office /
微软拼音切换)创建新 input thread 时反而把这次想修的崩溃问题重新引回。
Microsoft 文档 DisableThreadLibraryCalls 备注明确:使用 static CRT 的
DLL 不应禁用 thread library calls。
修复:删除该调用;保留 g_module 缓存。
@appergb

Copy link
Copy Markdown
CollaboratorAuthor

@codex P1 已采纳:commit 901e9df 删除了 DisableThreadLibraryCalls(instance)。理由完全同意——/MT 下静态 CRT 需要 DLL_THREAD_DETACH 通知做 per-thread 资源清理,禁用通知会让 thread-local 在 host 进程的 input thread 切换时泄漏,反而引入这个 PR 想修的同类崩溃。

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create an environment for this repo.

@github-actions

Copy link
Copy Markdown
Contributor

@appergb
appergb merged commit 96ca966 into mainMay 6, 2026
2 checks passed
@appergb
appergb deleted the fix/windows-ime-static-crt branch May 6, 2026 11:52
appergb pushed a commit that referenced this pull request May 6, 2026
主要修复
─────────
- fix(windows): TSF DLL 静态链接 CRT,防 host 进程(QQ 等)私有
MSVCP140 劫持导致切微软拼音崩溃 0xc0000005(PR #287 + #289 + Codex
P1 review)。
- fix(windows): 移除 DisableThreadLibraryCalls — /MT 下 CRT 需要
thread attach/detach 通知做 per-thread TLS 清理,禁用反而触发崩溃。
- fix(recording): 录音条出现时 mic 已 capture,按 Option 不再吞开头
字。emit Recording 加 race 检查避免短按时覆盖 stop/cancel UI 信号。
- fix(startup): splash 透明背景 + 卡片化,避免长启动时左半白屏。
- fix(vault): keyring chunks 用稳定 account name —— macOS Keychain
Always-Allow 不再因 UUID 轮换失效;不再每次 load 都尝试删 9 个
legacy entries 触发 ACL 弹窗(与 PR #277 一起)。
新功能
─────────
- feat(security): 凭据从 plaintext JSON 迁移到平台 credential vault
(macOS Keychain / Windows Credential Manager / Linux libsecret)。
Windows 2560 byte 限制下用 chunked storage;partial-write safe。
legacy 多源迁移完成后自动清理(PR #277)。
工程
─────────
- chore(ci): 暂时搁置 macos-13(Intel mac)matrix —— GH Actions
runner pool 紧张到每次 dispatch queue 1-2h(已观测 4 次)。Apple
Silicon 用户 dmg 仍发;Intel mac 用户保留 v1.2.20 dmg + Rosetta
说明(issue #299)。
- chore(devex): CI 加 macOS 矩阵 + 5-way 版本号一致性校验 +
scripts/bump-version.sh 一行同步 5 处版本号。.gitignore 加
promo-openless-v2 / node_modules / dist / target 全局兜底(PR #294)。
- docs: docs/audit-2026-05-06.md 工程审计基线,覆盖架构 / UI bug /
多端一致性 / IPC 1:1 校验 / P0/P1/P2 改进路径。
- chore: 9 个改进 issue (#295-#303) 跟踪 audit 列出的所有 P1/P2/P3
问题。
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@appergb