copy_to_user vs memcpy
Asked Answered
J

2

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.

Jiggle answered 20/2, 2013 at 1:20 Comment(5)
It's because of paging.Chromous
@Chromous 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?Jiggle
@Sandy: Hypothetical question: You are using a 32-bit system with 16 GB of RAM. Will memcpy work?Yurikoyursa
Don't use memcpy as copy_to_user! It's buggy.Ogbomosho
possible duplicate of Why do you have to use copy_to_user()/copy_from_user() to access user space from the kernel?Yurikoyursa
C
34

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.

Chromous answered 20/2, 2013 at 1:31 Comment(1)
+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 CPUs have user and supervisor modes. Even in MMU-less, memcpy() is bad.Soot
Z
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.

Zippel answered 30/6, 2021 at 13:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.