fast method_name->source_file lookups, heuristics for finding ioctl names
[moctel.git] / moctel_mod.c
1 // Copyright (C) 2013 Jann Horn <jann@thejh.net>
2 // This file is licensed under the GNU GPL v2 (see
3 // the LICENSE file).
4
5 #undef __KERNEL__
6 #define __KERNEL__
7 #undef MODULE
8 #define MODULE
9
10 #include <linux/module.h>
11 #include <linux/kernel.h>
12 #include <linux/init.h>
13 #include <linux/file.h>
14 #include <linux/fs.h>
15 #include <linux/ioctl.h>
16 #include <linux/uaccess.h>
17 #include <linux/miscdevice.h>
18 #include <linux/kallsyms.h>
19 #include <linux/blkdev.h>
20
21 struct fetch_fops_args {
22   int fd;
23   u64 retp;
24 };
25
26 #define MOCTEL_FETCH_FOPS _IOR('m', 1, struct fetch_fops_args)
27
28 static int ioctl_open(struct inode *nodp, struct file *filp) {
29   return 0;
30 }
31
32 static long ioctl_ioctl(struct file *filp_, unsigned int cmd, unsigned long arg) {
33   struct fetch_fops_args args;
34   struct file *filp;
35   void *ioctl_method = NULL;
36   char ioctl_method_name[KSYM_NAME_LEN];
37   int (*lookup_symbol_name_)(unsigned long addr, char *symname);
38
39   lookup_symbol_name_ = (void*)kallsyms_lookup_name("lookup_symbol_name");
40
41   if (cmd != MOCTEL_FETCH_FOPS) return -EINVAL;
42   memset(ioctl_method_name, 0, sizeof(ioctl_method_name));
43   if (copy_from_user(&args, (struct fetch_fops_args __user *)arg, sizeof(args))) return -EINVAL;
44   filp = fget(args.fd);
45   if (!filp) return -EBADF;
46   if (filp->f_op) ioctl_method = filp->f_op->unlocked_ioctl;
47   lookup_symbol_name_((unsigned long)ioctl_method, ioctl_method_name);
48   if (strcmp(ioctl_method_name, "block_ioctl") == 0) {
49     struct block_device *bdev = I_BDEV(filp->f_mapping->host);
50     ioctl_method = bdev->bd_disk->fops->ioctl;
51     lookup_symbol_name_((unsigned long)ioctl_method, ioctl_method_name);
52   }
53   fput(filp);
54   if (copy_to_user((struct file_operations __user *)args.retp, ioctl_method_name, KSYM_NAME_LEN)) return -EINVAL;
55   return 0;
56 }
57
58 static int ioctl_release(struct inode *nodp, struct file *filp) {
59   return 0;
60 }
61
62 static const struct file_operations ioctl_fops = {
63   .owner = THIS_MODULE,
64   .unlocked_ioctl = ioctl_ioctl,
65   .open = ioctl_open,
66   .release = ioctl_release
67 };
68
69 static struct miscdevice ioctl_miscdev = {
70   .minor = 0,
71   .name = "ioctl_info",
72   .fops = &ioctl_fops
73 };
74
75 static int __init init_ioctl(void) {
76   int ret;
77   printk(KERN_INFO "loading ioctl helper\n");
78
79   ret = misc_register(&ioctl_miscdev);
80   return ret;
81 }
82
83 static void __exit cleanup_ioctl(void) {
84   printk(KERN_INFO "unloading ioctl helper\n");
85   misc_deregister(&ioctl_miscdev);
86 }
87
88 module_init(init_ioctl);
89 module_exit(cleanup_ioctl);
90
91 MODULE_LICENSE("GPL v2");