已关闭
【runtime】TPRT-001: 不可信指针解引用导致任意代码执行 #823
佳祺创建于  25 天前关闭于  1 天前
佳祺
佳祺
25 天前 创建

【runtime】TPRT-001: 不可信指针解引用导致任意代码执行

基本信息

属性
漏洞 ID TPRT-001
类型 arbitrary-code-execution (CWE-822: Untrusted Pointer Dereference)
严重性 Critical
置信度 CONFIRMED
文件 src/tprt/feature/src/tprt_sqhandle.cc
函数 TprtExeAicpuTask (第47-56行)
模块 tprt (Task Processing Runtime Transport)

漏洞描述

tprt_sqhandle.cc 中的 TprtExeAicpuTask 函数将用户可控的 uint64_t 值(来自 SQE 结构的 startPcAddr 字段)直接通过 reinterpret_cast 转换为函数指针并调用,整个数据流路径上不存在任何地址合法性验证。攻击者可通过构造恶意 SQE 控制程序执行流,实现任意代码执行。

漏洞代码

// tprt_sqhandle.cc:47-56
static uint32_t TprtExeAicpuTask(const TprtSqe_t* sqe)
{
    uint64_t pcStart = sqe->aicpuSqe.startPcAddr;      // 用户可控值
    uint64_t argsAddr = sqe->aicpuSqe.argsAddr;         // 用户可控值
    if ((pcStart == 0UL) || (argsAddr == 0UL)) {         // 仅检查非零
        return TPRT_SQE_PARA_IS_INVALID;
    }
    PfnAicpuSqeFunc aicpuFunc = TprtValueToPtr<PfnAicpuSqeFunc>(pcStart);  // 直接 reinterpret_cast
    return aicpuFunc(argsAddr);                          // 调用攻击者控制的地址
}

TprtValueToPtr 的实现(tprt_base.hpp:90-93):

template <typename TO>
inline TO TprtValueToPtr(const uint64_t value)
{
    return reinterpret_cast<TO>(static_cast<uintptr_t>(value));  // 无任何验证
}

完整数据流分析

攻击路径

攻击者构造恶意 TprtSqe_t
    │
    ▼
TprtSqPushTask(devId, sendInfo)          [tprt_api.cc:119] ← 公开 C API
    │  验证: sendInfo != NULL, sqeNum < depth, device/sq/worker 存在
    │  ❌ 不验证 SQE 内容
    ▼
SqPushTask(sqeAddr, sqeNum)              [tprt_sqhandle.cc:69]
    │  sqQueue_[sqTail] = sqe[copySqeNum]  ← 原样拷贝 SQE 到内部队列
    │  ❌ 不验证 startPcAddr
    ▼
WorkerWakeUp()                           [tprt_api.cc:147]
    │  唤醒 worker 线程
    ▼
TprtWorkerScheduleSq()                   [tprt_worker.cc:52]
    │  SqPeekTask → SqExeTask
    ▼
SqExeTask(sqe)                           [tprt_sqhandle.cc:111]
    │  验证: sqeType < TPRT_SQE_TYPE_INVALID (仅检查类型范围)
    │  分发到 tprtSqeExeFuncMap[TPRT_SQE_TYPE_AICPU]
    ▼
TprtExeAicpuTask(sqe)                    [tprt_sqhandle.cc:47]
    │  验证: pcStart != 0 && argsAddr != 0 (仅非零检查)
    │  ❌ 无地址范围/白名单验证
    ▼
aicpuFunc(argsAddr)                      ← 执行攻击者控制的地址

合法数据流对比

在正常执行路径中,startPcAddr 由 runtime 内部设置:

// xpu_kernel_task.cc:56
sqe->startPcAddr = RtPtrToValue(
    aicpuTaskInfo->kernel->GetFuncNameDevAddr(taskInfo->stream->Device_()->Id_())
);

合法值来自 GetFuncNameDevAddr(),返回设备上的内核函数地址。但 TprtSqPushTask 作为公开 C API,无法区分合法调用者和直接构造 SQE 的攻击者。

攻击面分析

入口点

TprtSqPushTask 声明于 src/tprt/inc/external/tprt_api.h,以 extern "C" 导出,是公开 API:

// tprt_api.h:21
uint32_t TprtSqPushTask(const uint32_t devId, const TprtTaskSendInfo_t* sendInfo);

攻击前提

  1. 攻击者进程能够链接到 tprt 库(libtprt.so
  2. 攻击者已通过 TprtDeviceOpenTprtSqCqCreate 创建了设备和 SQ/CQ
  3. 攻击者构造 TprtSqe_t,设置:
    • aicpuSqe.header.type = TPRT_SQE_TYPE_AICPU (值为 0)
    • aicpuSqe.startPcAddr = <目标地址> (任意非零值)
    • aicpuSqe.argsAddr = <参数地址> (任意非零值)

SQE 结构定义

// tprt_sqe_cqe.h:51-63
struct TprtStarsAicpuSqe {
    TprtStarsSqeHeader_t header;   // word0-1: type, sqId, taskSn 等
    uint64_t startPcAddr;          // word2-3: 函数地址 ← 攻击者控制
    uint64_t argsAddr;             // word4-5: 参数地址 ← 攻击者控制
    uint32_t timeout;              // word6
    uint32_t res[9];               // word7-15
};

缺失的安全验证

验证项 状态 说明
startPcAddr != 0 ✅ 存在 仅排除 NULL 指针
startPcAddr 地址范围检查 ❌ 缺失 未验证地址是否在合法代码段内
startPcAddr 白名单校验 ❌ 缺失 未与已注册的 AICPU 函数地址比对
SQE 来源认证 ❌ 缺失 无法区分内部/外部调用者
SQE 内容完整性校验 ❌ 缺失 无签名或哈希验证
argsAddr 地址范围检查 ❌ 缺失 仅检查非零

影响评估

  • 任意代码执行: 攻击者可在 runtime 进程上下文中执行任意代码
  • 权限提升: 若 runtime 以较高权限运行(如设备驱动访问权限),攻击者可获取相应权限
  • 拒绝服务: 跳转到无效地址可导致进程崩溃
  • 数据泄露: 通过控制 argsAddr,攻击者可读取任意内存内容

修复建议

方案一:地址白名单验证(推荐)

TprtExeAicpuTask 中增加 startPcAddr 白名单校验:

static uint32_t TprtExeAicpuTask(const TprtSqe_t* sqe)
{
    uint64_t pcStart = sqe->aicpuSqe.startPcAddr;
    uint64_t argsAddr = sqe->aicpuSqe.argsAddr;
    if ((pcStart == 0UL) || (argsAddr == 0UL)) {
        return TPRT_SQE_PARA_IS_INVALID;
    }
    // 验证 startPcAddr 是否在已注册的 AICPU 函数地址集合中
    if (!TprtManage::Instance()->IsValidAicpuFuncAddr(pcStart)) {
        TPRT_LOG(TPRT_LOG_ERROR, "Invalid startPcAddr: 0x%" PRIx64, pcStart);
        return TPRT_SQE_PARA_IS_INVALID;
    }
    PfnAicpuSqeFunc aicpuFunc = TprtValueToPtr<PfnAicpuSqeFunc>(pcStart);
    return aicpuFunc(argsAddr);
}

方案二:间接调用表

将函数指针替换为索引,通过内部调度表间接调用:

// SQE 中传递函数索引而非直接地址
uint32_t funcIndex = sqe->aicpuSqe.funcIndex;
if (funcIndex >= registeredFuncCount) {
    return TPRT_SQE_PARA_IS_INVALID;
}
return registeredAicpuFuncs[funcIndex](argsAddr);

方案三:API 访问控制

限制 TprtSqPushTask 仅允许内部调用,不对外暴露该 C API,改为内部函数调用。

验证方法

  1. 构造单元测试:创建 TprtSqe_t,设置 startPcAddr 为已知测试函数地址以外的值,验证是否被拒绝
  2. 模糊测试:对 startPcAddrargsAddr 进行随机值注入,验证不会执行非预期地址
  3. 代码审计:确认所有 TprtValueToPtr 调用点均有配套的地址验证逻辑
likedislike
ykl999
ykl999成员
24 天前 评论:

/assign @Reyn52166

likedislike
CANN-robotCANN-robot成员
24 天前 将 Reyn52166 设为负责人
ykl999
ykl999成员
24 天前 评论:

你好,该问题已收到。不可信指针解引用导致任意代码执行问题我们将会根据issue描述查看代码,尽快答复

likedislike
Lleihuan1成员
24 天前 将 guo-yanjun 设为负责人
guo-yanjun成员
11 天前 评论:

@syaunfang 感谢反馈。针对该问题,我们已完成初步分析并形成统一整改方案:计划将 libxpu_tprt.so 调整为仅构建期使用的静态库,内部链接到 Runtime v100/v200,同时隐藏 Tprt*、cce::tprt::*等动态符号,并清理相关安装及打包清单,确保交付包不再包含 libxpu_tprt.so 或 libxpu_tprt.a。
该方案将收口 TprtCqReportRecv、TprtSqPushTask 等内部接口通过动态链接或 dlsym 被外部直接调用的路径,并保留现有 Runtime 内部调用链。后续还将补充必要的输入健壮性检查,并完成动态依赖、符号表、安装包、功能回归及升级回滚验证。目前正在修改代码并进行依赖和兼容性确认,有进一步进展会及时同步。

likedislike
guo-yanjun成员
1 天前 评论:

@syaunfang 您好,TPRT 内部接口暴露问题已完成整改和验证,相关修改已提交代码仓,如果您后续有新的进展或诉求,欢迎随时重新开启此 Issue 或提交新的 Issue,我们会继续为您跟进。

likedislike
Gguo-yanjun成员
1 天前 issue状态由 待办的 改变为 已解决
Gguo-yanjun成员
1 天前 关闭了 issue
Gguo-yanjun成员
1 天前 issue状态由 已解决 改变为 已完成
CANN-robotCANN-robot成员
1 天前 添加了label:resolved