
在 2025/8/26 11:21, Zhangfei Gao 写道:
这个方案3 我有点忘了
[PATCH 4/4] uacce: ensure safe queue release with state management
这个是解决这个问题的么
---------- Forwarded message --------- From: Zhangfei Gao <zhangfei.gao@linaro.org> Date: Mon, 21 Oct 2024 at 17:04 Subject: uacce ioctl UACCE_CMD_PUT_Q To: Yang Shen <shenyang39@huawei.com>, Hao Fang <fanghao11@huawei.com>, liulongfang <liulongfang@huawei.com>
讨论个uacce ioctl 问题: 进程1 put_q 之后,还没unmap 进程2 get_q 获取到这个q,mmap, 可能有风险访问/修改到进程1 映射到的内容
如何解决 方案1: uacce_put_queue + if (q->mapping) { + unmap_mapping_range(q->mapping, 0, 0, 1); + q->mapping = NULL; + }
这个方案不行,会同时unmap掉其他共享该filep->f_mappig的映射 比如多个线程,打开同一个设备,尽管fd/filep不同,但filep->f_mappig相同, 做了实验会同时unmap所有的映射。
hack了一个测试程序,2 threads,2个ctx 然后释放其中一个ctx,put_queue, 另一个也被调用了,然后core dump. https://github.com/gaozhangfei/uadk/tree/hack-10.17
方案2 想办法put_queue只unmap该fd的mapping
2.1. 目前还不知道怎么实现,没找到类似例子,估计比较hack,因为remove_vma不对外开放 2.2 假设能实现,应用程序addr = mmap, 调用ioctl(put_q), 继续访问 addr, 挂了,这个能否被接受 今天vpn连不上,没做实验,不知道啥错误,估计类似segmentation fault 之类 我理解这个不能被接受,use after free, 也得驱动自己去检查避免,用户程序没有unmap, 继续访问addr, 不能算错吧。 并行性也可能有问题,比如多线程一个在操作addr,另一个来着么一下,也没法上锁。 kernel不开放remove_vma 肯定是有原因的。
方案3 修改ioctl uacce_put_queue, 把uacce->ops->put_queue(q)挪到release 这个方案比较简单,也没啥风险。 问题,1. 和 UACCE_CMD_PUT_Q语义不匹配,变成stop_q, 2 如何兼容 觉得只要解释清楚,语义不匹配也能接受吧, 考虑兼容,可以再加一个UACCE_CMD_STOP_Q,两个行为一致。
put_q 本身也只是一个优化
/* * UACCE_CMD_PUT_Q: * User actively stop queue and free queue resource immediately * Optimization method since close fd may delay */ #define UACCE_CMD_PUT_Q _IO('W', 1)
大家什么建议
也见过其他修改ioctl 的例子 1, https://lore.kernel.org/all/1-v3-e2e16cd7467f+2a6a1-smmuv3_nesting_jgg@nvidi... jason 之前修改过一个ioctl --- a/include/uapi/linux/vfio.h +++ b/include/uapi/linux/vfio.h -#define VFIO_TYPE1_NESTING_IOMMU 6 /* Implies v2 */ +#define __VFIO_RESERVED_TYPE1_NESTING_IOMMU 6 /* Implies v2 */
git log --oneline include/uapi/linux
2. commit 636119af94f2fbf3e4458be66a1bc740ba69ce6d Author: Jens Axboe <axboe@kernel.dk> Date: Sat Sep 14 08:51:15 2024 -0600 io_uring: rename "copy buffers" to "clone buffers"
Re: Fwd: uacce ioctl UACCE_CMD_PUT_Q 问题: 应用先调用mmap获取地址(p1),再调用remap获取地址p2,接下来调用munmap解除p1,然后对p2区域做读写操作 mremap后,munmap原地址会成功(导致释放vma),然后使用新remap后地址读写成功 复现方法: #define _GNU_SOURCE /* See feature_test_macros(7) */ #include <stdio.h> #include <string.h> #include <stdint.h> #include <unistd.h> #include <inttypes.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/mman.h> static const char *char_dev = "/dev/hisi_sec2-0"; static const uint32_t size = 151552; static const uint32_t sleep_time = 10; #define RETURN_IF_CHECK_FAIL(exp) do { \ if (exp) { \ printf("exit due to check failed, %u %s\n", __LINE__, #exp); \ return; \ } \ } while(0) static void do_memset(void *addr, uint32_t size, const char *place) { printf("memset for place: %s\n", place); fflush(stdout); memset(addr, 0x11, size); } void main() { uint32_t remap_size = size / 2; int fd = open(char_dev, O_RDWR | O_CLOEXEC); RETURN_IF_CHECK_FAIL(fd < 0); void *mem = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 1 * getpagesize()); RETURN_IF_CHECK_FAIL(mem == (void *)-1); printf("mmap ret address = %p\n", mem); do_memset(mem, size, "after mmap"); void *remap = mremap(mem, size, remap_size, MREMAP_MAYMOVE | MREMAP_FIXED, 0x400000000); RETURN_IF_CHECK_FAIL(remap == (void *)-1); printf("mremap ret address = %p\n", remap); do_memset(remap, remap_size, "after remap"); // 实测访问再访问mem会出现段错误 printf("start munmap old!\n"); munmap(mem, size); sleep(sleep_time); printf("start munmap new!\n"); do_memset(remap, remap_size, "before munmap remap"); munmap(remap, size); sleep(sleep_time); printf("exit!\n"); } 此外在uacce加入打印以跟踪qfr: --- a/drivers/misc/uacce/uacce.c +++ b/drivers/misc/uacce/uacce.c @@ -189,6 +189,8 @@ static int uacce_fops_open(struct inode *inode, struct file *filep) list_add(&q->list, &uacce->queues); mutex_unlock(&uacce->mutex); + printk("uacce_fops_open: q=%p\n", q); + return 0; out_with_bond: @@ -204,6 +206,8 @@ static int uacce_fops_release(struct inode *inode, struct file *filep) struct uacce_queue *q = filep->private_data; struct uacce_device *uacce = q->uacce; + printk("uacce_fops_release: q=%p\n", q); + mutex_lock(&uacce->mutex); uacce_put_queue(q); uacce_unbind_queue(q); @@ -221,6 +225,9 @@ static void uacce_vma_close(struct vm_area_struct *vma) if (vma->vm_pgoff < UACCE_MAX_REGION) { struct uacce_qfile_region *qfr = q->qfrs[vma->vm_pgoff]; + printk("uacce_vma_close: q=%p vm_pgoff=%u\n", q, vma->vm_pgoff); + printk("uacce_vma_close: qfr=%p\n", qfr); + mutex_lock(&q->mutex); q->qfrs[vma->vm_pgoff] = NULL; mutex_unlock(&q->mutex); @@ -246,6 +253,8 @@ static int uacce_fops_mmap(struct file *filep, struct vm_area_struct *vma) enum uacce_qfrt type = UACCE_MAX_REGION; int ret = 0; + printk("uacce_fops_mmap: q=%p vm_pgoff=%u\n", q, vma->vm_pgoff); + if (vma->vm_pgoff < UACCE_MAX_REGION) type = vma->vm_pgoff; else @@ -290,6 +299,7 @@ static int uacce_fops_mmap(struct file *filep, struct vm_area_struct *vma) } q->qfrs[type] = qfr; + printk("uacce_fops_mmap: type=%u qfr=%p\n", type, qfr); mutex_unlock(&q->mutex); return ret;