Problem with ioctl() in a simple kernel module
Solution 1
A few things:
- You want to use "unlocked_ioctl" not "compat_ioctl".
-
The function interface for "device_ioctl" is wrong (see
include/linux/fs.h
), it should be:long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
- The appln.c doesn't check error codes (open, ioctl).
After fixing that, the code will work fine.
Solution 2
Minimal runnable example
Tested in a fully reproducible QEMU + Buildroot environment, so might help others get their ioctl
working. GitHub upstream:
kernel module |
shared header |
userland.
The most annoying part was understanding that some low ids are hijacked: https://stackoverflow.com/questions/10071296/ioctl-is-not-called-if-cmd-2 , you have to use _IOx
macros.
Kernel module:
#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)
Shared header:
#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;
}
tolgatanriverdi
Updated on September 18, 2022Comments
-
tolgatanriverdi over 1 year
I am trying to build a simple kernel module. Following are the contents of file involved in it:
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 *)
My module loads perfectly(I can see the mesg in hello_init() function). But when i run the appln.c program, even when it makes the ioctl() call, I see no result of it. Can someone tell why is the module ignoring my ioctl call.
Thanks,
-
tolgatanriverdi almost 13 yearsKees Cook: Thanks, One more thing what is the difference b/w unlocked_ioctl and device_ioctl. Is there any updated source of reference, for module development, that lists all such changes with kernel code.
-
LRDPRDX about 4 years@gkt, I am not an expert in this field but ...
unlocked_ioctl
is not a concrete (defined) function. It is a name for a member ofstruct
.device_ioctl
, on the other hand, is a concrete function, i.e. a value. Your question is meaningless to me. It the same as what is the difference betweenint a
and 6?.