newprint
newprint

Reputation: 7136

Questions about copying data into/from Linux Kernel

I am finishing up project for OS class and can't figure several things that have to do with SAFELY copying data from user-space into kernel and back from kernel into user space, and how to properly discard this information.

Say I have several system calls:

//copies data into kernel space
long sys_into(void __user *data, long length);
// copies data into user space
long sys_from(void __user *data, long length);

In both cases long length is the number of bytes to be copied.

Things I was able to figure out so far:
1. Validate that pointers *data are not null.
2. Validate that length < 0
3. I need to use access_ok. However, I am not sure if I need to use it for both functions or only for the long sys_into()
3. When copying into kernel using kmalloc(length) to allocate number of bytes and make sure that I can actually allocate this memory.
4. Finally use copy_from_user & copy_to_user to copy the data.

So far, I found very little information. 1. Source code example from "Linux kernel programming" (as was pointed out, example in the Linux Kernel Development is dangerous). 2. http://www.quora.com/Linux-Kernel/How-does-copy_to_user-work

Thanks !!!

Upvotes: 2

Views: 1831

Answers (1)

tian_yufeng
tian_yufeng

Reputation: 1826

I think your considering is right, I provide some codes as following:

#define MAXIMUM_LENGTH 128
char kaddr[MAXIMUM_LENGTH];

int sys_into(void __user *uaddr, int len)
{
    if (len < 0 || len > MAXIMUM_LENGTH)
        return -EINVAL;
    if (len == 0)
        return 0;

    if (copy_from_user(kaddr, uaddr, len))
        return -EFAULT;

    /* handling */

    return len;
}

int sys_from(void __user *uaddr, int len)
{
    if (len > MAXIMUM_LENGTH)
        len = MAXIMUM_LENGTH;

    if (len < 0 || len > MAXIMUM_LENGTH)
        return -EINVAL;
    if (len) {
        if (copy_to_user(uaddr, kaddr, len))
            return -EFAULT;
}

    return len;
}

Other considering: (1)If the copy size might be really large and vary, you should considering using the get_user()/put_user() check, that means you have to change the sys_from() parameters table to int sys_from(void __user *uaddr, int __user *ulen), the codes will be changed to:

int sys_from(void __user *uaddr, int __user *ulen)
{
    int err;
    int len;

    err = get_user(len, ulen);
    if (err)
        return err;
    if (len > MAXIMUM_LENGTH)
        len = MAXIMUM_LENGTH;
    if (len < 0 || len > MAXIMUM_LENGTH)
        return -EINVAL;
    if (len) {
        if (copy_to_user(uaddr, kaddr, len))
            return -EFAULT;
}

    return __put_user(len, ulen);
}

(2) If possible, it's better not dynamically kmalloc/kfree buffers frequently. While it's better kmalloc one kernel buffer which is big enough during initialization.

Upvotes: 3

Related Questions