首页 > 解决方案 > copy_from_user()/__get_user() 在 ioctl 内工作正常,但在 ioctl 外失败

问题描述

我目前正在使用内核模块进行实验。

我写了一个函数,它接受一个指向结构(在用户空间中)的指针作为参数,目的是将该结构从用户空间复制到内核空间;因此,我需要copy_from_useror __get_user

结构的定义很简单:

struct A {
    int a;
};

我的内核模块中的函数旨在获取a的值,并返回它的值,如下(有两种方法):

static int foo(struct A __user *arg)
{
    int num, ret; 

    if (!access_ok(VERIFY_WRITE, arg, sizeof(struct A)))
        return -EFAULT;
    
    /* approach1: directly copy the value from user space */
    ret = __get_user(num, (int __user *)&arg->a);
    if (ret) return -ENOMEM;

    /* approach2: allocate space for struct A, then copy the whole struct */
    struct A *tmp = kmalloc(sizeof(struct A), GFP_KERNEL);
    if (!tmp) return -ENOMEM;

    ret = copy_from_user(tmp, (const void __user *)arg, sizeof(struct A));
    if (ret) return -EFAULT;

    num = tmp->a;
    kfree(tmp);
    
    return num;
}

无论我使用哪种方法,此功能都可以在ioctl. 以下是中的代码片段ioctl

long foo_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    struct A __user *tmp_struct;
    int ret;
    ...

    switch (cmd) {
    case IOC_FOO:
        ret = foo((struct A __user *)arg);
        break;
    ...
    }
    
    ...
    return ret;
}

但是,当我foo()进入另一个功能foo2()时,它会在__get_user()或上失败copy_from_user()。伪代码如下:

int foo2() 
{
    int val;
    ...
    struct A __user *addr = the address of struct A in user space
    val = foo(addr); /* this is where error occurrs */
    ...
}

请注意,代码是我的实验的更简单版本。foo2()通过另一个 cmd in 调用ioctl(),由同一进程发出。我addr使用另一个 ioctl() cmd 从用户空间获得了 struct A 的地址,这与这个问题无关。我已经检查过用户空间中 struct A 的地址是否正确(通过打印用户空间和内核空间中的地址),这让我很困惑——为什么有效的用户空间地址会导致copy_from_user()or中的错误__get_user()

为什么foo()工作在ioctl()但不工作foo2()

任何想法将不胜感激。

标签: clinux-kernellinux-device-driver

解决方案


“它启动了一个内核线程。在线程内部”哎呀,这是你的错误。您只能从发出系统调用或故障或以其他方式从用户空间进入内核空间的线程调用从/向用户复制。在某种程度上,这是内核空间中的同一个用户空间线程,因此调用工作。从一个新的内核线程中,您不在该线程上,并且不再与该特定用户空间进程相关联,因此它不知道。

你很幸运,它失败了。在某些情况下,它可能已与 init 相关联,并破坏了 init 的内存,从而导致恐慌。


推荐阅读