From 1f1cf4b0bdeec78d5615e0aaa23c36c81ae63674 Mon Sep 17 00:00:00 2001 From: Ciro Santilli Date: Tue, 13 Jun 2017 09:44:29 +0100 Subject: [PATCH] Anonymous inode! --- kernel_module/anonymous_inode.c | 85 ++++++++++++++++++++++++++++ kernel_module/anonymous_inode.h | 9 +++ kernel_module/fops.c | 4 +- kernel_module/ioctl.c | 5 +- kernel_module/user/anonymous_inode.c | 40 +++++++++++++ kernel_module/user/ioctl.c | 4 +- rootfs_overlay/anonymous_inode.sh | 6 ++ rootfs_overlay/ioctl.sh | 3 +- 8 files changed, 149 insertions(+), 7 deletions(-) create mode 100644 kernel_module/anonymous_inode.c create mode 100644 kernel_module/anonymous_inode.h create mode 100644 kernel_module/user/anonymous_inode.c create mode 100755 rootfs_overlay/anonymous_inode.sh diff --git a/kernel_module/anonymous_inode.c b/kernel_module/anonymous_inode.c new file mode 100644 index 0000000..3ac3ebc --- /dev/null +++ b/kernel_module/anonymous_inode.c @@ -0,0 +1,85 @@ +/* +https://stackoverflow.com/questions/4508998/what-is-anonymous-inode + +anon_inode_getfd example: + +- get an anonymous inode via ioctl from a debugfs entry +- read from that inode + +This method allows getting multiple file descriptors from a single filesystem, +which reduces namespace pollution. +*/ + +#include /* copy_from_user, copy_to_user */ +#include +#include +#include /* EFAULT */ +#include +#include +#include /* min */ +#include +#include /* printk */ + +#include "anonymous_inode.h" + +MODULE_LICENSE("GPL"); + +static struct dentry *dir; + +static ssize_t read(struct file *filp, char __user *buf, size_t len, loff_t *off) +{ + char kbuf[1024]; + size_t ret; + + ret = snprintf(kbuf, sizeof(kbuf), "%llu", (unsigned long long)jiffies); + if (copy_to_user(buf, kbuf, ret)) { + ret = -EFAULT; + } + return ret; +} + +static const struct file_operations fops_anon = { + .read = read, +}; + +static long unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long argp) +{ + int fd; + + switch (cmd) { + case LKMC_ANONYMOUS_INODE_GET_FD: + fd = anon_inode_getfd( + "random", + &fops_anon, + NULL, + O_RDONLY | O_CLOEXEC + ); + if (copy_to_user((void __user *)argp, &fd, sizeof(fd))) { + return -EFAULT; + } + break; + default: + return -EINVAL; + break; + } + return 0; +} + +static const struct file_operations fops_ioctl = { + .unlocked_ioctl = unlocked_ioctl +}; + +static int myinit(void) +{ + dir = debugfs_create_dir("lkmc_anonymous_inode", 0); + debugfs_create_file("f", 0, dir, NULL, &fops_ioctl); + return 0; +} + +static void myexit(void) +{ + debugfs_remove_recursive(dir); +} + +module_init(myinit) +module_exit(myexit) diff --git a/kernel_module/anonymous_inode.h b/kernel_module/anonymous_inode.h new file mode 100644 index 0000000..d35fc06 --- /dev/null +++ b/kernel_module/anonymous_inode.h @@ -0,0 +1,9 @@ +#ifndef IOCTL_H +#define IOCTL_H + +#include + +#define LKMC_ANONYMOUS_INODE_MAGIC 0x33 +#define LKMC_ANONYMOUS_INODE_GET_FD _IOR(LKMC_ANONYMOUS_INODE_MAGIC, 0, int) + +#endif diff --git a/kernel_module/fops.c b/kernel_module/fops.c index e5e457a..2b8767d 100644 --- a/kernel_module/fops.c +++ b/kernel_module/fops.c @@ -17,13 +17,13 @@ in drivers (syscalls being the other one). Here we use debugfs. */ +#include /* copy_from_user, copy_to_user */ #include #include /* EFAULT */ #include #include /* min */ #include #include /* printk */ -#include /* copy_from_user, copy_to_user */ MODULE_LICENSE("GPL"); @@ -95,7 +95,7 @@ static ssize_t write(struct file *filp, const char __user *buf, size_t len, loff Called on the last close: http://stackoverflow.com/questions/11393674/why-is-the-close-function-is-called-release-in-struct-file-operations-in-the-l */ -static int release (struct inode *inode, struct file *filp) +static int release(struct inode *inode, struct file *filp) { printk(KERN_INFO "release\n"); return 0; diff --git a/kernel_module/ioctl.c b/kernel_module/ioctl.c index 95d38fe..aead6b4 100644 --- a/kernel_module/ioctl.c +++ b/kernel_module/ioctl.c @@ -73,7 +73,10 @@ static const struct file_operations fops = { static int myinit(void) { dir = debugfs_create_dir("lkmc_ioctl", 0); - debugfs_create_file("f", 0666, dir, NULL, &fops); + /* 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; } diff --git a/kernel_module/user/anonymous_inode.c b/kernel_module/user/anonymous_inode.c new file mode 100644 index 0000000..760fa0e --- /dev/null +++ b/kernel_module/user/anonymous_inode.c @@ -0,0 +1,40 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include /* sleep */ + +#include "../anonymous_inode.h" + +int main(int argc, char **argv) +{ + char buf[1024]; + int fd_ioctl, fd_ioctl_anon, ret; + + if (argc < 2) { + puts("Usage: ./prog "); + return EXIT_FAILURE; + } + fd_ioctl = open(argv[1], O_RDONLY); + if (fd_ioctl == -1) { + perror("open"); + return EXIT_FAILURE; + } + ret = ioctl(fd_ioctl, LKMC_ANONYMOUS_INODE_GET_FD, &fd_ioctl_anon); + if (ret == -1) { + perror("ioctl"); + return EXIT_FAILURE; + } + ret = read(fd_ioctl_anon, buf, sizeof(buf)); + printf("%.*s\n", ret, buf); + sleep(1); + ret = read(fd_ioctl_anon, buf, sizeof(buf)); + printf("%.*s\n", ret, buf); + close(fd_ioctl_anon); + close(fd_ioctl); + return EXIT_SUCCESS; +} diff --git a/kernel_module/user/ioctl.c b/kernel_module/user/ioctl.c index ccb4500..9df0b4c 100644 --- a/kernel_module/user/ioctl.c +++ b/kernel_module/user/ioctl.c @@ -15,8 +15,8 @@ int main(int argc, char **argv) int fd, arg_int, ret; lkmc_ioctl_struct arg_struct; - if (argc < 4) { - puts("Usage: ./prog "); + if (argc < 2) { + puts("Usage: ./prog "); return EXIT_FAILURE; } fd = open(argv[1], O_RDONLY); diff --git a/rootfs_overlay/anonymous_inode.sh b/rootfs_overlay/anonymous_inode.sh new file mode 100755 index 0000000..e10b237 --- /dev/null +++ b/rootfs_overlay/anonymous_inode.sh @@ -0,0 +1,6 @@ +#!/bin/sh +set -e +insmod /anonymous_inode.ko +cd /sys/kernel/debug/lkmc_anonymous_inode/ +/anonymous_inode.out f +rmmod anonymous_inode diff --git a/rootfs_overlay/ioctl.sh b/rootfs_overlay/ioctl.sh index 3e7c2be..ec3acd9 100755 --- a/rootfs_overlay/ioctl.sh +++ b/rootfs_overlay/ioctl.sh @@ -2,6 +2,5 @@ set -e insmod /ioctl.ko cd /sys/kernel/debug/lkmc_ioctl/ -/ioctl.out f 2 1 -#/ioctl.out f 1 0 +/ioctl.out f rmmod ioctl