copy_from_user()/__get_user() 在 ioctl 中工作正常,但在 ioctl 之外失败

copy_from_user()/__get_user() works fine within ioctl, but failed outside ioctl

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

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

结构体的定义很简单:

struct A {
    int a;
};

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

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() 通过 ioctl() 中的另一个 cmd 调用,由同一进程发出。我使用另一个 ioctl() 命令从用户 space 获得了结构 A - addr 的地址,这与这个问题无关。我已经检查过用户 space 中结构 A 的地址是否正确(通过打印用户 space 和内核 space 中的地址),这让我很困惑 - 为什么有效用户 space 地址会导致 copy_from_user()__get_user()?

中的错误

为什么 foo()ioctl() 中有效但在 foo2() 中无效?

如有任何想法,我们将不胜感激。

“启动内核线程。在线程内部”哎呀,你错了。您只能从发出系统调用或错误的线程调用复制 from/to 用户,或者以其他方式从用户 space 到达内核 space。在某种程度上,这是内核 space 中的同一用户 space 线程,因此调用有效。从一个新的内核线程,你不在那个线程上,也不再与那个特定的用户space进程相关联,所以它不知道。

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