1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
|
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/module.h>
#include "monitor_kernel_lib.c"
#define DEVICE_NAME "variable_monitor"
// for character device
static dev_t dev_num;
static struct cdev *watch_cdev;
static struct class *watch_class;
struct my_device_data
{
pid_t pid;
};
static int device_open(struct inode *inode, struct file *file)
{
// printk(KERN_INFO "%s\n", __FUNCTION__);
struct my_device_data *data;
// save pid
data = kmalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->pid = current->pid;
file->private_data = data;
return 0;
}
static int device_release(struct inode *inode, struct file *file)
{
// printk(KERN_INFO "%s\n", __FUNCTION__);
// load pid
struct my_device_data *data = file->private_data;
// clear watch with pid
clear_watch(data->pid);
return 0;
}
static long device_ioctl(struct file *file, unsigned int ioctl_num, unsigned long ioctl_param)
{
watch_arg warg;
void *kptr;
kernel_watch_timer *timer = NULL;
kernel_watch_arg k_watch_arg;
// copy watch_arg
if (copy_from_user(&warg, (watch_arg *)ioctl_param, sizeof(warg)))
{
return -EACCES;
}
printk(KERN_INFO "Watch_arg: task_id=%d, name=%s, ptr=%p, length_byte=%d, time_ns=%ld, threshold=%lld\n",
warg.task_id, warg.name, warg.ptr, warg.length_byte, warg.time_ns, warg.threshold);
// user space address to kernel space address
kptr = convert_user_space_ptr(warg.task_id, (unsigned long)warg.ptr);
if (kptr == NULL)
{
printk(KERN_ERR "Cannot access user space\n");
return -EACCES;
}
// check length
if (warg.length_byte != 1 && warg.length_byte != 2 && warg.length_byte != 4 && warg.length_byte != 8)
{
printk(KERN_ERR "Invalid length %d\n", warg.length_byte);
return -EINVAL;
}
// k_watch_arg init
w_arg2k_w_arg(kptr, warg, &k_watch_arg);
timer = get_timer(warg.time_ns); // get a valuable timer
printk(KERN_INFO "ptr transform kptr: %p\n", kptr);
printk(KERN_INFO "timer: %p\n", timer);
printk(KERN_INFO "timer->sentinel: %d, timer->time_ns: %lld\n", timer->sentinel, timer->time_ns);
printk(KERN_INFO "timer->hr_timer: %p\n", &timer->hr_timer);
TIMER_CANCEL(timer); // just in case
timer_add_watch(timer, k_watch_arg);
TIMER_START(timer);
printk(KERN_INFO "Start watching var: %s\n", warg.name);
return 0;
}
static struct file_operations fops = {
.open = device_open,
.release = device_release,
.unlocked_ioctl = device_ioctl,
};
int init_module(void)
{
printk(KERN_INFO "%s\n", __FUNCTION__);
if (alloc_chrdev_region(&dev_num, 0, 1, DEVICE_NAME) < 0)
{
printk(KERN_ALERT "Failed to register device number\n");
return -1;
}
if ((watch_cdev = cdev_alloc()) == NULL)
{
printk(KERN_ALERT "Failed to allocate cdev structure\n");
unregister_chrdev_region(dev_num, 1);
return -1;
}
cdev_init(watch_cdev, &fops);
if (cdev_add(watch_cdev, dev_num, 1) == -1)
{
printk(KERN_ALERT "Failed to add cdev structure\n");
device_destroy(watch_class, dev_num);
class_destroy(watch_class);
unregister_chrdev_region(dev_num, 1);
return -1;
}
if ((watch_class = class_create(THIS_MODULE, DEVICE_NAME)) == NULL)
{
printk(KERN_ALERT "Failed to create class\n");
cdev_del(watch_cdev);
unregister_chrdev_region(dev_num, 1);
return -1;
}
if (device_create(watch_class, NULL, dev_num, NULL, DEVICE_NAME) == NULL)
{
printk(KERN_ALERT "Failed to create device\n");
class_destroy(watch_class);
cdev_del(watch_cdev);
unregister_chrdev_region(dev_num, 1);
return -1;
}
printk(KERN_INFO "dev number: %d\n", dev_num);
printk(KERN_INFO "path: /dev/%s %d\n", DEVICE_NAME, dev_num);
fn_kallsyms_lookup_name_init(); // init kallsyms_lookup_name
LOOKUP_SYMS(stack_trace_save_tsk); // stack_trace_save_tsk
LOOKUP_SYMS(show_stack); // show_stack
return 0;
}
void cleanup_module(void)
{
printk(KERN_INFO "%s\n", __FUNCTION__);
// clear all timer and page list
clear_all_watch();
// unmount
device_destroy(watch_class, dev_num);
class_destroy(watch_class);
cdev_del(watch_cdev);
unregister_chrdev_region(dev_num, 1);
}
MODULE_LICENSE("GPL");
|