Linux Kernel Module/IOCTL: inappropriate ioctl for device

12,545

Solution 1

Okay. So. Here's the solution.

In Linux kernel 2.6.x the declaration for _ioctl calls changed from

static long wait_ioctl(struct inode *, struct file *, unsigned int, unsigned long);

To:

static long wait_ioctl(struct file *, unsigned int, unsigned long);

The fix is thus:

...
static long wait_ioctl(struct file *, unsigned int, unsigned long);
...
static long wait_ioctl(struct file *file, unsigned int cmd, unsigned long sub_cmd){
    if(_IOC_TYPE(cmd) != WAIT_DEVICE_MAGIC){
       printk(KERN_INFO "[wait device] invalid magic number: %u:%u:%u", _IOC_TYPE(cmd), cmd, WAIT_DEVICE_MAGIC);
       return -ENOTTY;
    }
 ....

Solution 2

.compat_ioctl

Also make sure to implement this file_operation if you are making 32-bit calls to a 64-bit kernel.

The symptom is that your ioctl handler is never run.

Share:
12,545

Related videos on Youtube

Tyler
Author by

Tyler

Updated on June 04, 2022

Comments

  • Tyler
    Tyler almost 2 years

    I am in the process of writing a Linux Kernel Module (LKM) serving as a pseudo-driver - I am unable to figure out how to make IOCTL calls between the LKM (wait.c) and the user-level program (user.c).

    The magic number for the device driver is 0xBF - the LKM does not communicate with a physical block/char device, it is simply an exercise. From what I can tell, the IOCTL call to KERN_IOCTL_CREATE_EVENT is not formatted properly & the magic number is incorrect.

    The IOCTL call that I am attempting to use is:

    #include <sys/ioctl.h>
    #define KERN_IOCTL_CREATE_EVENT   _IOWR(WAIT_DEVICE_MAGIC, 1, int)
    
    int main(){
    int ret;
    int fd;
    fd = open("/dev/wait", 0);
    if(fd < 0){
        return -1;
    }
    ret = ioctl(fd, KERN_IOCTL_CREATE_EVENT, 0);
    

    Error:

    [fail]: KERN_IOCTL_CREATE_EVENT: Inappropriate ioctl for device
    

    The user-mode application can open/close a file descriptor pointing to the device: /dev/wait but the case/switch statement isn't accepting the IOCTL call. Any suggestions?

    Here is the output of # uname -a

    Linux vagrant-ubuntu-trusty-64 3.13.11.11+ #1 SMP Mon Dec 1 20:50:23 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
    

    wait.c

    #include <linux/miscdevice.h>
    #include <linux/moduleparam.h>
    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/string.h>
    #include <asm/uaccess.h>
    #include <linux/sched.h>
    #include <linux/ioctl.h>
    #include <linux/cdev.h>
    #include <linux/init.h>
    #include <linux/wait.h>
    #include <linux/fs.h>
    
    #include "wait.h"
    
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("Tyler Fisher <[email protected]>");
    MODULE_DESCRIPTION("In-kernel wait queue");
    
    static unsigned long event_table_size = 50;
    module_param(event_table_size, ulong, (S_IRUSR | S_IRGRP | S_IROTH));
    MODULE_PARM_DESC(event_table_size, "Size of event table (i.e. how many processes can be blocking)");
    
    /* IOCTL function headers */
    static int wait_open(struct inode *, struct file *);
    static int wait_close(struct inode *, struct file *);
    static long wait_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
    
    /* other function headers */
    static long event_open(int event_id);
    
    /* file operations */
    static struct file_operations wait_fops = {
        .owner = THIS_MODULE,
        .open = wait_open,
        .release = wait_close,
        .llseek = noop_llseek,
        .unlocked_ioctl = wait_ioctl
    };
    
    /* device handler */
    static struct miscdevice wait_misc_device = {
      .minor = MISC_DYNAMIC_MINOR,
      .name = WAIT_DEVICE_NAME,
      .fops = &wait_fops
    };
    
    /* open wait device */
    static int wait_open(struct inode *inode, struct file *file){
        dev_t node = iminor(inode);
        if(MINOR(node) != WAIT_DEVICE_MINOR){
            return -ENODEV;
        }
        return 0;
    }
    
    static int wait_close(struct inode *inode, struct file *file){
        return 0;
    }
    
    static long wait_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long sub_cmd){
    switch(cmd){
        case KERN_IOCTL_CREATE_EVENT:
            printk(KERN_INFO "[wait device]: create event %lu\n", sub_cmd);
            return event_open(sub_cmd);
    
        default:
            return -ENOENT;
        }
    }
    
    static long event_open(int id){
        return 0;
    }
    
    static long __init wait_init(void){
        if(misc_register(&wait_misc_device) < 0){
            printk(KERN_ERR "[wait device] failed to register device\n");
            return -1;
        }
        printk(KERN_INFO "[wait device] has been registered\n");
        return 0;
    }
    
    static void __exit wait_exit(void){
        misc_deregister(&wait_misc_device);
        printk(KERN_INFO "[wait device] has been unregistered\n");
    }
    
    module_init(wait_init);
    module_exit(wait_exit);
    

    wait.h

    #include <linux/ioctl.h>
    
    #define WAIT_DEVICE_NAME    "wait"
    #define WAIT_DEVICE_MAGIC   0xBF
    #define WAIT_DEVICE_MAJOR   200
    #define WAIT_DEVICE_MINOR   0
    
    #define KERN_IOCTL_CREATE_EVENT     _IOWR(WAIT_DEVICE_MAGIC, 0x01, int)
    
    #define MAX_WAITING 5
    

    The test program for the IOCTL calls:

    user.c

    #include <sys/ioctl.h>
    #include <fcntl.h>
    #include <stdio.h>
    
    #define WAIT_DEVICE_MAGIC   0xBF
    #define KERN_IOCTL_CREATE_EVENT   _IOWR(WAIT_DEVICE_MAGIC, 0x01, int)
    #define KERN_IOCTL_DESTROY_EVENT  _IOWR(WAIT_DEVICE_MAGIC, 0x02, int)
    #define KERN_IOCTL_LOCK_EVENT     _IOWR(WAIT_DEVICE_MAGIC, 0x03, int)
    #define KERN_IOCTL_UNLOCK_EVENT   _IOWR(WAIT_DEVICE_MAGIC, 0x04, int)
    
    int main(){
        int fd;
        if(fd = open("/dev/wait", O_RDWR) < 0){
            perror("failed to open /dev/wait");
            return -1;
        }
    
        /* test IOCTL: event creation */
        if(ioctl(fd, KERN_IOCTL_CREATE_EVENT, 0) < 0){
            perror("[fail]: KERN_IOCTL_CREATE_EVENT");
            return -1;
        }
        return 0;
    }
    

    Makefile

    obj-m += wait.o
    CFLAGS_wait.o += -DDEBUG
    
    all:
      make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
    
    clean:
      make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
    

    In order to test out the LKM - clear dmesg, compile & execute user.c w/GCC:

    # dmesg -c > /dev/null 2>&1
    # make
    # rmmod wait.ko
    # insmod wait.ko
    # gcc user.c -o user && ./user
    

    The amount of debugging errors is embarassing. I feel bad for sharing this - and realize that this may cause the issue to be closed/downvoted into oblivion.

    # sh test.sh
    [+] cleared dmesg
    make -C /lib/modules/3.13.11.11+/build M=/home/vagrant/PROG40000-kernel-synchronization modules
    make[1]: Entering directory `/home/vagrant/ubuntu-trusty'
      CC [M]  /home/vagrant/PROG40000-kernel-synchronization/wait.o
    /home/vagrant/PROG40000-kernel-synchronization/wait.c:61:1: warning: initialization from incompatible pointer type [enabled by default]
     };
     ^
    /home/vagrant/PROG40000-kernel-synchronization/wait.c:61:1: warning: (near initialization for ‘wait_fops.unlocked_ioctl’) [enabled by default]
    In file included from include/linux/moduleparam.h:4:0,
                     from /home/vagrant/PROG40000-kernel-synchronization/wait.c:11:
    /home/vagrant/PROG40000-kernel-synchronization/wait.c: In function ‘__inittest’:
    include/linux/init.h:297:4: warning: return from incompatible pointer type [enabled by default]
      { return initfn; }     \
        ^
    /home/vagrant/PROG40000-kernel-synchronization/wait.c:167:1: note: in expansion of macro ‘module_init’
     module_init(wait_init);
     ^
      Building modules, stage 2.
      MODPOST 1 modules
      CC      /home/vagrant/PROG40000-kernel-synchronization/wait.mod.o
      LD [M]  /home/vagrant/PROG40000-kernel-synchronization/wait.ko
    make[1]: Leaving directory `/home/vagrant/ubuntu-trusty'
    [--dmesg--]
    [13112.810008] [wait device] has been unregistered
    [13112.819049] [wait device] has been registered
    [-/dmesg--]
    [+] compiled user-mode program
    -----
    [fail]: KERN_IOCTL_CREATE_EVENT: Inappropriate ioctl for device
    [fail]: KERN_IOCTL_CREATE_EVENT: Inappropriate ioctl for device
    [+] executed user-mode program
    -----
    [--dmesg--]
    [13112.810008] [wait device] has been unregistered
    [13112.819049] [wait device] has been registered
    [13112.893049] SOMEONE DARE READ FROM ME!?
    [13112.893057] [wait device] invalid magic number: 0:0:191
    [13112.893535] [wait device] invalid magic number: 0:0:191
    [-/dmesg--]
    
    • askb
      askb over 9 years
      You may need to install your updated kernel headers. Without this the user space program is unable to locate the correct ioctl, for some reason I had a similar issue on Ubuntu and the only thing that worked was installing the updated kernel headers.
    • Tyler
      Tyler over 9 years
      I updated the kernel using sudo make modules_install install -- and have the linux-headers-generic package installed -- current kernel version is 3.13.11.11+