CPython 3.5.4rc1 发布说明深度解读:安全修复、核心与标准库变更全览
【免费下载链接】cpythonThe Python programming language项目地址: https://gitcode.com/GitHub_Trending/cp/cpython
本文以 CPython 3.5 维护分支的候选发布版 3.5.4rc1(发布于 2017-07-23)的官方变更记录(Misc/NEWS.d/3.5.4rc1.rst)为主体,逐板块梳理其安全修复、Core and Builtins 缺陷修复、标准库行为修正、测试基建增强与构建/Windows/C API 变更,并结合当前 CPython 仓库中的源码与测试用例(如 Lib/urllib/parse.py、Modules/pyexpat.c、Lib/test/test_urlparse.py)佐证底层实现。读完本文,你将掌握 3.5.4rc1 每个关键补丁解决的问题、对应源码位置及其对后续版本的影响。
一、发布背景与变更总览
CPython 3.5 系列属于 2015 年发布的 3.x 分支,3.5.4rc1 是 3.5.4 正式版之前的候选发布版,文档头部标注的 release date 为 2017-07-23。整个变更记录采用当时标准的NEWS.d条目格式,每条包含 bpo 编号、日期、nonce 与所属 section(Security、Core and Builtins、Library、Documentation、Tests、Build、Windows、C API)。
从板块分布看,本版变更呈现三个鲜明特征:
- 安全类变更集中:覆盖 Windows subprocess 环境变量注入、expat 库连续升级(2.1.1 → 2.2.0 → 2.2.1)、urllib.parse 主机解析缺陷,是 3.5 维护分支对安全问题的集中响应;
- 核心解释器缺陷大量修复:涉及 dict 并发变异崩溃、pthread 锁阻塞、相对导入异常类型、协程 StopIteration 包装、旧字节码兼容等十余项;
- 标准库行为修正面广:multiprocessing、asyncio、unittest.mock、typing、pathlib、subprocess 等高频模块均有针对性修补。
下文按官方 section 顺序展开,并补充仓库源码证据。
二、安全修复(Security)
1. Windows 上 subprocess 环境变量注入防护(bpo-30730)
变更内容:在 Windows 平台阻止环境变量注入到 subprocess——防止把额外的环境变量与命令行参数传入子进程。
原理说明:Windows 创建进程时,如果传给CreateProcess的lpEnvironment或命令行参数处理不当,攻击者可以通过精心构造的环境变量名(例如包含=的键)或命令行转义字符实现参数/环境注入。该补丁在 Lib/subprocess.py 的环境变量序列化路径中体现为显式校验:构建env_list时对每个键执行os.fsencode(k)后检查b'=' in k,一旦发现非法名称立即抛出ValueError("illegal environment variable name")(见 Lib/subprocess.py)。这一防御逻辑在后续版本被保留并扩展,是 CPython 对 Windows 子进程安全的重要基线。
2. expat 升级至 2.2.1,修复多个安全漏洞(bpo-30694)
变更内容:将内置 expat 副本从 2.2.0 升级到 2.2.1,修复多个安全漏洞,包括:
- CVE-2017-9233:外部实体无限循环导致的拒绝服务(External entity infinite loop DoS);
- CVE-2016-9063:整数溢出(re-fix);
- CVE-2016-0718:修复 2.2.0 针对该 CVE 补丁引入的回归 bug;
- CVE-2012-0876:SipHash 计数器哈希洪泛(Counter hash flooding)。
关键说明:记录中特别指出 CVE-2016-5300(使用 getrandom 等 OS 熵源)不影响 Python,因为 Python 早已通过XML_SetHashSalt()从操作系统获取熵来设置 expat 的 secret。仓库源码可佐证:在 Modules/pyexpat.c 中,新建解析器时根据XML_COMBINED_VERSION调用XML_SetHashSalt16Bytes(self->itself, _Py_HashSecret.expat.hashsalt16)(expat ≥ 2.8.0)或XML_SetHashSalt(self->itself, (unsigned long)_Py_HashSecret.expat.hashsalt)(expat ≥ 2.1.0),即哈希盐直接取自 CPython 的进程级哈希密钥_Py_HashSecret,无需依赖 expat 自身的熵源。
3. urllib.parse.splithost() 片段解析修复(bpo-30500)
变更内容:修复urllib.parse.splithost()对 URL 片段的解析。此前splithost('//127.0.0.1#@evil.com/')会把@evil.com误当作认证信息(login@host)中的 host,即解析出错误的主机名;修复后正确返回主机127.0.0.1。
源码佐证:当前 Lib/urllib/parse.py 中_splithost()使用正则'//([^/#?]*)(.*)'提取主机端口部分,#位于排除字符集内,因此片段起始符之后的@evil.com不再参与主机匹配。测试用例位于 Lib/test/test_urlparse.py,明确注释# bpo-30500: # starts a fragment.,验证了splithost('//127.0.0.1#@host.com')返回('127.0.0.1', '/#@host.com')等场景。注意urllib.parse.splithost()自 3.8 起被标记为弃用,官方建议改用urllib.parse.urlsplit(),但底层_splithost仍保留此正确行为。
4. expat 升级至 2.2.0(bpo-29591)
变更内容:将 expat 副本从 2.1.1 升级到 2.2.0,获取 CVE-2016-0718 与 CVE-2016-4472 的修复。这条是 2.2.1 升级(bpo-30694)的前置步骤,两次升级共同构成了 3.5.4 分支对 expat 解析器安全性的完整加固链。
三、核心与内置(Core and Builtins)
1. 相对导入行为修正(bpo-30876)
- 从未加载包(unloaded package)发起的相对导入,现在会重新导入该包,而不是以
SystemError失败; - 从非包(non-package)发起的相对导入,现在以
ImportError失败,而不是SystemError。
这一改动把"包未加载"与"根本不是包"两种情形区分开,使异常类型更符合语义,也消除了相对导入在边界场景下暴露解释器内部状态(SystemError)的问题。
2. PyThread_acquire_lock() 非阻塞语义修复(bpo-30765)
变更内容:当PyThread_acquire_lock()被要求不阻塞(non-blocking)时,避免在pthread_mutex_lock()中阻塞。此前某些平台上非阻塞获取可能退化为阻塞行为,导致调用方线程意外挂起;修复后严格遵循"尝试获取、失败即返回"的语义。
3. dict 在迭代/插入/比较期间被外部变异时的崩溃修复(bpo-27945)
变更内容:修复当输入集合在搜索、插入或比较过程中被并发变异时,dict 出现的多种段错误(segfault)。补丁基于 Duane Griffin 与 Tim Mitchell 的补丁。这属于解释器核心容器在"迭代时修改"竞态下的健壮性修复。
4. type.setattr()/delattr() 对非驻留属性名的修复(bpo-25794)
修复type.__setattr__()与type.__delattr__()在属性名非 interned(驻留)时的处理错误,基于 Eryk Sun 的补丁。涉及元类层面的属性设置/删除与字符串驻留机制的交互。
5. tuple/list/deque 的 index() 错误消息修复(bpo-29935)
当向tuple.index()、list.index()、deque.index()传入错误类型(如字符串而非整数)的索引时,错误消息得到修正,更准确地描述问题。
6. bool(range) 与 len(range) 抛 OverflowError 的交互(bpo-28876)
变更内容:即使len(range)抛出OverflowError,bool(range)也能正常工作。此前布尔判断可能被长度计算的溢出异常干扰。
7. 协程返回值 StopIteration 包装修复(bpo-29600)
修复协程返回值在StopIteration中的包装方式,确保await表达式能够正确取出协程的返回值而不丢失或错位。
8. 与 3.5.0–3.5.2 字节码的运行时兼容(bpo-29537)
变更内容:恢复对 CPython 3.5.0 至 3.5.2 生成的字节码文件的运行时兼容性,并调整求值循环(eval loop),以避免这些旧字节码可能包含的畸形BUILD_MAP_UNPACK_WITH_CALL操作码变体引发的问题。补丁作者为 Petr Viktorin、Serhiy Storchaka、Nick Coghlan。这体现了 CPython 维护分支在语言版本演进中对向后兼容的刻意保护。
9. str 子类的rmod优先级(bpo-28598)
支持str的子类在str.__mod__之前被调用__rmod__。补丁作者 Martijn Pieters。这修正了反射二元运算符(reflected operand)在%运算中的方法解析顺序。
10. complex 构造函数对有符号零的处理(bpo-29602)
修复complex构造函数对复数子类以及实现了__complex__的输入在有符号零(signed zero,如-0.0)场景下的错误处理,避免正负零信息丢失。补丁作者 Serhiy Storchaka。
11. 其他核心修复一览
- bpo-29347:创建 weakref 对象时可能解引用未定义指针;
- bpo-29438:key sharing dict 中的 use-after-free 问题;
- bpo-29319:阻止
RunMainFromImporter覆盖sys.path[0]; - bpo-29337:比较 code 对象时可能出现的
BytesWarning(编译期即可发出警告); - bpo-29478:使用
Compat32策略且指定max_line_length=None时,该设置不再被忽略(补丁作者 Mircea Cosbuc)。
四、标准库(Library)
1. 并发与进程模块
- bpo-30886:修复
multiprocessing.Queue.join_thread()——即使线程是由创建队列的同一进程启动的,现在也会等待线程完成; - bpo-24484:避免 multiprocessing 清理过程中的竞态条件;
- bpo-30414:
multiprocessing.Queue._feed后台线程在异常时不再从主循环退出(增强鲁棒性); - bpo-30301:修复
SimpleQueue.empty()在spawn与forkserver启动方式下的AttributeError; - bpo-28699:
multiprocessing.pool中可迭代对象在第一个元素处抛异常时,异常可能被吞掉或导致程序挂起的问题; - bpo-29861:
multiprocessing.Pool中任务完成后尽早释放对任务、参数与结果的引用; - bpo-30185:
forkserver辅助进程收到 Ctrl-C 时避免KeyboardInterrupttraceback; - bpo-29376:修复
threading._DummyThread.is_alive()的断言错误。
2. asyncio
- bpo-29870:修复 asyncio/ssl 实现中连接中止时的 SSL socket 泄漏(补丁作者 Michaël Sghaïer);
- bpo-29743:握手过程中关闭 transport 会泄漏打开状态的 socket(补丁作者 Nikolay Kim);
- bpo-27585:修复
asyncio.Lock中 waiter 取消的问题(补丁作者 Mathieu Sornay); - bpo-29704:
asyncio.subprocess.SubprocessStreamProtocol不再在所有管道关闭前提前关闭; - bpo-29703:支持在子进程中实例化新的事件循环;
- bpo-30508:调用
Task/Future.cancel()时不再记录异常日志; - bpo-30048:修复
Task.cancel()在任务运行协程且协程在没有更多await的情况下返回时可能被忽略的问题; - bpo-29742:在已关闭的 ssl transport 上调用
get_extra_info()现在抛出异常(补丁作者 Nikolay Kim)。
3. unittest.mock
- bpo-29403:修复 autospec 在方法绑定的内建函数上失败的问题(补丁作者 Aaron Gallagher);
- bpo-28961:修复
unittest.mock._Call辅助类忽略name参数的问题(补丁作者 Jiajun Huang); - bpo-28735:修复
mock.MagicMock与mock.ANY的比较。
4. typing 模块系列更新(bpo-28556 多条目)
- 新增泛型
AsyncContextManager,并在所有版本上支持ContextManager; - 新增
typing.NoReturn类型,改用WrapperDescriptorType,并修复若干小 bug; - 新增
typing.Counter、typing.ChainMap,改进 ABC 缓存等; - bpo-29011:补充遗漏的
Deque类型。
5. 路径、文件与导入
- bpo-30879:
os.listdir()与os.scandir()在传入 bytes 类参数时返回 bytes 名称; - bpo-30645:修复
imp.load_package()的路径计算,解决包仅附带字节码时的加载问题(补丁作者 Alexandru Ardelean); - bpo-29694:修复
pathlib中mkdir(parents=True)的竞态条件(补丁作者 Armin Rigo); - bpo-25803:避免 OS 优先返回
EACCES而非EEXIST时Path.mkdir(exist_ok=True)抛出错误异常; - bpo-29416:防止
pathlib.Path.mkdir出现无限循环。
6. subprocess 与 os.environ
- bpo-30746:在
os.putenv()与os.spawn*()中禁止环境变量名包含=字符(与 bpo-30730 的 Windows 注入防护互为表里,当前 Lib/subprocess.py 仍保留该校验); - bpo-30441:修复在迭代
os.environ的同时修改它的 bug; - bpo-29335:修复子进程已退出到 stopped(而非 terminated)状态(如处于 ptrace 下)时
subprocess.Popen.wait()的行为; - bpo-30418:Windows 上若子进程仍在运行但已关闭管道,
subprocess.Popen.communicate()在stdin.write()时也忽略EINVAL。
7. 网络与套接字
- bpo-30378:修复
logging.handlers.SysLogHandler无法处理 IPv6 地址的问题; - bpo-30205:修复 Linux 上未绑定
AF_UNIXsocket 的getsockname(); - bpo-30329:
imaplib与poplib捕获 Windows socket 在shutdown(SHUT_RDWR)时的WSAEINVAL(错误码 10022),该错误在 SSL 连接上偶发; - bpo-29142:
urllib.request中no_proxy环境变量带前导点的后缀能再次匹配相关主机名(如.b.c匹配a.b.c,补丁作者 Milan Oberkirch)。
8. 其他标准库修复(精选)
- bpo-30730 关联:Windows subprocess 环境安全(见安全节);
- bpo-30807:
signal.setitimer()传入微小值(如 1e-6)时可能把定时器禁用而非重新调度;修复后微秒级有效非零间隔被正确保留; - bpo-29755:gettext 的
lgettext()系列函数现在始终返回 bytes; - bpo-23890:
unittest.TestCase.assertRaises()手动打破引用环,避免对象存活时间超出预期; - bpo-30149:
inspect.signature()支持用partialmethod包装的带可变参数的可调用对象(补丁作者 Donghee Na); - bpo-29931:修复
ipaddress.ip_interface对象的比较检查; - bpo-28994:
atexit注册回调中抛出SystemExit时不再显示 traceback; - bpo-28969:修复 C 实现的
functools.lru_cache竞态——满缓存时不同线程以相同未缓存参数并发调用可能抛出KeyError; - bpo-29532:创建
functools.partial()后,修改传入的 kwargs 字典不再影响 partial 对象; - bpo-29800:
partial.__repr__在partial.keywords的键不是字符串时崩溃(补丁作者 Michael Seifert); - bpo-29998:
ImportError的 pickle/复制现在保留name与path属性; - bpo-29942:修复
itertools.chain.from_iterable遇到长串空可迭代对象时的崩溃; - bpo-27863:修复 ElementTree 因竞态与错误类型导致的多个崩溃;
- bpo-30061 / bpo-30068:
IOBase的__next__()/readlines()在readline()或__next__()返回不可 size 对象时崩溃;同时检查PyObject_Size()、PySequence_Size()、PyMapping_Size()的结果; - bpo-30070:修复 parser 模块错误处理中的泄漏与崩溃;
- bpo-30243:移除
_jsonscanner 与 encoder 的__init__方法,误用可能导致内存泄漏或崩溃,现在对象在__new__中完全初始化; - bpo-28298:修复 array 类型
'Q'、'L'、'I'不接受大整数(实现__int__的对象)作为元素的问题(补丁作者 Oren Milman); - bpo-29615:
SimpleXMLRPCDispatcher不再把KeyError(或任何其他异常)链接到被分发方法抛出的异常; - bpo-8256:修复
input()在sys.stdin/sys.stdout的encoding/errors属性未设置或非字符串时失败或崩溃的问题; - bpo-25455:修复递归缓冲文件类对象的 repr 崩溃;
- bpo-29884:faulthandler 在 teardown 时恢复旧的
sigaltstack; - bpo-29854:使用 readline 的
history-size选项时readline段错误(补丁作者 Nir Soffer); - bpo-30961:修复 tracemalloc 中递减借用引用(borrowed reference)的问题;
- bpo-30532:修复 email 头部值解析器在特定情况下丢弃折叠空白(folding white space)的问题;
- bpo-29169:zlib 更新至 1.2.11;
- bpo-30003:修复 HZ 编解码器对转义字符的处理(基于 Ma Lin 的补丁);
- bpo-29990:修复 GB18030 解码器的范围检查(原始补丁作者 Ma Lin);
- bpo-30375:编译正则表达式时的警告现在总是指向用户代码所在行,而非 re 模块内部;
- bpo-29444:修复 match 对象
group()方法的越界缓冲区访问(基于 WGH 的补丁); - bpo-29110:
aifc.open()传入文件系统路径但文件不是合法 AIFF 格式时文件对象泄漏(补丁作者 Anthony Zhang); - bpo-29290:修复 argparse 帮助信息在不换行空格处错误换行的回归;
- bpo-29219:修复未初始化
ctypes.CDLL实例的 repr 无限递归; - bpo-29519:修复解释器关闭期间 weakref 在 multiprocessing 与自定义 codecs 罕见组合下抛异常;
- bpo-29100:修复 Python 3.6.0 引入的
datetime.fromtimestamp()回归:检查最小与最大年份; - bpo-26293:因 zipfile 破坏导致的变更(另见 bpo-29094);
- bpo-29692:修复
contextlib.contextmanager中RuntimeError被任意解除链(unchaining)的问题(补丁作者 Siddharth Velankar)。
五、文档、测试与构建基础设施
1. 文档(Documentation)
- bpo-30176:在 curses 文档中补充缺失的属性相关常量;
- bpo-26985:在 inspect 文档中补充 code 对象的缺失信息;
- bpo-28929:为文档页链接到 GitHub 上的源码文件;
- bpo-25008:将
smtpd.py标注为实际已弃用,并指向第三方 asyncio 替代品 aiosmtpd; - bpo-26355:每页增加指向对应主版本文档的 canonical 头部链接(补丁作者 Matthias Bussonnier);
- bpo-29349:修复文档构建代码中的 Python 2 语法。
2. 测试(Tests)
- bpo-30822:修复 regrtest 命令行解析,允许传递
-u extralargefile以运行test_zipfile64; - bpo-30383:从 master 分支反向移植并增强 regrtest——新增选项
--coverage、--testdir、--list-tests(仅列出测试文件不运行)、--list-cases(仅列出测试标识符不运行,issue 30523)、--matchfile(从文本文件加载测试过滤列表,issue 30540)、--slowest(--slow的别名);输出增加时间戳、测试结果、当前运行测试与含总耗时的Tests result: xxx汇总;同时修复--huntrleaks引用泄漏排查:预先暖缓存、显式创建所有按需创建的内部单例,避免引用泄漏检查的误报(issue 30675); - bpo-30357:
test_thread的setUp()使用support.threading_setup()与support.threading_cleanup()等待线程完成,避免对后续测试的随机副作用; - bpo-28087:在 macOS 上跳过
test_asyncore与test_eintr的 poll 失败,以及部分select.poll测试; - bpo-30197:增强
test.support的swap_attr()与swap_item()——在 with 语句内删除被替换的属性/项也能正常工作,旧值(或不存在时为 None)现在会赋给as子句的目标; - bpo-29571:为匹配
re.LOCALE行为,test_re.test_locale_flag改用locale.getpreferredencoding(False)确定测试正则的候选编码(默认区域编码为多字节编码时可正确跳过测试)。
3. 构建(Build)
- bpo-29243:配置
--enable-optimizations后,避免make test、make install等目标触发的非必要重建; - bpo-23404:不再基于文件修改时间自动重新生成生成文件,该操作改为显式执行——用
make regen-all取代make touch; - bpo-29643:修复
--enable-optimization(原文如此,指优化构建开关)未生效的问题。
4. Windows 与 C API
- bpo-30687:Windows 构建时定位
msbuild.exe而非vcvarsall.bat; - bpo-29392:防止向 msvcrt 模块传入无效参数时崩溃;
- bpo-27867:当
Py_LIMITED_API设为 0x03050400 到 0x03060000(不含)之间或 0x03060100 及以上时,PySlice_GetIndicesEx()函数被替换为宏。当前 Include/sliceobject.h 中仍保留函数声明与宏定义的共存结构,是稳定 ABI(limited API)演进的一个实例; - bpo-29083:修复部分公共 API 函数的声明——
PyArg_VaParse()与PyArg_VaParseTupleAndKeywords()此前在 limited API 中不可用;PyArg_ValidateKeywordArguments()、PyArg_UnpackTuple()与Py_BuildValue()在定义PY_SSIZE_T_CLEAN时对版本 < 3.3 的 limited API 不可用。
六、从 3.5.4rc1 看 CPython 维护分支的工程实践
纵观本版变更记录,可以提炼出 CPython 维护分支(backport 版本)的几条工程规律:
- 安全修复优先且成链:expat 的两次升级(bpo-29591 → bpo-30694)属于"上游出补丁、CPython 跟进打包"的典型路径,且对"是否受影响"做了逐 CVE 判断(如 CVE-2016-5300 因 Python 已用
XML_SetHashSalt注入熵而不受影响),这种审慎态度值得下游发行版参考; - 异常语义是核心稳定性的护城河:bpo-30876(相对导入异常类型)、bpo-29600(StopIteration 包装)、bpo-29935(index() 错误消息)等修复都在收敛"解释器把内部错误泄漏给用户"的边界;
- 回归防护与测试基建同步增强:bpo-29537 对旧字节码的兼容恢复、bpo-30383 对 regrtest 的大规模增强,说明维护版本不仅修 bug,还同步提升检测能力;
- C API 与稳定 ABI 持续打磨:bpo-27867、bpo-29083 直接关系到第三方扩展在 limited API 下的可用性,是生态兼容的重要一环。
对于仍在维护 3.5 系列(或希望理解后续 3.x 版本相应行为)的读者,本文件与仓库中的 Lib/test/test_urlparse.py 等测试用例一起,构成了验证这些修复行为是否仍然生效的可靠依据。
七、如何在当前仓库中验证这些修复
你可以在当前仓库中直接运行对应测试来验证上述修复的持续有效性(仓库为只读,仅需查看与运行测试):
# 验证 urllib.parse 的 splithost 片段解析(bpo-30500) python -m test test_urlparse -m test_splithost # 验证 subprocess 环境变量名校验(bpo-30730 / bpo-30746) python -m test test_subprocess # 运行标准库回归测试套件 python -m test test_multiprocessing test_asyncio test_unittest test_typing结合 Lib/test/test_urlparse.py 中# bpo-30500: # starts a fragment.的注释与断言、Lib/subprocess.py 中illegal environment variable name的校验,以及 Modules/pyexpat.c 中的XML_SetHashSalt调用,可以完整还原 3.5.4rc1 这批变更在解释器与标准库中的落地形态。
【免费下载链接】cpythonThe Python programming language项目地址: https://gitcode.com/GitHub_Trending/cp/cpython
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考