You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

调用VFIO文件操作时,set_fs(KERNEL_DS)后用内核地址调用copy_from_user是否安全?

问题

我正在基于kernel-4.9.168开发Linux内核模块,因需求需要调用VFIO模块的文件操作(如write)。VFIO模块的相关实现大致如下:

static ssize_t do_io_rw(struct vfio_pci_device *vdev, bool test_mem,
            void __iomem *io, char __user *buf,
            loff_t off, size_t count, size_t x_start,
            size_t x_end, bool iswrite)
{
    ssize_t done = 0;
    int ret;

    while (count) {
        size_t fillable, filled;

        if (off < x_start)
            fillable = min(count, (size_t)(x_start - off));
        else if (off >= x_end)
            fillable = count;
        else
            fillable = 0;

        if (fillable >= 4 && !(off % 4)) {
            u32 val;

            if (iswrite) {
                if (copy_from_user(&val, buf, 4))
                    return -EFAULT;

                ret = vfio_pci_iowrite32(vdev, test_mem,
                             val, io + off);
                if (ret)
                    return ret;
            }
        }
        // 省略剩余代码
    }
}

我了解copy_from_user会通过比较addr_limit与buf参数来检查地址是否属于用户空间,因此在自己的模块中,我使用__kernel_write来绕过用户空间检查,代码如下:

static int my_module_code(struct file *file, struct vhost_virtqueue *vvq)
{
    loff_t pos = yyy;
    unsigned int *idx = &vvq->idx;

    return __kernel_write(file, (char *)idx, 2, &pos);
}

__kernel_write会在调用file->ops->write前设置set_fs(KERNEL_DS),目前代码运行正常,但我因copy_from_user名称带有"user"而感到不安。请问我的代码是否安全?是否有更合适的处理方式?

回答

代码安全性分析

你的代码在当前场景下是安全的:

  • __kernel_write的设计初衷就是在内核态向文件对象写入内核空间数据,它会临时将addr_limit设置为KERNEL_DS,让后续的copy_from_user(或同类用户空间检查函数)认可传入的内核地址合法性,执行完成后再恢复原有addr_limit。
  • 你传入的idx是内核空间指针(&vvq->idx属于内核态内存),本身不存在用户空间地址合法性问题,__kernel_write的逻辑完全匹配这种使用场景。

更合适的处理方式

如果想要让代码逻辑更清晰,或者避免依赖__kernel_write,可以考虑以下两种方案:

  1. 直接调用VFIO核心读写函数:如果VFIO提供了vfio_pci_write这类直接操作设备的内核接口,可以直接传入内核态数据指针。需要注意的是,这类函数可能未被导出(非EXPORT_SYMBOL),需要确认内核配置或修改VFIO代码导出所需函数。
  2. 封装内核态写入适配函数:如果必须通过文件操作接口,可以自己封装一个逻辑明确的写入函数,本质和__kernel_write一致,但命名更贴合场景:
static ssize_t kernel_vfio_write(struct file *file, const void *buf, size_t count, loff_t *pos)
{
    mm_segment_t old_fs = get_fs();
    ssize_t ret;

    set_fs(KERNEL_DS);
    // 强制转换为__user类型绕过编译检查,实际已通过set_fs允许内核地址
    ret = file->f_op->write(file, (const char __user *)buf, count, pos);
    set_fs(old_fs);

    return ret;
}

额外注意事项

  • 确保传入的file对象确实是VFIO设备的文件句柄,避免因文件类型错误触发异常。
  • 注意vvq->idx的访问同步:如果该变量可能被其他线程或中断修改,需要添加自旋锁等同步机制,避免数据竞争。

内容的提问来源于stack exchange,提问作者user24452356

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 22:13:22