无法从linux内核获取copy_to_user

时间:2015-07-01 08:51:07

标签: linux linux-kernel kernel driver linux-device-driver

我们有一个内核驱动程序和一个与此驱动程序交互的用户空间应用程序。用户空间应用程序使用posix_memalign分配内存块,并将这些地址传递给内核驱动程序,如下所示:

    struct dma_cmd
    {
      int cmd;
      int usr_buf_size;
      char *buf;
    };

    struct set_mem_addr_cmd_struct
    {
      int channel_id;
      char *address;
    };

    char* register_mem_for_channel(ssize_t f, int channelid) {
            struct dma_cmd cmd;
            char *buf = (char *)malloc(sizeof(struct set_mem_addr_cmd_struct));
            cmd.usr_buf_size = sizeof(struct set_mem_addr_cmd_struct);
            cmd.cmd = CMD_SET_MEM_ADDR;

            (*(struct set_mem_addr_cmd_struct*)buf).channel_id = channelid;

            if ((channelid < 4))
                    char *addr;
                    posix_memalign(&addr, 4*1024, 4*1024*8);
                    if (addr == 0) {
                            printf("ERROR: Can not allocate aligned frame memory\n");
                            exit(-1);
                    }
                    (*(struct set_mem_addr_cmd_struct*)buf).address = addr;
            }
            cmd.buf = buf;
            write(f, &cmd, 0); // Send to driver
            char *addr = (*(struct set_mem_addr_cmd_struct*)buf).address;
            printf ("SET memory address for channel %d to %p\n", channelid, addr);
            return addr;
    }

内核驱动程序有以下内容:

#define MAX_CHANNELS 8
char *user_addr[MAX_CHANNELS];

...然后在内核中,我们从用户空间读取包含目标用户空间地址的消息:

...
...
    case CMD_SET_MEM_ADDR:
    {
        int cid;
        if (copy_from_user (&cmd_buf, kcmd.buf, sizeof (struct set_mem_addr_cmd_struct)))
        {
            printk(KERN_DEBUG "KERN: Set Memory Address FAULT\n");
            rc = -EFAULT;
            return rc;
        }
        cid = cmd_buf.channel_id;
        user_addr[cid] =  cmd_buf.address;
        printk(KERN_DEBUG "Set Channel %d Addr : %p\n", cmd_buf.channel_id, cmd_buf.address);
    }

...然后驱动程序产生一个内核线程,这个线程进入循环并尝试将样本数据复制到用户空间:

while (1) {
    msleep(333);
    for (i=0; i<64; i++) imagedata[i] = i;
    printk(KERN_DEBUG "copy to user buffer A at address %p", user_addr[0]);
    if (!access_ok(VERIFY_WRITE, user_addr[0], 64)) {
        printk(KERN_DEBUG "ERROR: Can not access userspace addr\n");
    }
    errcode = copy_to_user(user_addr[0], &imagedata[0], 64);
    if (errcode != 0) printk(KERN_DEBUG "ERROR: COPY TO USER A FAILED!!! (errcode:%ld)\n", errcode);
}

在上面的代码中,access_ok总是返回true,所以我们不会得到访问错误。但是,copy_to_user调用始终返回64,表示没有字节被复制到用户空间。

在用户空间中,我们有打印出字节的代码,我们可以确认没有任何内容写入目标。以下是来自内核和用户端的日志:

Kernel:
[ 9594.668322] Set Channel 0 Addr : 00007f460d68b000
[ 9594.719297] copy to user buffer A at address 00007f460d68b000
[ 9594.719341] COPY TO USER A FAILED!!! (errcode:64)
User:
USER: SET memory address for channel 0 to 0x7f460d68b000
USER: SIGNAL RECEIVED: value 1
USER: Callback for data received
USER: FIRST 10 BYTES for addr 0x7f460d68b000: 0 0 0 0 0 0 0 0 0 0 

有没有办法找出copy_to_user调用无效的原因?

2 个答案:

答案 0 :(得分:4)

您的衍生内核线程无法访问用户空间内存。

虽然内核的内存在所有进程(线程)之间共享,但切换到内核代码,用户空间内存是按进程,并且只能从进程访问,拥有此内存。换句话说,即使进程执行内核代码,地址空间概念也适用。

注意,access_ok检查是粗略的,它只是告诉内存不是内核的。例如,参见question

如果要在内核空间和用户进程之间共享内存区域,可以在debugfs中创建(在驱动程序中)文件或作为字符设备,并实现其mmap功能。

答案 1 :(得分:1)

基本上这不起作用。因为许多上下文都会被切换,所以在你创建copy_to_user之前......所以你无法猜测你将复制哪个进程地址空间。