在不使用 copy_from_user() 的情况下成功取消引用内核 space 中的 userspace 指针
Successfully de-referenced userspace pointer in kernel space without using copy_from_user()
我们公司代码库中的一个驱动程序存在多年的错误。
基本上我们通过 ioctl
s 调用驱动程序。用户space和驱动程序space之间传递的数据存储在struct
中,指向数据的指针被送入ioctl
。驱动程序负责使用 copy_from_user()
取消引用指针。但是这段代码多年来一直没有这样做,而只是取消对 userspace 指针的引用。到目前为止(据我所知)直到现在它还没有引起任何问题。
我想知道这段代码怎么这么长时间都没有引起任何问题?在什么情况下直接从用户 space 中取消引用内核 space 中的指针不会导致问题?
在用户中space
struct InfoToDriver_t data;
data.cmd = DRV_SET_THE_CLOCK;
data.speed = 1000;
ioctl(driverFd, DEVICE_XX_DRIVER_MODIFY, &data);
在驱动中
device_xx_driver_ioctl_handler (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
struct InfoToDriver_t *user_data;
switch(cmd)
{
case DEVICE_XX_DRIVER_MODIFY:
// what we've been doing for years, BAD
// But somehow never caused a kernel oops until now
user_data = (InfoToDriver_t *)arg;
if (user_data->cmd == DRV_SET_THE_CLOCK)
{ .... }
// what we're supposed to do
copy_from_user(user_data, (void *)arg, sizeof(InfoToDriver_t));
if (user_data->cmd == DRV_SET_THE_CLOCK)
{ ... }
一个可能的答案是,这取决于架构。如您所见,在健全的体系结构(例如 x86 或 x86-64)上,简单地取消引用 __user 指针就可以了。但是 Linux 假装支持所有可能的体系结构,有些体系结构中简单的取消引用不起作用。否则 copy_to/from_user 将不存在。
copy_to/from_user 的另一个原因是用户模式端可能会与内核端(在另一个线程中)同时修改其内存。您不能假设用户模式内存的内容在从内核访问时被冻结。恶意用户模式代码可以使用它来攻击内核。例如,你可以在执行工作之前探测输出数据的指针,但是当你将结果复制回用户模式时,这个指针已经失效了。哎呀。 copy_to_user API 确保(应该确保)内核不会在复制过程中崩溃,而是杀死有罪的应用程序。
更安全的方法是将整个用户模式数据结构复制到内核中(又名'capture'),检查此副本的一致性。
底线...如果此驱动程序被证明在特定体系结构上运行良好,并且没有移植它的计划,则没有紧迫性来更改它。但仔细检查内核代码的健壮性,如果需要捕获用户模式数据,或者在从用户模式复制过程中可能会出现问题。
我们公司代码库中的一个驱动程序存在多年的错误。
基本上我们通过 ioctl
s 调用驱动程序。用户space和驱动程序space之间传递的数据存储在struct
中,指向数据的指针被送入ioctl
。驱动程序负责使用 copy_from_user()
取消引用指针。但是这段代码多年来一直没有这样做,而只是取消对 userspace 指针的引用。到目前为止(据我所知)直到现在它还没有引起任何问题。
我想知道这段代码怎么这么长时间都没有引起任何问题?在什么情况下直接从用户 space 中取消引用内核 space 中的指针不会导致问题?
在用户中space
struct InfoToDriver_t data;
data.cmd = DRV_SET_THE_CLOCK;
data.speed = 1000;
ioctl(driverFd, DEVICE_XX_DRIVER_MODIFY, &data);
在驱动中
device_xx_driver_ioctl_handler (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
struct InfoToDriver_t *user_data;
switch(cmd)
{
case DEVICE_XX_DRIVER_MODIFY:
// what we've been doing for years, BAD
// But somehow never caused a kernel oops until now
user_data = (InfoToDriver_t *)arg;
if (user_data->cmd == DRV_SET_THE_CLOCK)
{ .... }
// what we're supposed to do
copy_from_user(user_data, (void *)arg, sizeof(InfoToDriver_t));
if (user_data->cmd == DRV_SET_THE_CLOCK)
{ ... }
一个可能的答案是,这取决于架构。如您所见,在健全的体系结构(例如 x86 或 x86-64)上,简单地取消引用 __user 指针就可以了。但是 Linux 假装支持所有可能的体系结构,有些体系结构中简单的取消引用不起作用。否则 copy_to/from_user 将不存在。
copy_to/from_user 的另一个原因是用户模式端可能会与内核端(在另一个线程中)同时修改其内存。您不能假设用户模式内存的内容在从内核访问时被冻结。恶意用户模式代码可以使用它来攻击内核。例如,你可以在执行工作之前探测输出数据的指针,但是当你将结果复制回用户模式时,这个指针已经失效了。哎呀。 copy_to_user API 确保(应该确保)内核不会在复制过程中崩溃,而是杀死有罪的应用程序。
更安全的方法是将整个用户模式数据结构复制到内核中(又名'capture'),检查此副本的一致性。
底线...如果此驱动程序被证明在特定体系结构上运行良好,并且没有移植它的计划,则没有紧迫性来更改它。但仔细检查内核代码的健壮性,如果需要捕获用户模式数据,或者在从用户模式复制过程中可能会出现问题。