ограничение размера параметра ioctl - PullRequest
2 голосов
/ 30 апреля 2019

Я пишу модуль ядра, который включает операции ioctl на символьных устройствах. Мой драйвер принимает вызовы ioctl, и я хочу передать некоторые данные из пространства ядра в пространство пользователя. У меня проблемы с передачей больших структур данных. Мне интересно, есть ли способ решить эту проблему и как-то обойти это ограничение.

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include "ioctl_def.h"

typedef struct
{
    char data[9999];
} DATA;

#define MAGIC 'v'
#define IOCTL_GET_DATA _IOR(MAGIC, 1, DATA)

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Noob");
MODULE_DESCRIPTION("Test module");
MODULE_VERSION("0.01");

#define MAJOR 400
#define NAME  "vdev"

static void __exit modexit(void)
{
    printk(KERN_INFO "Modexit\n");
    unregister_chrdev(MAJOR, NAME);
}

static int dev_open( struct inode *n, struct file *f )
{
    printk(KERN_INFO "Devopen\n");
    try_module_get(THIS_MODULE);
    return 0;
}

static int dev_release( struct inode *n, struct file *f )
{ 
    printk(KERN_INFO "Devrelease\n");
    module_put(THIS_MODULE);
    return 0;
}

static long dev_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
{ 
    printk("dev_ioctl cmd: %d\n", cmd);
    if((_IOC_TYPE(cmd) != MAGIC))
    {
        return -ENOTTY; 
    }

    switch(cmd)
    {
        case IOCTL_GET_DATA:
        {
            printk("IOCTL_GET_DATA: %d\n", cmd);
            DATA d;
            memset(&d, 0, sizeof(DATA));
            snprintf(d.data, sizeof(d.data), "%s", "Hello");            
            copy_to_user((void*)arg, &d, _IOC_SIZE(cmd));
        }
        break;
    }

    return 0;
}


static ssize_t dev_write(struct file *file, const char __user *buf, size_t count, loff_t *offset)
{
    printk("input length: %d\n", count);
    char* data = kmalloc(count, GFP_KERNEL);
    memset(data, 0, count);

    if (copy_from_user(data + *offset, buf, (unsigned long)count))
    {
        printk("read error!\n");
    }
    else
    {
        if (data[count - 1] == 10) // ascii line feed dec = 10
        {
            data[count - 1] = 0;
        }
        printk("%s", data);
    }
    kfree(data);
    return count;
}

static const struct file_operations fops = { 
   .owner = THIS_MODULE, 
   .open = dev_open,
   .write = dev_write,
   .release = dev_release, 
 //  .read  = dev_read, 
   .unlocked_ioctl = dev_ioctl
};

static int __init modinit(void)
{
    printk(KERN_INFO "Modinit\n");
    unregister_chrdev(MAJOR, NAME);

    int ret = register_chrdev(MAJOR, NAME, &fops);
    if( ret < 0 )
    { 
        printk(KERN_ERR "Could not register chardev\n" ); 
    }

    printk(KERN_ERR "Device initialized\n" );

    return ret;
}

module_init(modinit);
module_exit(modexit);

Текущий код компилируется, но размер данных недостаточен для моего приложения в пользовательском пространстве. Однако всякий раз, когда я увеличиваю массив для хранения большего количества данных, например

typedef struct
{
    char data[99999];
} DATA;

Код не скомпилируется, и возникает следующая ошибка:

vdev.c:54:3: error: case label does not reduce to an integer constant
   case IOCTL_GET_DATA:
   ^~~~

1 Ответ

1 голос
/ 01 мая 2019

Параметр «размер» для кода ioctl равен ограничен с (16K -1).Вы пытаетесь передать 'size', равный 99999, что превышает этот предел.

Заголовочный файл включает / uapi / asm-generic / ioctl.h и содержит следующий комментарий, объясняющий происхождение такогоlimit:

/* ioctl command encoding: 32 bits total, command in lower 16 bits,
 * size of the parameter structure in the lower 14 bits of the
 * upper 16 bits.
 * Encoding the size of the parameter structure in the ioctl request
 * is useful for catching programs compiled with old versions
 * and to avoid overwriting user space outside the user buffer area.
 * The highest 2 bits are reserved for indicating the ``access mode''.
 * NOTE: This limits the max parameter size to 16kB -1 !
 */

Существует множество «стандартных» способов передачи данных из ядра пользователю.Например, он может быть реализован как .read метод символьного устройства, которое естественным образом обрабатывает данные любой длины (включая переменную длину).

Необходимо прибегать к ioctl только для «нестандартных» случаев., когда другие методы не подходят естественным образом или уже используются для других целей.

...