新闻详情

新闻详情

首页 / 资讯中心 / 详情

CPython 修复 unicodedata 卸载后 `\N{...}` 转义与 namereplace 触发 use-after-free 崩溃(gh-149449)

发布时间:2026/9/10 11:36:30来源:尧图网络
CPython 修复 unicodedata 卸载后 `\N{...}` 转义与 namereplace 触发 use-after-free 崩溃(gh-149449)
CPython 修复 unicodedata 卸载后\N{...}转义与 namereplace 触发 use-after-free 崩溃gh-149449【免费下载链接】cpythonThe Python programming language项目地址: https://gitcode.com/GitHub_Trending/cp/cpython导读本文剖析 CPython 主分支gh-issue-149449中一个内存安全缺陷的修复当unicodedata模块被从sys.modules中移除并被垃圾回收后任何解码\N{...}字符名转义、或使用namereplace编解码错误处理器的操作都会触发 use-after-free释放后使用崩溃。文章以变更日志条目 Misc/NEWS.d/next/Core_and_Builtins/2026-05-23-22-08-01.gh-issue-149449.2lhQFF.rst 为骨架结合解释器源码还原崩溃根因、修复手法与验证路径帮助读者理解 CPython 中模块 C API 生命周期管理的内在约束。变更日志条目原文与背景本次修复对应的 NEWS 条目全文如下Fix a use-after-free crash when theunicodedatamodule was removed fromsys.modulesand garbage-collected between calls that decode\N{...}escapes or use thenamereplacecodec error handler.翻译过来即当unicodedata模块从sys.modules中被移除并被垃圾回收之后再调用解码\N{...}转义或使用namereplace编解码错误处理器时会触发 use-after-free 崩溃本修复解决该问题。这段文字位于Misc/NEWS.d/next/Core_and_Builtins/目录下文件名遵循 CPython 变更日志规范日期.gh-issue-编号.随机后缀.rst其中Core_and_Builtins表示该变更影响核心语言与内建功能。这类 NEWS 片段会在版本发布时被blurb工具合并进Misc/NEWS与Doc/whatsnew/的版本说明中。要理解这个 bug需要先弄清两个关键机制\N{...}转义在字符串字面量与unicode_escape编解码中\N{LATIN SMALL LETTER A}形式的转义需要将 Unicode 字符名称映射为码点namereplace错误处理器编码时遇到不可编码字符将其替换为\N{...}形式的转义序列。两者在 CPython 的 C 实现中共享同一份名称 ↔ 码点查找表而这份查找表恰好由unicodedata模块以 C API 的形式提供——这就是本次 bug 的根源所在。崩溃根因解释器缓存了指向模块堆内存的 C API 指针\N{...}转义如何拿到 Unicode 名称查找能力在 CPython 中Unicode 对象的名称解析能力并不是硬编码在解释器核心里的而是由unicodedata扩展模块以胶囊PyCapsule形式导出。解释器核心通过 Objects/unicodeobject.c 中的_PyUnicode_GetNameCAPI()惰性获取// Objects/unicodeobject.c _PyUnicode_Name_CAPI * _PyUnicode_GetNameCAPI(void) { PyInterpreterState *interp _PyInterpreterState_GET(); _PyUnicode_Name_CAPI *ucnhash_capi; ucnhash_capi _Py_atomic_load_ptr(interp-unicode.ucnhash_capi); if (ucnhash_capi NULL) { ucnhash_capi (_PyUnicode_Name_CAPI *)PyCapsule_Import( PyUnicodeData_CAPSULE_NAME, 1); // Its fine if we overwrite the value here. Its always the same value. _Py_atomic_store_ptr(interp-unicode.ucnhash_capi, ucnhash_capi); } return ucnhash_capi; }可以看到解释器状态interp-unicode.ucnhash_capi会缓存从PyCapsule_Import(PyUnicodeData_CAPSULE_NAME, 1)解出的指针后续所有调用都直接复用缓存值而不会再检查 capsule 是否仍然存活。PyCapsule_Import返回的是 capsule 中保存的void *指针即指向_PyUnicode_Name_CAPI结构体的地址。\N{...}解码路径位于 Objects/unicodeobject.c 的_PyUnicode_DecodeUnicodeEscapeInternal2中case N分支会调用_PyUnicode_GetNameCAPI()并用返回的getcode回调把字符名翻译成码点// Objects/unicodeobject.ccase N 分支节选 ucnhash_capi _PyUnicode_GetNameCAPI(); if (ucnhash_capi NULL) { PyErr_SetString( PyExc_UnicodeError, \\N escapes not supported (cant load unicodedata module) ); goto onError; } ... if (namelen INT_MAX ucnhash_capi-getcode(start, (int)namelen, ch, 0)) { ... }namereplace处理器同样依赖该 C APInamereplace错误处理器实现在 Python/codecs.c 的PyCodec_NameReplaceErrors中与\N{...}共享同一个 C API// Python/codecs.cPyCodec_NameReplaceErrors节选 _PyUnicode_Name_CAPI *ucnhash_capi _PyUnicode_GetNameCAPI(); if (ucnhash_capi NULL) { return NULL; } ... for (; imax end; imax) { Py_UCS4 c PyUnicode_READ_CHAR(obj, imax); if (ucnhash_capi-getname(c, buffer, sizeof(buffer), 1)) { // 替换为 \\ N { NAME } replsize 1 1 1 strlen(buffer) 1; } else { replsize codec_handler_unicode_hex_width(c); } ... }该处理器通过 Python/codecs.c 的_PyCodec_InitRegistry注册为内建错误处理器之一// Python/codecs.c { namereplace, { namereplace_errors, namereplace_errors, METH_O, PyDoc_STR(Implements the namereplace error handling, which replaces an unencodable character with a \\N{...} escape sequence.) } },崩溃链条结合上述代码可以还原 use-after-free 的完整链条首次执行\N{...}解码或namereplace编码时_PyUnicode_GetNameCAPI()通过PyCapsule_Import拿到unicodedata导出的_PyUnicode_Name_CAPI指针并缓存到解释器状态中随后用户执行del sys.modules[unicodedata]并触发垃圾回收unicodedata模块对象被销毁其导出的_ucnhash_CAPIcapsule 也被回收此时解释器状态中缓存的指针在修复前指向模块堆上动态分配的结构体已随模块释放而失效成为悬空指针之后任何一次\N{...}解码或namereplace编码都会再次命中该悬空指针并解引用导致 use-after-free 崩溃。值得说明的是_PyUnicode_GetNameCAPI()中PyCapsule_Import的第二个参数为1no_block且对每次失败的导入不会清空缓存因此模块被回收后缓存依旧指向旧地址这正是漏洞能够稳定触发的原因。修复方案将 C API 结构体改为静态分配本次修复的落点在 Modules/unicodedata.c 的unicodedata_create_capi()函数。修复后的代码如下// Modules/unicodedata.c static PyObject * unicodedata_create_capi(void) { // Statically allocated so that any cached pointers stay valid after unicodedata // is removed from sys.modules and the capsule is gcd (gh-149449). static _PyUnicode_Name_CAPI capi { .getname capi_getucname, .getcode capi_getcode, }; return PyCapsule_New(capi, PyUnicodeData_CAPSULE_NAME, NULL); }修复手法非常克制且经典将_PyUnicode_Name_CAPI结构体从每次模块初始化时动态分配改为static静态分配。关键点在于static存储期的结构体在进程生命周期内地址恒定不随模块对象、capsule 的销毁而释放解释器核心缓存的指针指向这块静态内存即使unicodedata被从sys.modules移除、capsule 被 GC缓存的指针依然有效彻底消除悬空指针PyCapsule_New的析构函数参数为NULLcapsule 销毁时不会尝试释放任何资源与静态分配策略一致。从源码结构看这个修复同时覆盖了\N{...}转义解码和namereplace处理器两条路径因为二者都经由_PyUnicode_GetNameCAPI()获得同一份静态 C API 指针。该 C API 由模块导出过程挂载见 Modules/unicodedata.c 的unicodedata_exec// Modules/unicodedata.cunicodedata_exec节选 /* Export C API */ if (PyModule_Add(module, _ucnhash_CAPI, unicodedata_create_capi()) 0) { return -1; }相关机制解释器关闭时的缓存清理interp-unicode.ucnhash_capi缓存的清理与模块生命周期解耦见 Objects/unicodeobject.c 的_PyUnicode_Fini// Objects/unicodeobject.c_PyUnicode_Fini节选 // bpo-47182: force a unicodedata CAPI capsule re-import on // subsequent initialization of interpreter. interp-unicode.ucnhash_capi NULL;这说明缓存重置只发生在解释器终结阶段配合 bpo-47182 的历史背景保证子解释器重新初始化时能重新导入 capsule。在正常运行期缓存一旦建立便不再失效——这正是本次修复采用静态分配保证指针永不过期这一方案的直接原因既然缓存生命周期与解释器一致那么被缓存的对象的生命周期也必须与解释器一致。可复现场景与验证路径触发条件崩溃需要同时满足两个条件\N{...}转义或namereplace已被使用过至少一次确保 C API 已缓存随后unicodedata被从sys.modules中移除并被 GC例如del sys.modules[unicodedata]; import gc; gc.collect()。之后再次执行\N{LATIN SMALL LETTER A}.encode(ascii, namereplace)或\\N{LATIN SMALL LETTER A}.encode().decode(unicode_escape)之类的操作即可命中崩溃路径。测试覆盖本仓库中与这两个功能点相关的测试包括Lib/test/test_codeccallbacks.py覆盖namereplace等编解码错误处理器的行为Lib/test/test_unicodedata.py覆盖unicodedata模块本身与\N{...}相关行为Lib/test/test_capi/test_codecs.py针对 C 层编解码实现的测试Lib/test/test_codecs.py通用编解码器测试。注上述测试文件在本次变更相关路径下均可找到其中test_capi/test_codecs.py与 C 层codecs实现直接对应。防御性编码建议对于应用层开发者即使 CPython 已修复该问题仍应避免在运行期从sys.modules中删除被解释器核心依赖的模块如unicodedata、codecs。若确需控制模块加载更稳妥的做法是保持unicodedata常驻而不是依赖删除后可重新导入这一非契约化行为。总结gh-issue-149449 是一次典型的内存生命周期修复CPython 解释器核心在_PyUnicode_GetNameCAPI()中缓存了unicodedata模块通过PyCapsule导出的_PyUnicode_Name_CAPI指针而该结构体原先的生命周期绑定于模块对象本身。当unicodedata从sys.modules移除并被 GC 后\N{...}解码与namereplace错误处理器便解引用了悬空指针。修复通过在 Modules/unicodedata.c 中将该结构体改为static分配使缓存指针在解释器生命周期内始终有效从根本上消除了 use-after-free。这一修复同时印证了 CPython 内部模块 C API 导出的设计约束被解释器核心缓存的指针其生命周期必须不短于解释器状态本身。【免费下载链接】cpythonThe Python programming language项目地址: https://gitcode.com/GitHub_Trending/cp/cpython创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

深度学习模型导入:原理、挑战与工业实践 2026/9/10 12:12:44

深度学习模型导入:原理、挑战与工业实践

1. 项目概述:模型导入的底层逻辑与工程实践在工业级AI开发流程中,模型导入环节往往被开发者视为"简单步骤"而草率处理。但根据我参与的37个跨行业AI项目实战经验,85%的模型部署失败案例都源于导入阶段的参数错配或框架兼容性问题。…

阅读更多 →
impeccable 设计技能(SKILL)完全指南:让 AI 前端协作达到生产级设计水准 2026/9/10 12:12:44

impeccable 设计技能(SKILL)完全指南:让 AI 前端协作达到生产级设计水准

impeccable 设计技能(SKILL)完全指南:让 AI 前端协作达到生产级设计水准 【免费下载链接】impeccable The design language that makes your AI harness better at design. 项目地址: https://gitcode.com/GitHub_Trending/im/impeccable …

阅读更多 →
嵌入式代码重构:用状态机与事件驱动告别意大利面条式架构 2026/9/10 12:12:44

嵌入式代码重构:用状态机与事件驱动告别意大利面条式架构

要是让我用一个场景来形容很多嵌入式项目的真实状态,那就是:刚写完那一周觉得逻辑清清楚楚,三个月后再打开,光是要搞清楚某个外设的中断回调到底被谁改过状态、哪个全局变量又在哪个 if 分支里被悄悄赋值,就得花掉半天…

阅读更多 →
RustFS 审计系统 rustfs-audit 完全指南:多目标扇出、热重载与可观测性实战 2026/9/10 12:12:44

RustFS 审计系统 rustfs-audit 完全指南:多目标扇出、热重载与可观测性实战

RustFS 审计系统 rustfs-audit 完全指南:多目标扇出、热重载与可观测性实战 【免费下载链接】rustfs 🚀2.3x faster than MinIO for 4KB object payloads. RustFS is an open-source, S3-compatible high-performance object storage system supporting …

阅读更多 →
如何在 aspnetcore 仓库新增一个项目并注册到解决方案过滤器与构建列表 2026/9/10 12:12:44

如何在 aspnetcore 仓库新增一个项目并注册到解决方案过滤器与构建列表

如何在 aspnetcore 仓库新增一个项目并注册到解决方案过滤器与构建列表 【免费下载链接】aspnetcore ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux. 项目地址: https://gitcode.com/GitHub…

阅读更多 →
20 分钟跑通 ESP32-P4 MIPI-CSI 摄像头:一份完整实战教程 2026/9/10 12:09:43

20 分钟跑通 ESP32-P4 MIPI-CSI 摄像头:一份完整实战教程

20 分钟跑通 ESP32-P4 MIPI-CSI 摄像头:一份完整实战教程 【免费下载链接】esp-idf Espressif IoT Development Framework. Official development framework for Espressif SoCs. 项目地址: https://gitcode.com/GitHub_Trending/es/esp-idf 在 ESP-IDF 仓库…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞