web-dev-qa-db-fra.com

Problème avec ioctl () dans un module de noyau simple

J'essaie de construire un module de noyau simple. Voici le contenu du fichier impliqué:

module.c:

    #include <linux/init.h>
    #include <linux/fs.h>
    #include <linux/device.h>
    #include <linux/kernel.h>
    #include "header.h"






    static int device_open(struct inode *inode, struct file *file)
    {
    printk("\n Open \n");
    return 0;
    }

    static int device_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long args)
    {
    switch(cmd)
    {
    case IOCTL_CMD:
    printk(KERN_ALERT "\n %s \n", (char *)args);
    break;
    }
    return 0;
    }


    static int device_release(struct inode *inode, struct file *file)
    {
    printk("\n Release \n");
    return 0;
    }

    static struct class *my_class;

    static struct file_operations fops={
    .open = device_open,
    .release = device_release,
    .compat_ioctl = device_ioctl
    };

    static int hello_init(void)
    {
    major_no = register_chrdev(0, DEVICE_NAME, &fops);
    printk("\n Major_no : %d", major_no);

    my_class = class_create(THIS_MODULE, DEVICE_NAME);
    device_create(my_class, NULL, MKDEV(major_no,0), NULL, DEVICE_NAME);
    printk("\n Device Initialized in kernel ....!!!");
    return 0;
    }




    static void hello_exit(void)
    {
    printk("\n Device is Released or closed \n");
    device_destroy(my_class,MKDEV(major_no,0));
    class_unregister(my_class);
    class_destroy(my_class);
    unregister_chrdev(major_no, DEVICE_NAME);
    printk("\n===============================================================\n");
    }



    module_init(hello_init);
    module_exit(hello_exit);

    MODULE_LICENSE("GPL");

appln.c

    #include <stdio.h>
    #include <fcntl.h>
    #include <string.h>

    #include "header.h"

    int main()
    {
    int fd;
    char * msg = "yahoooo";
    fd = open(DEVICE_PATH, O_RDWR);
    ioctl(fd, IOCTL_CMD, msg);
    printf("ioctl executed\n");
    close(fd);
    return 0;
    }

header.h:

    #include <linux/ioctl.h>
    #include <linux/kdev_t.h> /* for MKDEV */

    #define DEVICE_NAME "my_dev"
    #define DEVICE_PATH "/dev/my_dev"
    #define WRITE 0
    static int major_no;
    #define MAGIC_NO '4'
    /* 
     * Set the message of the device driver 
     */
    #define IOCTL_CMD _IOR(MAGIC_NO, 0, char *)

Mon module se charge parfaitement (je peux voir le mesg dans la fonction hello_init ()). Mais lorsque j'exécute le programme appln.c, même s'il effectue l'appel ioctl (), je ne vois aucun résultat. Quelqu'un peut-il dire pourquoi le module ignore-t-il mon appel ioctl?.

Merci,

3
gkt

Quelques choses:

  • Vous voulez utiliser "unlocked_ioctl" et non "compat_ioctl".
  • L’interface de fonction pour "device_ioctl" est incorrecte (voir include/linux/fs.h), elle devrait être:

        long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
    
  • Appln.c ne vérifie pas les codes d'erreur (open, ioctl).

Après avoir corrigé cela, le code fonctionnera correctement.

2
Kees Cook

Exemple minimal exécutable

Testé dans un environnement QEMU + Buildroot entièrement reproductible, pourrait donc aider les autres à faire fonctionner leur ioctl. GitHub en amont: module du noya | en-tête partagé | tilisateur .

La partie la plus gênante était de comprendre que certains identifiants bas sont détournés: https://stackoverflow.com/questions/10071296/ioctl-is-not-called-if-cmd-2 , vous devez utiliser _IOx macros.

Module de noyau:

#include <asm/uaccess.h> /* copy_from_user, copy_to_user */
#include <linux/debugfs.h>
#include <linux/module.h>
#include <linux/printk.h> /* printk */

#include "ioctl.h"

MODULE_LICENSE("GPL");

static struct dentry *dir;

static long unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long argp)
{
    void __user *arg_user;
    union {
        int i;
        lkmc_ioctl_struct s;
    } arg_kernel;

    arg_user = (void __user *)argp;
    pr_info("cmd = %x\n", cmd);
    switch (cmd) {
        case LKMC_IOCTL_INC:
            if (copy_from_user(&arg_kernel.i, arg_user, sizeof(arg_kernel.i))) {
                return -EFAULT;
            }
            pr_info("0 arg = %d\n", arg_kernel.i);
            arg_kernel.i += 1;
            if (copy_to_user(arg_user, &arg_kernel.i, sizeof(arg_kernel.i))) {
                return -EFAULT;
            }
        break;
        case LKMC_IOCTL_INC_DEC:
            if (copy_from_user(&arg_kernel.s, arg_user, sizeof(arg_kernel.s))) {
                return -EFAULT;
            }
            pr_info("1 arg = %d %d\n", arg_kernel.s.i, arg_kernel.s.j);
            arg_kernel.s.i += 1;
            arg_kernel.s.j -= 1;
            if (copy_to_user(arg_user, &arg_kernel.s, sizeof(arg_kernel.s))) {
                return -EFAULT;
            }
        break;
        default:
            return -EINVAL;
        break;
    }
    return 0;
}

static const struct file_operations fops = {
    .owner = THIS_MODULE,
    .unlocked_ioctl = unlocked_ioctl
};

static int myinit(void)
{
    dir = debugfs_create_dir("lkmc_ioctl", 0);
    /* ioctl permissions are not automatically restricted by rwx as for read / write,
     * but we could of course implement that ourselves:
     * https://stackoverflow.com/questions/29891803/user-permission-check-on-ioctl-command */
    debugfs_create_file("f", 0, dir, NULL, &fops);
    return 0;
}

static void myexit(void)
{
    debugfs_remove_recursive(dir);
}

module_init(myinit)
module_exit(myexit)

En-tête partagé:

#ifndef IOCTL_H
#define IOCTL_H

#include <linux/ioctl.h>

typedef struct {
    int i;
    int j;
} lkmc_ioctl_struct;
#define LKMC_IOCTL_MAGIC 0x33
#define LKMC_IOCTL_INC     _IOWR(LKMC_IOCTL_MAGIC, 0, int)
#define LKMC_IOCTL_INC_DEC _IOWR(LKMC_IOCTL_MAGIC, 1, lkmc_ioctl_struct)

#endif

Userland:

#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#include "../ioctl.h"

int main(int argc, char **argv)
{
    int fd, arg_int, ret;
    lkmc_ioctl_struct arg_struct;

    if (argc < 2) {
        puts("Usage: ./prog <ioctl-file>");
        return EXIT_FAILURE;
    }
    fd = open(argv[1], O_RDONLY);
    if (fd == -1) {
        perror("open");
        return EXIT_FAILURE;
    }
    /* 0 */
    {
        arg_int = 1;
        ret = ioctl(fd, LKMC_IOCTL_INC, &arg_int);
        if (ret == -1) {
            perror("ioctl");
            return EXIT_FAILURE;
        }
        printf("arg = %d\n", arg_int);
        printf("ret = %d\n", ret);
        printf("errno = %d\n", errno);
    }
    puts("");
    /* 1 */
    {
        arg_struct.i = 1;
        arg_struct.j = 1;
        ret = ioctl(fd, LKMC_IOCTL_INC_DEC, &arg_struct);
        if (ret == -1) {
            perror("ioctl");
            return EXIT_FAILURE;
        }
        printf("arg = %d %d\n", arg_struct.i, arg_struct.j);
        printf("ret = %d\n", ret);
        printf("errno = %d\n", errno);
    }
    close(fd);
    return EXIT_SUCCESS;
}