ioctl参数大小限制

时间:2019-04-30 13:48:04

标签: c linux linux-kernel linux-device-driver

我正在编写一个内核模块,该模块涉及字符设备上的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);

当前代码可以编译OKAY,但数据大小不足以在用户空间中容纳我的应用程序。但是,每当我增加数组来存储更多数据时,例如

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 个答案:

答案 0 :(得分:1)

ioctl代码的参数“大小”受(16K -1)限制。您尝试传递的“大小”等于99999,该值超出了该限制。

头文件include/uapi/asm-generic/ioctl.h包含以下注释,解释了这种限制的来源:

/* 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