我正在kernel-4.9.168中开发一个linux内核模块,出于某种原因,我想调用另一个模块(vfio)的文件操作。 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()
之所以做set_fs(KERNEL_DS)
,正是因为它需要调用需要__user
地址的函数,同时实际传递kernel地址。确实如此:
old_fs = get_fs();
set_fs(get_ds());
ret = __vfs_write(file, (__force const char __user *)buf, count, pos);
set_fs(old_fs);
其中
__vfs_write
需要 __user
缓冲区,因为它将直接调用 file->f_ops->write()
。