Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

copy_to_user a struct that contains an array (pointer)

Disclosure: I'm fairly new to C. If you could explain any answers verbosely, I would appreciate it.

I am writing a linux kernel module, and in one of the functions I am writing I need to copy a structure to userspace that looks like this:

typedef struct
{
    uint32_t  someProperty;
    uint32_t  numOfFruits;
    uint32_t  *arrayOfFruits;
} ObjectCapabilities;

The API I'm implementing has documentation that describes the arrayOfFruits member as "an array of size numOfFruits where each element is a FRUIT_TYPE constant." I am confused how to do this, given that the arrayOfFruits is a pointer. When I copy_to_user the ObjectCapabilities structure, it will only copy the pointer arrayOfFruits to userspace.

How can userspace continuously access the elements of the array? Here is my attempt:

ObjectCapabilities caps;
caps.someProperty = 1024;
caps.numOfFruits  = 3;
uint32_t localArray[] = {
        FRUIT_TYPE_APPLE,
        FRUIT_TYPE_ORANGE,
        FRUIT_TYPE_BANANA
};
caps.arrayOfFruits = localArray;

And then for the copy... can I just do this?

copy_to_user((void *)destination, &caps, (sizeof(caps) + (sizeof(localArray) / sizeof((localArray)[0]))));
like image 258
boltup_im_coding Avatar asked Aug 29 '13 20:08

boltup_im_coding


1 Answers

The user needs to provide enough space for all the data being copied out. Ideally he'll tell you how much space he provided, and you check that everything fits.

The copied-out data should (in general) not include any pointers, since they're "local" to a different "process" (the kernel can be viewed as a separate process, as it were, and kernel / user interactions involve process-to-process IPC, similar to sending stuff over local or even Internet-connected sockets).

Since the kernel has pretty intimate knowledge of a process, you can skirt these rules somewhat, e.g., you could compute what the user's pointer will be, and copy out a copy of the original data, with the pointer modified appropriately. But that's kind of wasteful. Or, you can copy a kernel pointer and just not use it in the user code, but now you're "leaking data" that "bad guys" can sometimes leverage in various ways. In security-people-speak you've left a wide-open "covert channel".

In the end, then, the "right" way to do this tends to be something like this:

struct user_interface_version_of_struct {
    int property;
    int count;
    int data[]; /* of size "count" */
};

The user code mallocs (or otherwise arranges to have sufficient space) the "user interface version" and makes some system call to the kernel (read, receive, rcvmsg, ioctl, whatever, as long as it involves doing a "read"-type operation) and tells the kernel: "here's the memory holding the struct, and here's how big it is" (in bytes, or the maximum count value, or whatever: user and kernel simply need to agree on the protocol). The kernel-side code then verifies the user's values in some appropriate manner, and either does the copy-out however is most convenient, or returns an error.

"Most convenient" is sometimes two separate copy ops, or some put_user calls, e.g., if the kernel side has the data structure you showed, you might do:

/* let's say ulen is the user supplied length in bytes,
   and uaddr is the user-supplied address */
struct user_interface_version_of_struct *p;

needed = sizeof(*p) + 3 * sizeof(int);
if (needed > ulen)
    return -ENOMEM; /* user did not supply enough space */
p = uaddr;
error = put_user(1024, &p->property);
if (error == 0)
    error = put_user(3, &p->count);
if (error == 0 && copy_to_user(&p->data, localArray, 3 * sizeof(int))
    error = -EFAULT;

You may have a situation where you must conform to some not-very-nice interface, though.


Edit: if you're adding your own system call (rather than tying in to read or ioctl for instance), you can separate the header and data, as in Adam Rosenfield's answer.

like image 113
torek Avatar answered Sep 18 '22 15:09

torek