1

我的内核驱动程序中有一个 ioctl 处理程序,它可以读写 ioctl。我还有一个用户空间应用程序,它使用这个 IOCTL 来写入/读取数据。

当我在我的用户空间应用程序中使用这个 ioctl 时,它会导致内核 OOPS"BUG: unable to handle kernel paging request at ffffffffffffffff"

用户空间片段:

typedef struct abc_T {
    uint8_t size;
    uint8_t addr;
    uint8_t data[64];
} abc_T;

// Read
int abc_read(uint8_t addr, uint8_t size, uint8_t * data) {
     abc_T abc = {};
     int fd;
     int retval = 0;
     abc.size = size;
     abc.addr = addr;
     fd = open("/dev/"ABC_DEV, O_RDWR);

     if (fd >=0) {
       retval = ioctl(fd, READ_ABC, &abc);
       if (retval == 0)
           memcpy(data, abc.data, size);
     }
     close(fd);
     return retval;
}

// Write
int abc_write(uint8_t addr, uint8_t size, uint8_t * data) {
     abc_T abc = {};
     int fd;
     int retval = 0;
     abc.size = size;
     abc.addr = addr;
     memcpy(abc.data, data, size);
     fd = open("/dev/"ABC_DEV, O_RDWR);

     if (fd >=0) {
       retval = ioctl(fd, WRITE_ABC, &abc);
     }
     close(fd);
     return retval;
}

核心:

static int ABC_ioctl(struct file * file, uint cmd, ulong arg)
{
ABC_T abc;
int retval;

if (copy_from_user(&abc, (void *)arg,
                        sizeof(ABC_T)) != 0) {
    return -EFAULT;
}

switch(cmd) {
    case READ_ABC:
    retval = read_func(&abc);
    if (retval == 0) {
        if (copy_to_user((void *)arg, &abc,
                     sizeof(ABC_T)) != 0) {
                retval = -EFAULT;
        } else {
            retval = 0;
        }
    }
    break;
case WRITE_ABC:
    ret_val = write_func(&abc);
    break;
return retval;
}
Error: 
BUG: unable to handle kernel paging request at ffffffffffffffff
IP: [<ffffffffffffffff>] 0xfffffffffffffffe
PGD 1e0f067 PUD 1e11067 PMD 0 
Oops: 0010 [#1] PREEMPT SMP 
...
Call Trace:
 [<ffffffff8117a8b8>] ? vfs_write+0x198/0x1b0
 [<ffffffff8118ccc0>] ? SyS_ioctl+0x80/0x90
 [<ffffffff8117b139>] ? SyS_write+0x49/0xa0
 [<ffffffff819af419>] ? system_call_fastpath+0x16/0x1b
Code:  Bad RIP value.
RIP  [<ffffffffffffffff>] 0xfffffffffffffffe
...

不知道这有什么问题。有人可以帮忙吗?

更新:abc_T 中的数组大小为 32,我没有看到问题。但是当我将数组大小更改为 64 时,我看到了问题。任何帮助,将不胜感激。

更新 2:abc_T 中数组大小为 64,如果 read/write size<= 32,则没有问题,只有当size要 read/write 大于 32 时,才会看到 crash。

4

2 回答 2

0

我不能确定这是你的问题,但你的代码有一些问题

int abc_read(uint8_t addr, uint8_t size, uint8_t * data) {
     abc_T abc = {};
     int fd, retval;
     abc.size = size;
     abc.addr = addr;
     fd = open("/dev/"ABC_DEV, O_RDWR);

     if (fd >=0) {
     retval = ioctl(fd, READ_ABC, &abc);
     if (retval == 0)
         memcpy(data, abc.data, size);

     }
return retval;
}

如果打开失败retval从未设置但您将其返回,则行为未指定,ABC_ioctl因为您使用了返回值

我们没有看到结束,如果打开失败abc_write,你会返回一个值吗?

PS我首先假设您即使打开失败也要执行memcpy,请注意缩进(我编辑了您的问题以更改它)


可以看看How to use ioctl to read and write to a chardev?

于 2019-03-05T14:00:38.827 回答
0

鉴于这些更新:

更新:abc_T 中的数组大小为 32,我没有看到问题。但是当我将数组大小更改为 64 时,我看到了问题。任何帮助,将不胜感激。

更新2:abc_T中的数组大小为64,如果读/写大小<= 32,没有问题,只有当要读/写的大小大于32时,才会看到crash。

您可能会超出为内核调用堆栈分配的空间。

像这样的代码

 abc_T abc = {};

在调用堆栈上分配一个abc_T对象,这在内核内部是极其有限的。您可能应该使用kmalloc()

abc_T *abc = kmalloc( sizeof( *abc ), GFP_KERNEL );

您需要检查结果,并释放使用分配的所有内存kfree()

kfree( abc );
于 2019-03-06T16:04:51.857 回答