飞桨安全公告 PDSA-2023-007 深度解析:paddle.linalg.matrix_rank 除零漏洞(CVE-2023-38675)的复现、修复与防护
【免费下载链接】PaddlePArallel Distributed Deep LEarning: Machine Learning Framework from Industrial Practice (『飞桨』核心框架,深度学习&机器学习高性能单机、分布式训练和跨平台部署)项目地址: https://gitcode.com/GitHub_Trending/pa/Paddle
本文围绕飞桨(PaddlePaddle)官方安全公告 PDSA-2023-007 展开,剖析paddle.linalg.matrix_rank在空张量输入下触发除零(FPE)漏洞(CVE-2023-38675)的根因、PoC 复现过程、补丁修复方案与回归测试验证。读者读完后,将理解该漏洞的触发条件与源码级成因,掌握修复后版本的正确行为语义,并了解飞桨 FPE 类漏洞的共同模式、安全边界认定规则以及漏洞上报渠道,从而在自研算子或二次开发中避免同类缺陷。
一、漏洞概览
PDSA-2023-007 是飞桨官方于 2023 年披露的一则安全公告,完整信息如下:
| 项目 | 内容 |
|---|---|
| 公告编号 | PDSA-2023-007 |
| CVE 编号 | CVE-2023-38675 |
| 漏洞类型 | FPE(浮点异常,由整数除零触发) |
| 受影响 API | paddle.linalg.matrix_rank |
| 受影响版本 | < 2.6.0 |
| 修复版本 | 飞桨 2.6.0 |
| 报告者 | Tong Liu of ShanghaiTech University |
在 security/README_cn.md 的公告总表中,该公告与 PDSA-2023-017(paddle.amin)、PDSA-2023-015(paddle.lerp)、PDSA-2023-014(paddle.topk)等一批 FPE 类公告同列,全部标注受影响版本< 2.6.0,是飞桨 2.6.0 版本批量修复的除零/浮点异常问题的典型代表。
需要特别说明的是,公告中的 "FPE" 在 POSIX 术语中对应SIGFPE信号。虽然名称含 "Floating Point",但本漏洞实际由整数除法numel / (rows * cols)在除数为 0 时触发,导致进程以非干净方式退出(崩溃),这正是其被认定为安全问题的关键——详见后文"安全模型"一节。
二、受影响 API:paddle.linalg.matrix_rank 的功能与参数
paddle.linalg.matrix_rank用于计算矩阵的秩,即大于指定阈值的奇异值个数(hermitian=False时)或绝对值大于阈值的特征值个数(hermitian=True时),其 Python 层实现在 python/paddle/tensor/linalg.py:
def matrix_rank( x: Tensor, tol: float | Tensor | None = None, hermitian: bool = False, atol: float | Tensor | None = None, rtol: float | Tensor | None = None, name: str | None = None, ) -> Tensor:核心参数语义如下:
- x:输入张量,形状应为
[..., m, n],其中...为 0 个或多个 batch 维度;若x是批量矩阵,则输出具有相同的 batch 维度。数据类型支持float32、float64、complex64、complex128。 - tol:单独使用的阈值。未指定时按
tol = sigma * max(m, n) * eps计算,其中sigma为最大奇异值(或特征值绝对值),eps为x数据类型对应的机器精度;批量输入时每个 batch 独立计算。 - atol / rtol:与
tol互斥的绝对/相对容差组合(同时指定atol/rtol时不允许再传tol,否则抛出ValueError)。容差最终取max(atol, sigma_1 * rtol),sigma_1为最大奇异值。若rtol未指定,默认取max(m, n) * eps;若rtol未指定且atol显式大于 0,则rtol取 0。 - hermitian:是否将
x视为 Hermitian 矩阵。为True时走特征值分解路径(更高效),函数内部不校验x是否真的为 Hermitian,仅使用矩阵下三角部分计算。
在动态图模式下,Python 层会根据参数形式分别派发到底层算子:使用tol时调用matrix_rank/matrix_rank_tol,使用atol/rtol时调用matrix_rank_atol_rtol(见 python/paddle/tensor/linalg.py)。
三、漏洞根因:除零语句的源码级定位
公告明确指出漏洞触发条件:当由x的维度计算出的rows或cols为 0 时,numel / (rows * cols)会触发除 0 异常。
在 Kernel 实现中,batch 数量通过如下方式计算:
int k = std::min(rows, cols); int batches = static_cast<int>(x.numel() / (rows * cols));其中rows、cols直接取自张量形状的最后两个维度:
auto dim_x = x.dims(); int rows = static_cast<int>(dim_x[dim_x.size() - 2]); int cols = static_cast<int>(dim_x[dim_x.size() - 1]);当输入形状最后两维任一为 0(例如 PoC 中的[0, 0, 0, 0, 0],rows = 0, cols = 0)时,rows * cols = 0,此时x.numel() = 0,表达式退化为整数运算0 / 0,在 C++ 中触发未定义行为,实际表现为进程收到SIGFPE信号而崩溃。GPU 路径中还存在对rows * cols <= INT_MAX的约束检查(cusolver 的gesvdj系列接口不支持 int64 索引),但空输入的除零防护是另一独立问题。
从当前仓库的 CPU Kernel 实现 paddle/phi/kernels/cpu/matrix_rank_tol_kernel.cc 可以看到,修复后的代码将batches的计算严格置于x.numel() == 0的早退判断之后;同理,GPU Kernel paddle/phi/kernels/gpu/matrix_rank_tol_kernel.cu 也采用相同顺序。这一"先防护、后计算"的顺序正是补丁的核心改动,而修复前的版本中该除零表达式直接暴露在 kernel 主流程,未对空输入做前置拦截。
四、PoC 复现与逐步解析
公告附带的 PoC 代码如下:
import paddle import numpy as np x = np.random.uniform(0, 0, [0, 0, 0, 0, 0]).astype(np.float32) x = paddle.to_tensor(x) paddle.linalg.matrix_rank(x)逐步解析:
np.random.uniform(0, 0, [0, 0, 0, 0, 0])构造一个形状为[0, 0, 0, 0, 0]、元素个数为 0 的 5 维空数组(uniform区间上下界均为 0 仅用于生成空数组,实际并不产生任何元素)。paddle.to_tensor(x)将其转换为形状[0, 0, 0, 0, 0]、dtype 为float32的飞桨张量,numel() == 0。paddle.linalg.matrix_rank(x)进入matrix_rankKernel:最后两维rows = 0, cols = 0,执行batches = numel / (rows * cols) = 0 / 0,触发整数除零,进程崩溃(SIGFPE)。
该 PoC 的精妙之处在于:它不需要任何恶意构造的数值,仅靠"空张量 + 零尺寸维度"即可稳定触发,是典型的输入校验缺失型漏洞,攻击面极低、影响面极广——任何将动态形状张量(如 batch 维度为 0 的数据流、预处理后为空集的特征)传入matrix_rank的用户都可能命中。
五、补丁修复方案:空输入早退与零填充
根据公告,修复合入于 PR #55644 的 commit9bb6c669206c4bcc3ce3f6daf8a55650e190c1a1,并包含在飞桨 2.6.0 版本中。当前仓库源码印证了修复后的防御逻辑:
// paddle/phi/kernels/cpu/matrix_rank_tol_kernel.cc if (x.numel() == 0) { dev_ctx.template Alloc<int64_t>(out); if (out && out->numel() != 0) { Full<int64_t, Context>(dev_ctx, out->dims(), 0, out); } return; } int k = std::min(rows, cols); int batches = static_cast<int>(x.numel() / (rows * cols));修复策略可概括为三点:
- 前置防护:在计算
k、batches(含除零表达式)之前,先判断x.numel() == 0,命中即提前返回,从根上杜绝除零。 - 语义正确:空矩阵的秩在数学上定义为 0,因此用
Full将输出填充为 0,保证结果正确而非仅"不崩溃"。输出张量形状保持为x.shape[:-2](即去掉最后两个矩阵维后的 batch 维度),与正常路径一致。 - CPU/GPU 双路径覆盖:同样的防护逻辑同时存在于 CPU Kernel paddle/phi/kernels/cpu/matrix_rank_tol_kernel.cc 与 GPU Kernel paddle/phi/kernels/gpu/matrix_rank_tol_kernel.cu,
matrix_rank、matrix_rank_tol、matrix_rank_atol_rtol三个算子路径均有对应处理,确保不同参数组合下行为一致。
此外,Kernel 注册时输出数据类型被显式指定为INT64(见 paddle/phi/kernels/cpu/matrix_rank_tol_kernel.cc 的kernel->OutputAt(0).SetDataType(phi::DataType::INT64)),即matrix_rank的返回值为 int64 类型,这一点在验证修复后行为时需要注意。
六、修复后的回归测试验证
仓库中的单元测试 test/legacy_test/test_matrix_rank_op.py 专门覆盖了含 0 尺寸维度的场景,是修复有效性的直接证据:
- 静态图场景:构造
shape=[2, 0, 6, 0]的输入,运行paddle.linalg.matrix_rank,断言输出形状为x.shape[:-2],即[2, 6](见 test/legacy_test/test_matrix_rank_op.py)。 - 动态图 CPU/GPU 场景:分别验证
(2, 0, 6, 6)、(0, 0)、(2, 3, 8, 0)(CPU)与(2, 3, 0, 7)(GPU)等形状,断言输出形状与x.shape[:-2]一致,且输出值全部为 0(见 test/legacy_test/test_matrix_rank_op.py)。
这些用例覆盖了"rows=0"、"cols=0"、"全零维"等多种组合,既验证了不再崩溃,也验证了空输入的秩为 0 的正确语义。对于升级到 2.6.0 及以后版本的用户,可直接运行该测试文件确认修复状态;对于框架开发者而言,这也是"新增防御逻辑必须配套回归测试"的规范示范。
七、同类 FPE 漏洞模式与通用防护经验
PDSA-2023-007 并非孤例。根据 security/README_cn.md 公告总表,2023 年飞桨披露的 FPE 类型漏洞还包括:
- PDSA-2023-006:
paddle.nanmedian - PDSA-2023-009:
paddle.linalg.eig - PDSA-2023-014:
paddle.topk - PDSA-2023-015:
paddle.lerp - PDSA-2023-017:
paddle.amin - PDSA-2023-022:
paddle.argmin/paddle.argmax
它们与 PDSA-2023-007 均由上海科技大学的 Tong Liu 报告,受影响版本同为< 2.6.0,并一同在 2.6.0 中修复。从这批公告可以归纳出 FPE 类漏洞的共性根因模式:
- 缺少输入形状前置校验:直接使用
numel、rows、cols参与除法或比值运算,未考虑 0 尺寸、空张量等退化情形; - 退化输入未定义语义:数学库函数(SVD、特征值分解等)对 0 维矩阵的处理依赖底层 LAPACK/cuSOLVER 行为,框架层必须显式定义并拦截。
对算子开发者的通用防护建议:凡涉及numel / (rows * cols)一类与形状相关的除法,务必在运算前校验除数是否为 0;凡涉及空张量(numel() == 0)的路径,必须定义明确的输出语义(如 0 填充)并早退,同时配套覆盖 0 尺寸维度的单元测试。
八、安全模型、升级建议与漏洞报告
飞桨在 SECURITY_cn.md 中明确了安全问题认定边界:
- 由于模型可执行任意计算、操作文件、网络通信等,飞桨计算图执行可能造成内存耗尽、死锁等非预期行为,但只有当这些行为超出所涉及操作意图时才认定为安全问题;
- 框架对非预期参数和行为的检查会抛出异常(Python)或返回错误状态(C++),此类干净退出不视为安全问题;
- 若非预期参数导致内存破坏或非干净退出(如本公告中的 SIGFPE 崩溃),则认定为安全问题。
PDSA-2023-007 正属于"非干净退出"情形,这也是其获得 CVE-2023-38675 编号并进入安全公告的原因。
升级建议:所有使用paddle.linalg.matrix_rank(或其他受影响 API)且版本低于 2.6.0 的用户,应尽快升级到包含修复的 2.6.0 及以上版本;因业务约束无法立即升级的,应避免向该 API 传入含 0 尺寸维度的张量,并在上游对动态形状做非空校验。
漏洞报告:飞桨鼓励安全研究人员通过百度安全应急响应中心(BSRC)平台负责任地披露(Responsible Disclosure)安全问题,提交时建议包含:漏洞细节与可复现的 PoC、攻击场景及攻击者可能达成的效果、问题是否已公开、署名信息。飞桨将在发布中注明修复情况,并在致谢公告中公布漏洞详情与提报人信息。
九、总结
PDSA-2023-007(CVE-2023-38675)是一例典型的"输入校验缺失导致除零崩溃"型框架漏洞:一行未防护的numel / (rows * cols),在空张量输入下即可触发 SIGFPE 使进程崩溃。修复方案以"空输入早退 + 0 填充语义"双管齐下,并通过 CPU/GPU 全路径覆盖与回归测试确保了长期有效性。该案例与 2023 年同批 FPE 公告共同提醒我们:数值库算子的健壮性始于对退化输入(0 尺寸、空张量)的显式定义与前置防护。
【免费下载链接】PaddlePArallel Distributed Deep LEarning: Machine Learning Framework from Industrial Practice (『飞桨』核心框架,深度学习&机器学习高性能单机、分布式训练和跨平台部署)项目地址: https://gitcode.com/GitHub_Trending/pa/Paddle
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考