diff --git a/kernel_module/ioctl.c b/kernel_module/ioctl.c index fdb8344..95d38fe 100644 --- a/kernel_module/ioctl.c +++ b/kernel_module/ioctl.c @@ -1,13 +1,12 @@ /* -Give integer and a pointer to the kernel, and get one positive integer out. +Input: an integer (with some annoying restrictions) and a pointer -Feels like an archaic API... so many weird restrictions for something that could be so simple! +Output: -TODO: must the ioctl numbers be globally unique? How to ensure that? +- positive integer return value, which for sanity should only be used with negative values for success +- the input pointer data may be overwritten to contain output -Ioctl is super picky about the ioctl numbers, it is very annoying: https://stackoverflow.com/questions/10071296/ioctl-is-not-called-if-cmd-2 - -See how do_vfs_ioctl highjacks several values. This "forces" use to use the _IOx macros... +Feels like an archaic API... so many weird restrictions and types for something that could be so simple! Documentation/ioctl/ioctl-number.txt has some info: @@ -17,18 +16,54 @@ Documentation/ioctl/ioctl-number.txt has some info: _IOWR an ioctl with both write and read parameters. */ +#include /* copy_from_user, copy_to_user */ #include #include #include /* printk */ +#include "ioctl.h" + MODULE_LICENSE("GPL"); static struct dentry *dir; -static long unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +static long unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long argp) { - pr_info("cmd = %u\n", cmd); - return cmd + 1; + 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 = { diff --git a/kernel_module/ioctl.h b/kernel_module/ioctl.h index 4fad8c3..3f4d5bb 100644 --- a/kernel_module/ioctl.h +++ b/kernel_module/ioctl.h @@ -3,6 +3,30 @@ #include -#define IOCTL0 _IOW(123, 0, char *) +/* Structs are the way to pass multiple arguments. */ +typedef struct { + int i; + int j; +} lkmc_ioctl_struct; + +/* Some random number I can't understand how to choose. */ +#define LKMC_IOCTL_MAGIC 0x33 + +/* + * I think those number does not *need* to be unique across, that is just to help debugging: + * https://stackoverflow.com/questions/22496123/what-is-the-meaning-of-this-macro-iormy-macig-0-int + * + * However, the ioctl syscall highjacks several low values at do_vfs_ioctl, e.g. + * This "forces" use to use the _IOx macros... + * https://stackoverflow.com/questions/10071296/ioctl-is-not-called-if-cmd-2 + * + * Some of those magic low values are used for fnctl, which can also be used on regular files: + * e.g. FIOCLEX for close-on-exec: + * https://stackoverflow.com/questions/6125068/what-does-the-fd-cloexec-fcntl-flag-do + * + * TODO are the W or R of _IOx and type functional, or only to help with uniqueness? + * */ +#define LKMC_IOCTL_INC _IOWR(LKMC_IOCTL_MAGIC, 0, int) +#define LKMC_IOCTL_INC_DEC _IOWR(LKMC_IOCTL_MAGIC, 1, lkmc_ioctl_struct) #endif diff --git a/kernel_module/user/ioctl.c b/kernel_module/user/ioctl.c index 34fddb0..ccb4500 100644 --- a/kernel_module/user/ioctl.c +++ b/kernel_module/user/ioctl.c @@ -12,7 +12,8 @@ int main(int argc, char **argv) { - int fd, ret; + int fd, arg_int, ret; + lkmc_ioctl_struct arg_struct; if (argc < 4) { puts("Usage: ./prog "); @@ -23,13 +24,32 @@ int main(int argc, char **argv) perror("open"); return EXIT_FAILURE; } - ret = ioctl(fd, IOCTL0, argv[3]); - if (ret == -1) { - perror("ioctl"); - 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); } - printf("ret = %d\n", ret); - printf("errno = %d\n", errno); close(fd); return EXIT_SUCCESS; }