SomethingBetter
SomethingBetter

Reputation: 1304

Can not get copy_to_user work from the linux kernel

We have a kernel driver and a user space application that interacts with this driver. The user space application allocates blocks of memory with posix_memalign, and passes these addresses to the kernel driver as follows:

    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;
    }

The kernel driver has the following decleration:

#define MAX_CHANNELS 8
char *user_addr[MAX_CHANNELS];

... then in the kernel, we read the message from the user space which contains the target user space address:

...
...
    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);
    }

... then the driver spawns a kernel thread and this thread gets in a loop and tries to copy sample data to user space:

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);
}

In the above code, access_ok always returns true, so we do not get the access error. However, the copy_to_user call always returns 64, indicating that no bytes were copied to user space.

In the user space, we have code that prints out the bytes and we can confirm that nothing is written into the target. Here are the logs from the kernel and user side:

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 

Is there a way to figure out why the copy_to_user call is not working?

Upvotes: 3

Views: 3237

Answers (2)

Tsyvarev
Tsyvarev

Reputation: 66118

Your spawned kernel thread simply has no access to user-space memory.

While kernel's memory is shared between all processes(threads), switched to the kernel code, user-space memory is per-process, and can be accessed only from the process, owned this memory. In other words, address space notion is applicable even when process executes kernel code.

Note, that access_ok check is rough, it just tells that memory is not a kernel's one. See, e.g., this question.

If you want to share memory region between kernel space and user process, you can create (in the driver) file in debugfs or as character device and implement its mmap functionality.

Upvotes: 4

Alex Hoppus
Alex Hoppus

Reputation: 3935

Basically that wouldn't work. Because of many context will be switched, before you will make copy_to_user ... so your can't guess at which process address space you will copy.

Upvotes: 1

Related Questions