]> git.karo-electronics.de Git - karo-tx-linux.git/blob - kernel/kcov.c
Merge branch 'akpm-current/current'
[karo-tx-linux.git] / kernel / kcov.c
1 #define pr_fmt(fmt) "kcov: " fmt
2
3 #include <linux/compiler.h>
4 #include <linux/types.h>
5 #include <linux/file.h>
6 #include <linux/fs.h>
7 #include <linux/mm.h>
8 #include <linux/printk.h>
9 #include <linux/slab.h>
10 #include <linux/spinlock.h>
11 #include <linux/vmalloc.h>
12 #include <linux/debugfs.h>
13 #include <linux/uaccess.h>
14 #include <linux/kcov.h>
15
16 /*
17  * kcov descriptor (one per opened debugfs file).
18  * State transitions of the descriptor:
19  *  - initial state after open()
20  *  - then there must be a single ioctl(KCOV_INIT_TRACE) call
21  *  - then, mmap() call (several calls are allowed but not useful)
22  *  - then, repeated enable/disable for a task (only one task a time allowed)
23  */
24 struct kcov {
25         /*
26          * Reference counter. We keep one for:
27          *  - opened file descriptor
28          *  - task with enabled coverage (we can't unwire it from another task)
29          */
30         atomic_t                rc;
31         /* The lock protects mode, size, area and t. */
32         spinlock_t              lock;
33         enum kcov_mode          mode;
34         unsigned                size;
35         void                    *area;
36         struct task_struct      *t;
37 };
38
39 /*
40  * Entry point from instrumented code.
41  * This is called once per basic-block/edge.
42  */
43 void __sanitizer_cov_trace_pc(void)
44 {
45         struct task_struct *t;
46         enum kcov_mode mode;
47
48         t = current;
49         /*
50          * We are interested in code coverage as a function of a syscall inputs,
51          * so we ignore code executed in interrupts.
52          */
53         if (!t || in_interrupt())
54                 return;
55         mode = READ_ONCE(t->kcov_mode);
56         if (mode == KCOV_MODE_TRACE) {
57                 unsigned long *area;
58                 unsigned long pos;
59
60                 /*
61                  * There is some code that runs in interrupts but for which
62                  * in_interrupt() returns false (e.g. preempt_schedule_irq()).
63                  * READ_ONCE()/barrier() effectively provides load-acquire wrt
64                  * interrupts, there are paired barrier()/WRITE_ONCE() in
65                  * kcov_ioctl_locked().
66                  */
67                 barrier();
68                 area = t->kcov_area;
69                 /* The first word is number of subsequent PCs. */
70                 pos = READ_ONCE(area[0]) + 1;
71                 if (likely(pos < t->kcov_size)) {
72                         area[pos] = _RET_IP_;
73                         WRITE_ONCE(area[0], pos);
74                 }
75         }
76 }
77 EXPORT_SYMBOL(__sanitizer_cov_trace_pc);
78
79 static void kcov_get(struct kcov *kcov)
80 {
81         atomic_inc(&kcov->rc);
82 }
83
84 static void kcov_put(struct kcov *kcov)
85 {
86         if (atomic_dec_and_test(&kcov->rc)) {
87                 vfree(kcov->area);
88                 kfree(kcov);
89         }
90 }
91
92 void kcov_task_init(struct task_struct *t)
93 {
94         t->kcov_mode = 0;
95         t->kcov_size = 0;
96         t->kcov_area = NULL;
97         t->kcov = NULL;
98 }
99
100 void kcov_task_exit(struct task_struct *t)
101 {
102         struct kcov *kcov;
103
104         kcov = t->kcov;
105         if (kcov == NULL)
106                 return;
107         spin_lock(&kcov->lock);
108         if (WARN_ON(kcov->t != t)) {
109                 spin_unlock(&kcov->lock);
110                 return;
111         }
112         /* Just to not leave dangling references behind. */
113         kcov_task_init(t);
114         kcov->t = NULL;
115         spin_unlock(&kcov->lock);
116         kcov_put(kcov);
117 }
118
119 static int kcov_mmap(struct file *filep, struct vm_area_struct *vma)
120 {
121         int res = 0;
122         void *area;
123         struct kcov *kcov = vma->vm_file->private_data;
124         unsigned long size, off;
125         struct page *page;
126
127         area = vmalloc_user(vma->vm_end - vma->vm_start);
128         if (!area)
129                 return -ENOMEM;
130
131         spin_lock(&kcov->lock);
132         size = kcov->size * sizeof(unsigned long);
133         if (kcov->mode == 0 || vma->vm_pgoff != 0 ||
134             vma->vm_end - vma->vm_start != size) {
135                 res = -EINVAL;
136                 goto exit;
137         }
138         if (!kcov->area) {
139                 kcov->area = area;
140                 vma->vm_flags |= VM_DONTEXPAND;
141                 spin_unlock(&kcov->lock);
142                 for (off = 0; off < size; off += PAGE_SIZE) {
143                         page = vmalloc_to_page(kcov->area + off);
144                         if (vm_insert_page(vma, vma->vm_start + off, page))
145                                 WARN_ONCE(1, "vm_insert_page() failed");
146                 }
147                 return 0;
148         }
149 exit:
150         spin_unlock(&kcov->lock);
151         vfree(area);
152         return res;
153 }
154
155 static int kcov_open(struct inode *inode, struct file *filep)
156 {
157         struct kcov *kcov;
158
159         kcov = kzalloc(sizeof(*kcov), GFP_KERNEL);
160         if (!kcov)
161                 return -ENOMEM;
162         atomic_set(&kcov->rc, 1);
163         spin_lock_init(&kcov->lock);
164         filep->private_data = kcov;
165         return nonseekable_open(inode, filep);
166 }
167
168 static int kcov_close(struct inode *inode, struct file *filep)
169 {
170         kcov_put(filep->private_data);
171         return 0;
172 }
173
174 static int kcov_ioctl_locked(struct kcov *kcov, unsigned int cmd,
175                              unsigned long arg)
176 {
177         struct task_struct *t;
178
179         switch (cmd) {
180         case KCOV_INIT_TRACE:
181                 /*
182                  * Enable kcov in trace mode and setup buffer size.
183                  * Must happen before anything else.
184                  * Size must be at least 2 to hold current position and one PC.
185                  */
186                 if (arg < 2 || arg > INT_MAX)
187                         return -EINVAL;
188                 if (kcov->mode != 0)
189                         return -EBUSY;
190                 kcov->mode = KCOV_MODE_TRACE;
191                 kcov->size = arg;
192                 return 0;
193         case KCOV_ENABLE:
194                 /*
195                  * Enable coverage for the current task.
196                  * At this point user must have been enabled trace mode,
197                  * and mmapped the file. Coverage collection is disabled only
198                  * at task exit or voluntary by KCOV_DISABLE. After that it can
199                  * be enabled for another task.
200                  */
201                 if (arg != 0 || kcov->mode == 0 || kcov->area == NULL)
202                         return -EINVAL;
203                 if (kcov->t != NULL)
204                         return -EBUSY;
205                 t = current;
206                 /* Cache in task struct for performance. */
207                 t->kcov_size = kcov->size;
208                 t->kcov_area = kcov->area;
209                 /* See comment in __sanitizer_cov_trace_pc(). */
210                 barrier();
211                 WRITE_ONCE(t->kcov_mode, kcov->mode);
212                 t->kcov = kcov;
213                 kcov->t = t;
214                 /* This is put either in kcov_task_exit() or in KCOV_DISABLE. */
215                 kcov_get(kcov);
216                 return 0;
217         case KCOV_DISABLE:
218                 /* Disable coverage for the current task. */
219                 if (arg != 0 || current->kcov != kcov)
220                         return -EINVAL;
221                 t = current;
222                 if (WARN_ON(kcov->t != t))
223                         return -EINVAL;
224                 kcov_task_init(t);
225                 kcov->t = NULL;
226                 kcov_put(kcov);
227                 return 0;
228         default:
229                 return -EINVAL;
230         }
231 }
232
233 static long kcov_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
234 {
235         struct kcov *kcov;
236         int res;
237
238         kcov = filep->private_data;
239         spin_lock(&kcov->lock);
240         res = kcov_ioctl_locked(kcov, cmd, arg);
241         spin_unlock(&kcov->lock);
242         return res;
243 }
244
245 static const struct file_operations kcov_fops = {
246         .open           = kcov_open,
247         .unlocked_ioctl = kcov_ioctl,
248         .mmap           = kcov_mmap,
249         .release        = kcov_close,
250 };
251
252 static int __init kcov_init(void)
253 {
254         if (!debugfs_create_file("kcov", 0600, NULL, NULL, &kcov_fops)) {
255                 pr_err("failed to create kcov in debugfs\n");
256                 return -ENOMEM;
257         }
258         return 0;
259 }
260
261 device_initcall(kcov_init);