Копировать структуру с включенными пользовательскими указателями из пространства пользователя в пространство ядра (copy_from_user) - PullRequest
0 голосов
/ 08 апреля 2019

Я хочу передать структуру транзакции, которая содержит указатель пространства пользователя на массив, в ядро ​​с помощью copy_from_user. Цель состоит в том, чтобы получить доступ к элементам массива в пространстве ядра.

Сторона пространства пользователя: Я размещаю массив структур _sg_param в пространстве пользователя. Теперь я поместил адрес этого массива в структуру транзакции (строка (*)). Затем я передаю структуру транзакций ядру через ioctl ().

Сторона пространства ядра: При выполнении этого ioctl полная структура транзакции копируется в пространство ядра (строка ()). Теперь пространство ядра выделено для хранения массива (строка (*)). Затем я пытаюсь скопировать массив из пользовательского пространства в новое выделенное пространство ядра (строка (****)), и здесь начинаются мои проблемы: Ядро повреждено во время выполнения этой копии. dmesg показывает следующий вывод:

[   54.443106] Unhandled fault: page domain fault (0x01b) at 0xb6f09738
[   54.448067] pgd = ee5ec000
[   54.449465] [b6f09738] *pgd=2e9d7831, *pte=2d56875f, *ppte=2d568c7f
[   54.454411] Internal error: : 1b [#1] PREEMPT SMP ARM

Есть идеи ???

После упрощенного извлечения моего кода:

// structure declaration

typedef struct _sg_param {
    void *seg_buf;
    int seg_len;
    int received;
} sg_param_t;

struct transaction {
    ...
    int num_of_elements;
    sg_param_t *pbuf_list;    // Array of sg_param structure
    ...
} trans;


// user space side:

    if ((pParam = (sg_param_t *) malloc(NR_OF_STRUCTS * sizeof(sg_param_t))) == NULL) {
        return -ENOMEM;
    }
    else {
        trans.num_of_elements = NR_OF_STRUCTS;
        trans.pbuf_list = pParam;    // (*)
    }

    rc = ioctl(dev->fd, MY_CMD, &trans);
    if (rc < 0) {
        return rc;
    }


// kernel space side

static long ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    arg_ptr = (void __user *)arg;

    // Perform the specified command
    switch (cmd) {
        case MY_CMD:
        {
            struct transaction *__user user_trans;
            user_trans = (struct transaction *__user)arg_ptr;

           if (copy_from_user(&trans, arg_ptr, sizeof(trans)) != 0) { // (**)
                k_err("Unable to copy transfer info from userspace for "
                     "AXIDMA_DMA_START_DMA.\n");       
                return -EFAULT;
            }
            int size = trans.num_of_elements * sizeof(sg_param_t);

            if (trans.pbuf_list != NULL) {
                // Allocate kernel memory for buf_list
                trans.pbuf_list = (sg_param_t *) kmalloc(size, GFP_KERNEL); // (***)
                if (trans.pbuf_list == NULL) {
                    k_err("Unable to allocate array for buffers.\n");
                    return -ENOMEM;
                }
                // Now copy pbuf_list from user space to kernel space
                if (copy_from_user(trans.pbuf_list, user_trans->pbuf_list, size) != 0) { // (****)
                    kfree(trans.pbuf_list);
                    return -EFAULT;
                }
            }
            break;
        }
    }

1 Ответ

0 голосов
/ 08 апреля 2019

Вы напрямую получаете доступ к данным пользовательского пространства (user_trans->pbuf_list).Вы должны использовать тот, который вы уже скопировали в ядро ​​(trans.pbuf_list).

Код для этого обычно будет выглядеть примерно так:

sg_param_t *local_copy = kmalloc(size, ...);
// TODO check it succeeded
if (copy_from_user(local_copy, trans.pbuf_list, size) ...)
trans.pbuf_list = local_copy;
// use trans.pbuf_list

Обратите внимание, что вам также необходимо проверитьtrans.num_of_elements будет действительным (0 приведет к тому, что kmalloc вернет ZERO_SIZE_PTR, а слишком большое значение может быть способом для DoS).

...