copy_to_user против memcpy

Мне всегда говорили (в книгах и учебниках), что при копировании данных из пространства ядра в пространство пользователя мы должны использовать copy_to_user(), а использование memcpy() вызовет проблемы в системе. Недавно по ошибке я использовал memcpy(), и он отлично работал без каких-либо проблем. Почему мы должны использовать copy_to_user вместо memcpy()

Мой тестовый код (модуль ядра) выглядит примерно так:

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);

Из приложения пользовательского пространства я читаю свою запись /proc, и все работает нормально.

Взгляд на исходный код copy_to_user() показывает, что это также простая функция memcpy(), где мы просто пытаемся проверить, действителен ли указатель с помощью access_ok и выполнения memcpy.

Итак, насколько я понимаю в настоящее время, если мы уверены в указателе, который мы передаем, всегда можно использовать memcpy() вместо copy_to_user.

Пожалуйста, поправьте меня, если я ошибаюсь, а также любой пример, где работает copy_to_user, а memcpy() не работает. Спасибо.


person mk..    schedule 20.02.2013    source источник
comment
Это из-за пейджинга.   -  person Linuxios    schedule 20.02.2013
comment
@Linuxios Извините, но не могли бы вы объяснить немного больше. Я не могу обосновать, поскольку ядро ​​​​способно отлично копировать, и я не вижу ничего, связанного с пейджингом в исходном коде copy_to_user. Не могли бы вы уточнить?   -  person mk..    schedule 20.02.2013
comment
@Sandy: Гипотетический вопрос: вы используете 32-битную систему с 16 ГБ ОЗУ. Будет ли работать memcpy?   -  person Zan Lynx    schedule 20.02.2013
comment
Не используйте memcpy как copy_to_user! Это глючит.   -  person Ilya Matveychikov    schedule 20.02.2013


Ответы (2)


На это есть несколько причин.

Во-первых, безопасность. Поскольку ядро ​​может писать по любому адресу, который он хочет, если вы просто используете адрес пользовательского пространства, который вы получили, и используете memcpy, злоумышленник может записать на страницы другого процесса, что является огромной проблемой безопасности. copy_to_user проверяет, что целевая страница доступна для записи текущему процессу.

Есть также некоторые соображения по архитектуре. Например, на x86 целевые страницы должны быть закреплены в памяти. Для некоторых архитектур могут потребоваться специальные инструкции. И так далее. Цель ядра Linux, заключающаяся в том, чтобы быть очень переносимой, требует такого рода абстракции.

person Linuxios    schedule 20.02.2013
comment
+1 За безопасность. Не просто еще один процесс. При разделении памяти 3G/1G процесс user может попытаться перезаписать память ядра. Это может быть особенно полезно, если ваши данные code нужны для изменения ядра. Многие CPU имеют режимы пользователя и супервизора. Даже в MMU-less memcpy() плохо. - person artless noise; 21.02.2013
comment
@BillPringlemeir: Точно. Спасибо за +1. - person Linuxios; 21.02.2013

Этот ответ может быть поздним, но в любом случае copy_to_user() и его сестра copy_from_user() оба выполняют некоторые проверки ограничений размера в отношении переданного пользователем параметра size и размеров буфера, поэтому метод чтения:

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;
}

и приложение пользовательского пространства, читаемое как read(ret, buffer, 10);, в порядке, но замените 10 на 35 или больше, и ядро ​​​​выдаст эту ошибку:

Buffer overflow detected (34 < 35)!

и привести к сбою копии, чтобы предотвратить утечку памяти. То же самое касается copy_from_user(), который также выполняет некоторые проверки размера буфера ядра.


Вот почему вы должны использовать char name[], а не char *name, поскольку использование указателя (не массива) делает невозможным определение размера, из-за чего ядро ​​выдает эту ошибку:

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

Надеюсь, этот ответ хоть как-то поможет.

person Khaled    schedule 30.06.2021