2
votes

I have a struct as so:

typedef struct
{
    char* BUFFER;
    int Size;
}DataTransfer;

In my IOCTL function I attempt to populate the struct and pass to userspace:

case CHAR_DRIVER_IOCQREAD:
    printk(KERN_INFO "In CHAR_DRIVER_IOCQREAD");
    dataTransfer.BUFFER = kmalloc(strlen_user("Hello") +1, GFP_KERNEL);
    dataTransfer.Size = strlen_user("Hello") +1;
    error_count = copy_to_user((DataTransfer*) arg, &dataTransfer, sizeof(dataTransfer) );

In userspace I attempt to receive the struct as so:

DataTransfer dataTransfer;
if(ioctl(fd, CHAR_DRIVER_IOCQREAD, &dataTransfer) < 0)
{
    perror("ERROR in ioctl CHAR_DRIVER_IOCQREAD");
}
else
{
    printf("Kernel returned size %d \n", dataTransfer.Size);
    printf("Kernel returned string %s \n", dataTransfer.BUFFER);
}

What is the correct way of doing this?

1
zOMG, where did you get this style from?! - 0andriy
@0andriy I don't get you? - Robben_Ford_Fan_boy

1 Answers

4
votes

A couple of problems here. First, you are copying the structure to user space but not the string it points to. The structure will point to the kernel memory you allocated which user space can not access. Second you are allocating the kernel memory but not actually putting anything there.

One way of doing this would be for user space to allocate memory for the IOCtl to write the string into and then pass a structure like your DataTransfer to the IOCtl describing the memory it allocated. The kernel would read the structure from user memory using copy_from_user and then, if the buffer allocated was large enough to hold the string, write it there using copy_to_user on the address passed to it inside the structure.

E.g. (kernel side, sketch only):

case CHAR_DRIVER_IOCQREAD:
    DataTransfer dataTransfer;
    if (copy_from_user(&dataTransfer, arg, sizeof(dataTransfer)))
        return -EFAULT;
    if (dataTransfer.Size < strlen(myString) + 1)
        return -ENOMEM;
    if (copy_to_user(dataTransfer.BUFFER, myString, strlen(myString) + 1))
        return -EFAULT;