Reputation: 7136
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
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