15

I have always been told(In books and tutorials) that while copying data from kernel space to user space, we should use copy_to_user() and using memcpy() would cause problems to the system. Recently by mistake i have used memcpy() and it worked perfectly fine with out any problems. Why is that we should use copy_to_user instead of memcpy()

My test code(Kernel module) is something like this:

static ssize_t test_read(struct file *file, char __user * buf,
             size_t len, loff_t * offset)
{
    char ani[100];

    if (!*offset) {
        memset(ani, 'A', 100);
        if (memcpy(buf, ani, 100))
            return -EFAULT;
        *offset = 100;
        return *offset;
    }

    return 0;
}

struct file_operations test_fops = {
    .owner = THIS_MODULE,
    .read = test_read,
};

static int __init my_module_init(void)
{
    struct proc_dir_entry *entry;

    printk("We are testing now!!\n");
    entry = create_proc_entry("test", S_IFREG | S_IRUGO, NULL);
    if (!entry)
        printk("Failed to creats proc entry test\n");

    entry->proc_fops = &test_fops;
    return 0;
}
module_init(my_module_init);

From user-space app, i am reading my /proc entry and everything works fine.

A look at source code of copy_to_user() says that it is also simple memcpy() where we are just trying to check if the pointer is valid or not with access_ok and doing memcpy.

So my understanding currently is that, if we are sure about the pointer we are passing, memcpy() can always be used in place of copy_to_user.

Please correct me if my understanding is incorrect and also, any example where copy_to_user works and memcpy() fails would be very useful. Thanks.

Sandeep
  • 18,356
  • 16
  • 68
  • 108
  • It's because of paging. – Linuxios Feb 20 '13 at 01:22
  • @Linuxios Sorry But can you pls explain a littl more. I am not able to justify as the kernel is able to copy perfectly fine also i am not able to see anything related to paging in the source code of copy_to_user. Could you kindly elaborate? – Sandeep Feb 20 '13 at 01:25
  • 1
    @Sandy: Hypothetical question: You are using a 32-bit system with 16 GB of RAM. Will memcpy work? – Zan Lynx Feb 20 '13 at 02:31
  • Don't use memcpy as copy_to_user! It's buggy. – Ilya Matveychikov Feb 20 '13 at 14:42
  • 1
    possible duplicate of [Why do you have to use copy\_to\_user()/copy\_from\_user() to access user space from the kernel?](http://stackoverflow.com/questions/12666493/why-do-you-have-to-use-copy-to-user-copy-from-user-to-access-user-space-from) – Zan Lynx Feb 21 '13 at 20:10

2 Answers2

33

There are a couple of reasons for this.

First, security. Because the kernel can write to any address it wants, if you just use a user-space address you got and use memcpy, an attacker could write to another process's pages, which is a huge security problem. copy_to_user checks that the target page is writable by the current process.

There are also some architecture considerations. On x86, for example, the target pages must be pinned in memory. On some architectures, you might need special instructions. And so on. The Linux kernels goal of being very portable requires this kind of abstraction.

Linuxios
  • 34,849
  • 13
  • 91
  • 116
  • 8
    +1 For security. Not just another process. With a 3G/1G memory split a `user` process can try to over-write kernel memory. This might be especially useful if your data is `code` to modify the kernel. Many `CPU`s have user and supervisor modes. Even in MMU-less, `memcpy()` is bad. – artless noise Feb 21 '13 at 00:13
1

This answer may be late but anyway copy_to_user() and it's sister copy_from_user() both do some size limits checks about user passed size parameter and buffer sizes so a read method of:

char name[] = "This message is from kernel space";
ssize_t read(struct file *f, char __user *to, size_t size, loff_t *loff){
                
    int ret = copy_to_user(to, name, size);
    if(ret){
        pr_info("[+] Error while copying data to user space");
        return ret;
    }
    pr_info("[+] Finished copying data to user space");
    return 0;
}

and a user space app read as read(ret, buffer, 10); is OK but replace 10 with 35 or more and kernel will emit this error:

Buffer overflow detected (34 < 35)!

and cause the copy to fail to prevent memory leaks. Same goes for copy_from_user() which will also make some kernel buffer size checks.


That's why you have to use char name[] and not char *name since using pointer(not array) makes determining size not possible which will make kernel emit this error:

BUG: unable to handle page fault for address: ffffffffc106f280
#PF: supervisor write access in kernel mode
#PF: error_code(0x0003) - permissions violation

Hope this answer is helpful somehow.

KMG
  • 1,433
  • 1
  • 8
  • 19