我们公司的代码库中的驱动程序中存在一个错误,该错误已存在多年。
基本上我们通过ioctl
来调用驱动程序。在用户空间和驱动程序空间之间传递的数据存储在struct
中,指向数据的指针被送入ioctl
。驱动程序负责使用copy_from_user()
取消引用指针。但是这段代码多年来一直没有这样做,只是取消引用用户空间指针。到目前为止(我知道)它到目前为止还没有引起任何问题。
我想知道这段代码如何长期没有引起任何问题?在什么情况下直接从用户空间解除引用内核空间中的指针不会导致问题?
在用户空间
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)
{ ... }
答案 0 :(得分:2)
一个可能的答案是,这取决于架构。正如您所看到的,在一个理智的体系结构(例如x86或x86-64)上,只需解除引用__user指针即可。但Linux假装支持所有可能的架构,有一些架构,其中简单的解除引用不起作用。否则copy_to / from_user将不复存在。
copy_to / from_user的另一个原因是usermode端可能与内核端(在另一个线程中)同时修改其内存。您无法假设从内核访问用户模式内存时内容被冻结。 Rougue用户模式代码可以用它来攻击内核。例如,您可以在执行工作之前探测指向输出数据的指针,但是当您将结果复制回usermode时,此指针已经无效。哎呀。 copy_to_user API确保(应该确保)内核在复制期间不会崩溃,而有罪的应用程序将被终止。
更安全的方法是将整个用户模式数据结构复制到内核中(也就是'捕获'),检查此副本是否一致。
底线......如果证明这个驱动程序在某些架构上运行良好,并且没有计划移植它,那么就没有迫切需要改变它。但是,如果需要捕获用户模式数据,请仔细检查内核代码的健壮性,否则在从用户模式复制时可能会出现问题。