漏洞

TensorRT-LLM 的受限反序列化器仍放进了可执行的 PyTorch 全局对象(CVE-2026-24233

TensorRT-LLM 的 RLHF 权重热更新把 base64 编码的 pickle 交给受限反序列化器,但调用点以 ^torch.* 放行整个 PyTorch 模块族,使危险全局对象能在结果类型检查前参与 pickle 构造;rc15 通过优先级更高的精确拒绝表封住该路径。

暖纸手绘的检查站:密封的 pickle 包进入 TensorRT-LLM 工作进程,允许的全局对象在工坊前排队,阴影中的危险可调用对象被修复后的拒绝闸门拦下。
文章导航

研究依据SOSEC AI 基础设施安全研究 · 修复提交固定为 634f86c3910f ,对照父提交逐行复核 WorkerExtension.update_weights() 、 serialization.loads() 与 Unpickler.find_class()

来源NVIDIA 安全公告 / TensorRT-LLM 修复提交与父提交 / Python pickle 语义 / SOSEC 源码复核

1 一份用于恢复 GPU 张量的权重句柄,在进入 Worker 时仍是一段会执行构造指令的 pickle

NVIDIA 在 2026 年 7 月公布 CVE-2026-24233,影响 TensorRT-LLM 1.3.0rc14 及更早相关版本,1.3.0rc15 修复。公告给出 CVSS 3.1 8.4,高危,向量 AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H,归类为 CWE-502。风险集中在本地权重更新数据进入 Python Worker 的反序列化路径。

这一功能有明确工程目的。强化学习或持续训练进程产生新模型权重,推理 Worker 无需把每个张量复制成普通数组,而是通过 CUDA IPC 句柄在进程间恢复共享内存张量。代码把参数名、恢复函数和函数参数组成句柄列表,为不同设备 UUID 分组,再序列化传送。

接收端 WorkerExtension.update_weights() 取得当前 GPU 的 device_uuid,从字典里选出相应值。字符串值被视为 base64 编码的 pickle,先解码成字节,再交给项目自带的 serialization.loads()。调用点允许一组基本内置类型,并通过正则 ^torch.* 允许整个 PyTorch 模块族。

这个正则满足张量重建需要,也扩大了 pickle.Unpickler.find_class() 可以解析的全局对象集合。pickle 的 GLOBAL、STACK_GLOBAL 与 REDUCE 等操作在 loads() 返回前解析并调用对象。随后那句“结果必须是 list 类型”的检查只能验证已经构造完的结果,无法撤销构造期间发生的行为。

修复提交 634f86c3910f81e6c5eae4e81dd27de3e823ab66 修改两个文件。它没有取消广泛的 torch 允许规则,而是在受限 Unpickler 中新增 disallowed_imports,并要求拒绝判断先于精确允许和模块正则。调用点明确拒绝三个 PyTorch 全局对象。

1.1 公告把风险限定为本地向量,部署判断仍要追到谁能向 Worker 提交句柄

AV:L 表示利用需要本地访问条件,不应把它描述成面向互联网的无认证 HTTP RCE。代码路径通过 Ray Worker 扩展与 collective RPC(把同一控制动作分发给全部目标工作进程)进行权重更新,入口是否能被外部服务间接驱动,取决于部署的编排、训练控制面、RPC 暴露和身份策略。

PR:N 在 CVSS 语境中表示满足本地攻击向量后不要求该脆弱组件内的既有权限。它不等于任意互联网用户都能传入 ipc_handles。资产评估需要识别能够在节点创建进程、写入更新队列、调用 Ray 控制动作或影响训练—推理桥接数据的主体。

UI:N 与代码一致:Worker 处理数据时不需要管理员点击文件。AC:L 表明额外条件较少。机密性、完整性和可用性均为高,反映反序列化发生在承载模型、GPU 句柄、服务凭据和控制面的 Worker 进程内。

厂商公告本身同时使用了两种平台口径,资产判断时应并列记录,不能自行替厂商消解:正文描述点名“TensorRT-LLM for Linux”,但 Security Updates 表中包含 CVE-2026-24233 的版本行又把“Platform or OS”标为“All”。因此,应把实际安装的 TensorRT-LLM 包、脆弱代码路径与厂商更新表一起作为判断依据,不能只凭一个操作系统标签排除资产。版本核验仍要落到运行中的实际包:容器可能锁定预发布 wheel,镜像标签也可能不等于 Python 包版本。运行实例应输出 tensorrt_llm.__version__、wheel 哈希、镜像摘要与 rlhf_utils.py/serialization.py 文件哈希。

发行方或内部团队若回移补丁,接受条件是等价语义:Unpickler 接受拒绝表,find_class() 首先执行拒绝,load()/loads() 透传参数,RLHF 调用点传入三个精确拒绝项,并通过安全回归。提交散列可以不同,执行顺序不能不同。

1.2 RLHF 权重热更新追求的是低复制与不停服,可信关系因此落到句柄传输

训练与推理协同需要频繁更新权重。把数十亿参数完整序列化、经网络复制、再分配 GPU 内存会带来显著停顿。CUDA IPC 允许一个进程导出设备内存相关句柄,另一个进程在同一主机和兼容设备上下文中重建张量视图。

TensorRT-LLM 的 WorkerExtension 通过 ray_worker_extension_cls 注入 Worker,并以广播式 RPC 调用 update_weights。方法被 control_action_decorator 装饰,文档说明它在更新前等待活跃请求结束。权重替换是控制面动作,不属于普通推理请求。

ipc_handles 是按设备 UUID 索引的字典。每个设备对应一组 (param_name, tensor_handle),其中 tensor_handle 又是 (func, args)。恢复函数和参数作为数据传输,是 pickle 能力被采用的根本原因:它能保留 Python 全局对象引用与元组结构。

接收端会把参数列表中的第七项,即索引 6,替换为当前 self.device_id,再执行 func(*list_args)。这表明函数对象并非反序列化过程中的偶然产物,而是应用协议的正式字段。任何安全格式替代都必须显式表达允许的重建操作与参数,而不能只发送任意可调用对象。

权重更新结束后,代码调用模型加载器的 reload(..., allow_partial_loading=True),清理 CUDA IPC,并在收尾分支处理模块后加载钩子、MoE 负载均衡器、前缀缓存与 CUDA 同步。这个高权限生命周期解释了为何句柄来源与结构必须比普通业务 JSON 更严格。

1.3 代码与处置定位:漏洞函数、固定行号、发布分支、修复结论和临时控制

2 update_weights() 先改模型状态,再进入按设备选择的反序列化分支

方法开始时检查模型是否具有 first_pre_reload_weights 标记。首次更新会遍历模块,对实现了 pre_reload_weights 且未移除权重的模块调用预处理,然后设置标记。此时 Worker 已经进入权重切换状态。

ipc_handles 非空,代码记录日志并调用 get_device_uuid(self.device_id)。字典缺少当前 UUID 时抛出 ValueError。这个检查防止把其他设备的句柄直接用于当前 Worker,但没有认证字典整体来源,也没有验证选中值的序列化语义。

变量 serialized_handles 接收 ipc_handles[device_uuid]。如果它是字符串,代码走 base64+pickle 分支;否则为了向后兼容,直接把对象赋给 all_handles。这两种输入模式需要分别建模:字符串路径受 Unpickler 规则保护,预构造对象路径依赖 RPC 与调用者权限。

base64 解码函数只做编码转换。它可以在输入损坏时抛错,却不判断内容是否可信、是否为 pickle、是否包含安全全局对象。真正的安全判断从 serialization.loads() 及其 find_class() 才真正开始。

整个方法以 try/except 包裹,异常会记录 Encountered an error in update_weights 并重新抛出。审计日志若只保留这一行,会看不到拒绝的模块与名称。安全运营应在不泄露完整 pickle 的前提下记录拒绝项、来源作业、设备 UUID、调用主体和请求 ID。

2.1 输入路径把解码、对象构造、列表检查与后续可调用对象执行接成一条可验证过程

第一段是字典选择:device_uuid → ipc_handles[device_uuid]。第二段是编码:字符串 → base64.b64decode() → pickle 字节。第三段是解释:字节 → serialization.loads() → Python 对象图。第四段是协议解包:列表项 → 参数名、函数、参数。第五段才是显式调用与模型加载。

安全问题集中在第三段,却影响第五段。pickle 在构造对象图时已经能解析全局对象并执行 REDUCE 指定的可调用对象;返回的函数对象还会在第五段被应用再次调用。一个全局引用因此拥有两个可能的行为时刻:反序列化期间,或进入句柄循环之后。

修复针对第三段的已知危险全局对象做精确拒绝。更长期的协议设计应限制第四、第五段:每个 tensor_handle 使用固定操作码或枚举,参数采用结构化数值与字节字段,接收端从内部映射选择重建函数,输入永远不直接携带 Python 可调用对象。

设备索引替换也值得测试。代码假定 args 至少七项并把索引 6 改成 device_id。畸形参数会抛异常;恶意或不兼容结构可能在模型预处理之后打断更新。结构约束验证应在任何模型状态改变前完成。

device_uuid 选择、base64 解码、serialization.loads、func args 解包和函数调用组成的五段权重句柄路径。
loads() 返回后的类型检查只能看最终对象;pickle 操作码在它之前已经完成全局解析与构造。

2.2 pickle 是对象构造程序,不是只把字节映射成字段的被动数据格式

Python 官方文档明确警告 pickle 对不可信数据不安全。协议保存对象图、共享引用、类与函数的全局名称,以及重建对象所需的归约信息。Unpickler 像一台小型栈机执行操作码,最终产生 Python 对象。

GLOBAL 或 STACK_GLOBAL 根据模块名与名称请求一个全局对象,Unpickler 会调用 find_class(module, name)。REDUCE 从栈中取出可调用对象与参数并执行,再把结果压回栈中。BUILD、NEWOBJ 等操作继续修改对象。安全检查必须在全局解析与调用发生前限制能力。

受限 Unpickler 的核心价值就在重写 find_class()。它无法把 pickle 变成天然无执行格式,却可以控制允许解析哪些全局。允许集合越窄、越具体,攻击面越容易理解。模块正则会一次引入一个包下面众多公开与内部符号,风险随依赖版本变化。

检查 loads() 返回类型是常见误区。无论最终对象是不是列表,判断都发生在内部构造已经完成之后。安全有效的检查要在操作码解释之前验证格式,或在 find_class() 和操作码能力处限制。

base64、压缩、签名或加密都不会改变 pickle 的执行语义。签名可以认证发送者,前提是密钥与调用者权限可信;加密保护机密性;base64 便于文本传输。接收端仍然执行经过认证或解码后的 pickle,来源授权必须与允许行为相匹配。

至此,问题已经从“pickle 是否危险”收缩为一个可审计的问题:RLHF 调用究竟给 find_class() 交付了怎样的允许集合。下一章沿着这份实际策略继续,而不再重复格式本身的风险。

3 默认拒绝并没有失效,真正扩大的能力集合来自 RLHF 调用点给出的模块级例外

tensorrt_llm/serialization.py 包装标准 pickle,维护 BASE_EXAMPLE_CLASSES 精确允许表,并提供 register_approved_class()。修复前的 Unpickler 接收精确允许与模块正则;find_class() 只在其中一项命中时调用父类解析,否则抛出 ValueError

因此,问题不是项目完全没有受限加载器,而是实际许可范围由调用者提供的策略决定。RLHF Worker 交来一小组 builtins 和覆盖整个 torch 命名空间的模块规则,使兼容性例外重新放进了超出句柄协议所需范围的全局对象。

load()loads() 是并列入口:前者处理文件对象,后者用 io.BytesIO 包装字节,两者都直接构造自定义 Unpickler。它们不会先把输入解析成无执行结构,也没有在 find_class() 之外增加对象图上限,所以补丁参数必须分别抵达两个入口。

文件中的基础表还服务其他序列化场景,而 RLHF 调用点构造自己的局部策略。rc15 因此选择在公共 Unpickler 提供拒绝能力,再由这个业务入口传入三项拒绝;它没有声称一次改写仓库中所有 pickle 用法。

3.1 真实策略由允许与拒绝的裁决顺序决定,也会随 PyTorch 版本改变

正则使用 re.match,从模块字符串开头匹配。torchtorch.storagetorch.hub 以及更多子模块都符合。find_class() 不再检查具体名称,只要模块命中就交给标准 Unpickler 导入对应全局对象。

这种规则对兼容性很有吸引力。PyTorch 不同版本、张量类型与存储重建可能引用多个内部辅助函数,逐个维护允许表容易漏项。一个前缀能让合法句柄继续工作,减少发布与升级阻力。

安全成本是允许集由 TensorRT-LLM 当前代码扩展到“运行环境里这个 PyTorch 版本可导入的所有全局”。依赖升级可能增加新的可调用对象,应用代码无需变化,实际能力集合却已经改变。审计无法只看自己的仓库得出完整清单。

修复采用局部阻止三个已知危险项,是在兼容性与风险之间的最小变更。它保留广泛正则满足 IPC,同时确保三个对象即使匹配正则也先被拒绝。顺序因此是修复的核心,不只是名单内容。

长期演进可以通过记录正常权重更新实际解析的模块与名称,生成版本化精确允许表。测试覆盖支持的 PyTorch/设备矩阵,遇到新符号走评审,而不是自动放行整个模块族。这样能逐步缩小对拒绝表的依赖。

3.2 pickle 构造与应用调用发生在两个不同时间点,外层类型检查只能落在两者之间

阶段 A 发生在 serialization.loads(decoded_data) 内。Unpickler 读取 GLOBAL/STACK_GLOBAL,调用 find_class() 获得对象;REDUCE 等操作可能立刻调用。外层尚未得到 all_handles,无法用 isinstance() 或循环结构拦截。

阶段 B 发生在 for param_name, tensor_handle in all_handles。应用解包 func, args,复制参数、改写设备索引,然后主动调用 func(*list_args)。即使 pickle 构造本身没有副作用,错误的可调用对象仍可能在这里执行。

此次修复通过阶段 A 的 find_class() 拒绝阻断三个全局对象进入对象图,也间接防止它们在阶段 B 作为 func 出现。对于其他允许对象,阶段 B 仍依赖它们确实是预期张量重建函数。更严格的条目结构约束与可调用对象身份比较可以继续收窄。

列表类型检查位于 A 与 B 之间,只确认顶层容器。它没有验证每项是二元组、参数名属于模型、tensor_handle 是二元组、args 长度足够、func 在允许集合,也没有在预处理模型之前完成。安全加固应把完整结构验证提前。

反序列化期间 REDUCE 调用与应用循环 func args 显式调用组成的两个执行阶段对照图。
阶段 A 在 loads() 返回前完成;阶段 B 是权重句柄协议设计的一部分,两处都要求可调用对象来源可信。

两个执行时间点厘清后,补丁为何必须同时改序列化辅助函数和 RLHF 调用点也随之清楚:前者提供拒绝优先的裁决能力,后者给出这个业务路径必须扣除的三个精确对象。

4 修复让 load()loads() 各自把拒绝表直送 Unpickler,再由 RLHF 调用点提供策略

Unpickler.__init__() 新增 disallowed_imports=None,保存为 self.disallowed_imports = disallowed_imports or {}。使用 None 避免调用者未传时共享可变默认对象,现有接口保持兼容。

find_class() 的第一段现在检查 name in self.disallowed_imports.get(module, []),命中立即抛 ValueError。只有未命中才进入精确 approved_imports,然后进入 approved_module_patterns。三个集合构成明确优先级。

模块级 load() 接收新参数,并在为文件对象构造 Unpickler 时直接传入;字节式 loads() 独立接收参数,用 io.BytesIO 包装输入,再把拒绝表直接传给自己构造的 Unpickler。否则调用点即使创建拒绝表也无法到达实际裁决点。补丁分别修改两个并列入口,避免文件流与字节流行为不一致。

WorkerExtension.update_weights() 创建局部拒绝字典,并在 loads() 调用中传入。注释解释 CUDA IPC 句柄需要 torch 重建辅助,因此保留广泛模式;拒绝项在 Unpickler 中优先。修复意图和控制顺序都直接写进源码。

补丁没有修改列表检查、显式 func 调用、模型重载或 RPC 编排,便于把因果范围压到全局允许策略。回归验收既要确认危险全局对象失败,也要确认合法 IPC 句柄仍能跨 GPU 工作进程更新权重。

find_class 的三项裁决:命中精确拒绝便抛出 ValueError,任一允许路径命中便调用 super().find_class,全部未命中同样抛出 ValueError。
图中完整列出 builtins 允许项,包括 NoneType。命中拒绝表立即抛错;命中精确允许或模块正则时,经 super().find_class() 返回;两项允许均未命中时直接拒绝,不进入父解析器。

4.1 补丁关闭已确认能力路径,但没有替身份、协议和模型事务完成所有工作

rc15 收窄的是到达 RLHF 反序列化入口后的全局解析,不负责认证谁能调用 Ray 控制动作、批准哪个训练作业或把更新绑定到哪组 Worker。它也没有把仍然存在的包级模块允许改成完整精确名单。因此,升级是明确结论,控制面身份与基于正常语料的精确操作表则是另外两项加固。

提交没有增加编码或解码字节数、操作码、嵌套深度、条目数和张量总量等资源上限,也没有验证每个条目的参数名、元组结构、args 布局、形状、数据类型、存储范围或可调用对象身份。拒绝三项能力可以成立,同时其他畸形输入仍造成可用性或协议错误;这些限制应按真实句柄格式补上。

非字符串兼容分支直接使用现成 Python 对象,不进入已修加载器,安全性取决于 RPC 与生产端怎样创建对象;两条分支随后又共享 func(*args)。同样,仓库中其他标准 pickle、PyTorch 序列化或不同策略的调用者不自动继承这项局部修复。应用层精确操作检查能够覆盖两种输入格式,但范围外发现仍需自己的证据。

模型切换也不是原子事务:预处理可能先发生,张量逐项恢复,部分加载受到允许,收尾由后续控制动作完成。把 rc15 写入磁盘也不会改变已经导入旧模块的解释器。发布必须重启或替换全部 Worker,覆盖弹性模板与节点缓存,并用一致的模型和策略身份证明没有旧进程留在服务中。

最后,公告与提交证明的是潜在影响和源码能力变化,不证明某个组织已经遭到利用。运行时结论要来自更新记录、主机与模型证据。这番限定让处置顺序更清楚:先升级或等价回移,升级前暂停或严格代理更新并压缩 Worker 权限,随后再完成身份、结构、资源与事务加固。

4.2 长期协议应收回可调用对象的选择权,同时保留有界 CUDA IPC 重建

发送端不传 func,只传如 rebuild_cuda_tensor_v1 的枚举。接收端在代码内维护不可由输入扩展的映射,选择固定函数。参数使用 protobuf、msgpack 或具有严格结构约束的 JSON/二进制格式,字节缓冲独立传输。

结构约束定义参数名、设备 UUID、存储句柄、偏移、大小、形状、步幅、数据类型与版本。每个数值有范围,数组有长度上限,参数名必须存在于待更新模型。接收端完成全部验证后才调用 pre_reload_weights

完整性层对清单和每个句柄块签名,绑定训练作业、模型版本、目标集群、设备映射、过期时间与 nonce。控制服务验证后颁发一次性更新 ID。Worker 只接受来自本地受控代理的已验证结构。

兼容升级通过协议版本和显式能力协商完成,不把未知字段自动解释为 Python 对象。旧 pickle 路径先默认关闭,再在受控迁移期保留,记录使用者并设定删除日期。

性能可以保留:CUDA IPC 的底层句柄与共享内存无需因去掉 pickle 而复制模型数据。变化主要是控制元数据编码和函数选择。安全与吞吐不必二选一,关键是把可执行对象从输入协议移回接收端代码。

5 真实利用面由句柄来源、控制 RPC、节点本地权限与 Worker 身份共同决定

第一项是版本与功能:运行受影响版本,并启用 Ray WorkerExtension 的权重更新路径。没有使用 RLHF IPC 热更新的实例仍应升级,但实际入口可能不可达。资产清单要区分安装、加载和调用。

第二项是输入控制:主体能够让选中设备 UUID 对应的值成为其构造的 base64 字符串或对象。可能来源包括本地进程、训练作业、编排队列、共享存储、RPC 调用或被攻陷的上游控制服务。每个部署需画出真实数据生产者。

第三项是 Worker 权限。进程可访问模型文件、缓存、日志、云身份、Ray 控制面、GPU 和节点文件系统。容器非 root、只读文件系统、最小挂载、独立服务账号和网络分段可以限制后果。

第四项是生命周期时机。control_action_decorator 等待活跃请求结束后更新,使动作位于控制面窗口。监控可以对非维护窗口、异常调用者、频繁失败、未知训练作业和权重哈希突变告警。

第五项是回滚与共享。权重更新可能通过广播式控制调用跨多个工作进程执行,一个异常句柄影响多个进程。控制面应先验证签名、来源、结构与版本,再分批广播;失败时保持旧权重可用并隔离来源。

5.1 检测从更新意图、生产者和载荷身份一直追到主机与模型状态

控制面日志记录调用主体、训练作业、模型 ID、权重版本、设备 UUID 集合、句柄格式、载荷哈希、大小、工作进程集合、审批或签名与请求 ID。不要记录 pickle 原文,它可能含敏感信息并会诱使分析工具反序列化。

序列化层在 ValueError 前记录模块与名称、拒绝来源(精确拒绝表、未在允许表、结构错误)、调用点和版本。任一 rc15 精确拒绝对的单次命中即按高优先级处理。

工作进程侧记录重载前处理开始、反序列化成功或失败、条目数量、允许的重建函数集合、模型重载结果、前缀缓存重置和耗时。异常发生后确认模型是否已经进入预处理状态,是否需要重启或回滚。

主机与容器遥测关注权重更新窗口内的新进程、文件写入、网络连接、Python 模块导入、异常 Hub 目录访问和模型外文件变化。它们不能单独证明 CVE 利用,但与拒绝日志和载荷哈希关联后能提高置信度。

历史排查从所有 update_weights() 调用、错误日志和工作进程时间窗开始,查找陌生调用者、无对应训练版本、base64 异常大小、反序列化错误、模型哈希漂移和节点行为。若缺少模块级日志,可用 Python 审计事件或 EDR 还原部分活动。

5.2 处置先停低信任生产者、收窄 Worker 权限,再进入受控发布

优先暂停 RLHF 热更新;若业务不能完全暂停,只允许一个经过严格认证、权限收窄的发布服务从获准训练制品构造并发起更新,拒绝上游直接提交序列化数据块。阻断未经身份认证的 Ray 控制接口,限制训练作业到推理 Worker 的网络与队列写权限。已有推理可继续使用固定权重。该发布服务只能收窄提交主体,不能修复 pickle 语义,因此仍是升级前的临时措施。

为权重包增加来源签名、模型版本、设备映射与内容哈希校验。签名不能让 pickle 天然安全,却能把允许生产者收窄到受控训练流水线,并在出现问题时定位责任作业。密钥存放在独立控制服务,不进入训练容器。

Worker 使用只读根文件系统、非 root、最小卷挂载、无交互命令行、最小云权限和限制出网。模型写目录与应用目录分离。即使反序列化发生异常行为,也减少持久化、秘密读取和横向移动空间。

保全可疑载荷时,只保存原始 base64 文本或字节的哈希与隔离副本,分析使用 pickletools.dis() 等非执行检查,并在无凭据、无网络沙箱中进行。切勿用普通 pickle.loads() “看看里面是什么”。

升级到 rc15 或更新受支持版本后,用合法句柄和三项拒绝回归验证,再恢复热更新。恢复分批执行,观察拒绝、模型一致性和 Worker 健康。旧的未签名更新队列不直接重放。

5.3 验收同时证明拒绝优先级、无害复现、合法 IPC 与全量 Worker 生效

源码检查确认修复提交或等价改动:Unpickler 初始化保存拒绝表,find_class() 第一分支检查拒绝,load()loads() 均透传,RLHF 调用点定义三项并传入。顺序错误会让前面的模块级允许分支提前返回。

单元测试为每个拒绝项构造只触发全局解析的安全样本,断言 ValueError 中包含模块和名称,并用替身证明目标函数未被调用。另加一个同时出现在精确允许表与拒绝表的样本,确认拒绝获胜。

允许测试覆盖实际 IPC 重建辅助函数、基础内置类型、多设备 UUID、字符串与兼容对象分支。合法权重更新后,抽样参数与预期哈希、形状、数据类型一致,前缀缓存适当重置,推理回归通过。

结构测试覆盖非列表、条目长度错误、args 少于七项、未知参数名、重复参数、超大列表、错误设备和部分加载失败。失败应在模型状态改变前尽可能发生,并给控制面可定位的错误。

集成测试在隔离 Ray 集群运行,限制网络和文件写入,通过审计钩子记录导入与系统行为。升级证据包含 wheel 与镜像摘要、测试日志、Worker 版本、模型一致性和部署批次。

6 受限 Unpickler 的安全强度取决于最终能力集合,而不是类名里的“受限”二字

CVE-2026-24233 揭示的核心并不是项目忘了使用自定义加载器,而是一个为兼容 PyTorch 句柄而开放的包级例外,足以压过原本清晰的默认拒绝设计。外层列表检查又位于对象构造之后,无法替前面的能力判断补救。

rc15 在公共 Unpickler 中加入拒绝优先级,并由 RLHF 入口扣除三项厂商确认对象,在保留 CUDA IPC 兼容的同时关闭已知路径。受影响版本需要升级或实施语义等价的回移;发布还要证明新模块已由所有 Worker 实际加载。

验收一端使用无害测试证明拒绝发生在父解析器之前,另一端用真实生产者生成的句柄证明合法多 GPU 更新、模型输出和集群一致性没有被破坏。控制面身份、最小进程权限和历史更新证据把源码修复接到可运营的生产控制。

长期方向是让传输内容只表达版本化操作和有界数据,由接收端代码选择唯一允许的重建函数。这样,依赖包新增导出符号不再悄悄扩大协议权限,张量更新的兼容性也能通过显式版本与测试管理。

研究记录

7证据、对象与来源

下面保留本文实际使用的标识、时间和原始材料,便于继续调查。

7.1研究对象

报告涉及的产品、行为者、技术、受影响对象和控制点。

漏洞编号CVE-2026-24233

TensorRT-LLM 受限反序列化器绕过

影响上限TensorRT-LLM 1.3.0rc14 and earlier

NVIDIA 公告受影响版本

修复下限TensorRT-LLM 1.3.0rc15

厂商修复版本

修复提交634f86c3910f81e6c5eae4e81dd27de3e823ab66

收紧反序列化允许类型的上游提交

高风险允许规则approved_module_patterns=[r'^torch.*']

允许整个 PyTorch 模块族

拒绝全局torch.storage._load_from_bytes

优先于 torch 模块正则拒绝

拒绝全局torch.hub._load_local

优先于 torch 模块正则拒绝

拒绝全局torch.save

优先于 torch 模块正则拒绝

函数链update_weights → b64decode → serialization.loads → Unpickler.find_class → func(*args) → model_loader.reload

完整输入与执行链

7.2事件时间

  1. 修复提交进入上游

    提交 634f86c 新增拒绝优先级与 RLHF 局部名单。

  2. NVIDIA 公告发布

    CVE-2026-24233 公开并指定 rc15 修复。

  3. SOSEC 完成父提交与修复对照

    逐行复核句柄、反序列化与显式调用路径。

7.3来源与材料

  1. NVIDIA TensorRT-LLM 2026 年 7 月安全公告https://nvidia.custhelp.com/app/answers/detail/a_id/5840
  2. CVE-2026-24233 记录https://www.cve.org/CVERecord?id=CVE-2026-24233
  3. TensorRT-LLM 修复提交 634f86chttps://github.com/NVIDIA/TensorRT-LLM/commit/634f86c3910f81e6c5eae4e81dd27de3e823ab66
  4. 直接脆弱父提交 bf61b67https://github.com/NVIDIA/TensorRT-LLM/commit/bf61b672597c21d480c0f3005218badb05df4737
  5. 固定修复提交与行号的 TensorRT-LLM serialization.pyhttps://github.com/NVIDIA/TensorRT-LLM/blob/634f86c3910f81e6c5eae4e81dd27de3e823ab66/tensorrt_llm/serialization.py#L128-L211
  6. 固定修复提交与行号的 TensorRT-LLM RLHF WorkerExtensionhttps://github.com/NVIDIA/TensorRT-LLM/blob/634f86c3910f81e6c5eae4e81dd27de3e823ab66/tensorrt_llm/llmapi/rlhf_utils.py#L37-L132
  7. TensorRT-LLM v1.3.0rc15 发布页https://github.com/NVIDIA/TensorRT-LLM/releases/tag/v1.3.0rc15
  8. TensorRT-LLM 上游仓库https://github.com/NVIDIA/TensorRT-LLM
  9. Python pickle 文档与安全警告https://docs.python.org/3/library/pickle.html
  10. Python pickletools 非执行检查文档https://docs.python.org/3/library/pickletools.html
  11. CPython 3.13 pickle 实现https://github.com/python/cpython/blob/3.13/Lib/pickle.py
  12. PyTorch 序列化语义https://pytorch.org/docs/stable/notes/serialization.html
  13. PyTorch torch.save 文档https://pytorch.org/docs/stable/generated/torch.save.html
  14. PyTorch Hub 文档https://pytorch.org/docs/stable/hub.html
  15. Ray Actor 与 Worker 文档https://docs.ray.io/en/latest/ray-core/actors.html
  16. NVIDIA CUDA Runtime IPC 相关接口文档https://docs.nvidia.com/cuda/cuda-runtime-api/group__CUDART__DEVICE.html
  17. CWE-502:不可信数据反序列化https://cwe.mitre.org/data/definitions/502.html