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 的内存,从而导致恐慌。
我目前正在使用内核模块进行实验。
我编写了一个函数,它接受一个指向结构(在用户 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 的内存,从而导致恐慌。