Problema com ioctl () em um módulo do kernel simples

3

Eu estou tentando construir um módulo de kernel simples. A seguir, o conteúdo do arquivo envolvido nele:

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

Meu módulo carrega perfeitamente (eu posso ver o mesg na função hello_init ()). Mas quando eu executo o programa appln.c, mesmo quando ele faz a chamada ioctl (), não vejo resultado disso. Alguém pode dizer por que o módulo está ignorando minha chamada ioctl?

Obrigado,

    
por gkt 23.07.2011 / 16:36

2 respostas

2

Algumas coisas:

  • Você deseja usar "unlocked_ioctl" e não "compat_ioctl".
  • A interface de função para "device_ioctl" está errada (veja include/linux/fs.h ), deve ser:

        long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
    
  • O appln.c não verifica os códigos de erro ( aberto , ioctl ).

Depois de corrigir isso, o código funcionará bem.

    
por Kees Cook 24.07.2011 / 19:59
0

Exemplo mínimo executável

Testado em um ambiente QEMU + Buildroot totalmente reproduzível, portanto, pode ajudar outras pessoas a obter seu ioctl funcionando. GitHub upstream: módulo do kernel | cabeçalho compartilhado | userland .

A parte mais irritante foi entender que algumas ids baixas são sequestradas: link , você precisa usar _IOx macros.

Módulo do kernel:

#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)

Cabeçalho compartilhado:

#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;
}