【问题标题】:copy_from_user()/__get_user() works fine within ioctl, but failed outside ioctlcopy_from_user()/__get_user() 在 ioctl 内工作正常,但在 ioctl 外失败
【发布时间】:2021-08-08 05:16:47
【问题描述】:

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

我写了一个函数,它接收一个指向结构(在用户空间中)的指针作为参数,目的是将该结构从用户空间复制到内核空间;因此,我需要copy_from_user__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中代码的sn-p:

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() cmd 从用户空间获得了 struct A - addr 的地址,这与这个问题无关。我已经检查过用户空间中结构 A 的地址是否正确(通过打印用户空间和内核空间中的地址),这让我很困惑 - 为什么有效的用户空间地址会导致 copy_from_user()__get_user()?

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

任何想法都将不胜感激。

【问题讨论】:

  • foo2 是从哪里调用的?您确定它来自同一进程发出的系统调用吗?
  • @NateEldredge: foo2 是从 ioctl 中的另一个 cmd 调用的,由同一进程发出。
  • @EthanL。你能说明foo2() 是如何被调用的吗?
  • @Khaled: foo2() 被 ioctl 调用,它启动了一个内核线程。在线程内部,它首先获取struct A 在用户空间的地址,然后尝试使用copy_from_user() 复制整个结构。我检查了传入内核的用户空间中struct A的地址是否正确。我知道我所做的可能没有任何意义,正如我所提到的,我只是在玩一个简单的模块并用它做一些小实验。这就是为什么我也想测试 kthread 接口,因为我以前从未尝试过创建内核线程。

标签: c linux-kernel linux-device-driver


【解决方案1】:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-09-25
    • 2012-11-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-03
    • 1970-01-01
    • 1970-01-01
    • 2015-02-03
    相关资源
    最近更新 更多