【发布时间】: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