System call filtering
[lttng-modules.git] / lttng-syscalls.c
1 /*
2 * lttng-syscalls.c
3 *
4 * LTTng syscall probes.
5 *
6 * Copyright (C) 2010-2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; only
11 * version 2.1 of the License.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23 #include <linux/module.h>
24 #include <linux/slab.h>
25 #include <linux/compat.h>
26 #include <linux/err.h>
27 #include <linux/bitmap.h>
28 #include <asm/ptrace.h>
29 #include <asm/syscall.h>
30
31 #include "wrapper/tracepoint.h"
32 #include "lttng-events.h"
33
34 #ifndef CONFIG_COMPAT
35 # ifndef is_compat_task
36 # define is_compat_task() (0)
37 # endif
38 #endif
39
40 static
41 void syscall_entry_probe(void *__data, struct pt_regs *regs, long id);
42
43 /*
44 * Forward declarations for old kernels.
45 */
46 struct mmsghdr;
47 struct rlimit64;
48 struct oldold_utsname;
49 struct old_utsname;
50 struct sel_arg_struct;
51 struct mmap_arg_struct;
52
53 #ifdef IA32_NR_syscalls
54 #define NR_compat_syscalls IA32_NR_syscalls
55 #else
56 #define NR_compat_syscalls NR_syscalls
57 #endif
58
59 /*
60 * Take care of NOARGS not supported by mainline.
61 */
62 #define DECLARE_EVENT_CLASS_NOARGS(name, tstruct, assign, print)
63 #define DEFINE_EVENT_NOARGS(template, name)
64 #define TRACE_EVENT_NOARGS(name, struct, assign, print)
65
66 /*
67 * Create LTTng tracepoint probes.
68 */
69 #define LTTNG_PACKAGE_BUILD
70 #define CREATE_TRACE_POINTS
71 #define TP_MODULE_NOINIT
72 #define TRACE_INCLUDE_PATH ../instrumentation/syscalls/headers
73
74 #define PARAMS(args...) args
75
76 /* Hijack probe callback for system calls */
77 #undef TP_PROBE_CB
78 #define TP_PROBE_CB(_template) &syscall_entry_probe
79 #define SC_TRACE_EVENT(_name, _proto, _args, _struct, _assign, _printk) \
80 TRACE_EVENT(_name, PARAMS(_proto), PARAMS(_args),\
81 PARAMS(_struct), PARAMS(_assign), PARAMS(_printk))
82 #define SC_DECLARE_EVENT_CLASS_NOARGS(_name, _struct, _assign, _printk) \
83 DECLARE_EVENT_CLASS_NOARGS(_name, PARAMS(_struct), PARAMS(_assign),\
84 PARAMS(_printk))
85 #define SC_DEFINE_EVENT_NOARGS(_template, _name) \
86 DEFINE_EVENT_NOARGS(_template, _name)
87 #undef TRACE_SYSTEM
88 #define TRACE_SYSTEM syscalls_integers
89 #include "instrumentation/syscalls/headers/syscalls_integers.h"
90 #undef TRACE_SYSTEM
91 #define TRACE_SYSTEM syscalls_pointers
92 #include "instrumentation/syscalls/headers/syscalls_pointers.h"
93 #undef TRACE_SYSTEM
94 #undef SC_TRACE_EVENT
95 #undef SC_DECLARE_EVENT_CLASS_NOARGS
96 #undef SC_DEFINE_EVENT_NOARGS
97
98 #define TRACE_SYSTEM syscalls_unknown
99 #include "instrumentation/syscalls/headers/syscalls_unknown.h"
100 #undef TRACE_SYSTEM
101
102 /* For compat syscalls */
103 #undef _TRACE_SYSCALLS_integers_H
104 #undef _TRACE_SYSCALLS_pointers_H
105
106 /* Hijack probe callback for system calls */
107 #undef TP_PROBE_CB
108 #define TP_PROBE_CB(_template) &syscall_entry_probe
109 #define SC_TRACE_EVENT(_name, _proto, _args, _struct, _assign, _printk) \
110 TRACE_EVENT(compat_##_name, PARAMS(_proto), PARAMS(_args), \
111 PARAMS(_struct), PARAMS(_assign), \
112 PARAMS(_printk))
113 #define SC_DECLARE_EVENT_CLASS_NOARGS(_name, _struct, _assign, _printk) \
114 DECLARE_EVENT_CLASS_NOARGS(compat_##_name, PARAMS(_struct), \
115 PARAMS(_assign), PARAMS(_printk))
116 #define SC_DEFINE_EVENT_NOARGS(_template, _name) \
117 DEFINE_EVENT_NOARGS(compat_##_template, compat_##_name)
118 #define TRACE_SYSTEM compat_syscalls_integers
119 #include "instrumentation/syscalls/headers/compat_syscalls_integers.h"
120 #undef TRACE_SYSTEM
121 #define TRACE_SYSTEM compat_syscalls_pointers
122 #include "instrumentation/syscalls/headers/compat_syscalls_pointers.h"
123 #undef TRACE_SYSTEM
124 #undef SC_TRACE_EVENT
125 #undef SC_DECLARE_EVENT_CLASS_NOARGS
126 #undef SC_DEFINE_EVENT_NOARGS
127 #undef TP_PROBE_CB
128
129 #undef TP_MODULE_NOINIT
130 #undef LTTNG_PACKAGE_BUILD
131 #undef CREATE_TRACE_POINTS
132
133 struct trace_syscall_entry {
134 void *func;
135 const struct lttng_event_desc *desc;
136 const struct lttng_event_field *fields;
137 unsigned int nrargs;
138 };
139
140 #define CREATE_SYSCALL_TABLE
141
142 #undef TRACE_SYSCALL_TABLE
143 #define TRACE_SYSCALL_TABLE(_template, _name, _nr, _nrargs) \
144 [ _nr ] = { \
145 .func = __event_probe__##_template, \
146 .nrargs = (_nrargs), \
147 .fields = __event_fields___##_template, \
148 .desc = &__event_desc___##_name, \
149 },
150
151 static const struct trace_syscall_entry sc_table[] = {
152 #include "instrumentation/syscalls/headers/syscalls_integers.h"
153 #include "instrumentation/syscalls/headers/syscalls_pointers.h"
154 };
155
156 #undef TRACE_SYSCALL_TABLE
157 #define TRACE_SYSCALL_TABLE(_template, _name, _nr, _nrargs) \
158 [ _nr ] = { \
159 .func = __event_probe__##compat_##_template, \
160 .nrargs = (_nrargs), \
161 .fields = __event_fields___##compat_##_template,\
162 .desc = &__event_desc___##compat_##_name, \
163 },
164
165 /* Create compatibility syscall table */
166 const struct trace_syscall_entry compat_sc_table[] = {
167 #include "instrumentation/syscalls/headers/compat_syscalls_integers.h"
168 #include "instrumentation/syscalls/headers/compat_syscalls_pointers.h"
169 };
170
171 #undef CREATE_SYSCALL_TABLE
172
173 struct lttng_syscall_filter {
174 DECLARE_BITMAP(sc, NR_syscalls);
175 DECLARE_BITMAP(sc_compat, NR_compat_syscalls);
176 };
177
178 static void syscall_entry_unknown(struct lttng_event *event,
179 struct pt_regs *regs, unsigned int id)
180 {
181 unsigned long args[UNKNOWN_SYSCALL_NRARGS];
182
183 syscall_get_arguments(current, regs, 0, UNKNOWN_SYSCALL_NRARGS, args);
184 if (unlikely(is_compat_task()))
185 __event_probe__compat_sys_unknown(event, id, args);
186 else
187 __event_probe__sys_unknown(event, id, args);
188 }
189
190 void syscall_entry_probe(void *__data, struct pt_regs *regs, long id)
191 {
192 struct lttng_channel *chan = __data;
193 struct lttng_event *event, *unknown_event;
194 const struct trace_syscall_entry *table, *entry;
195 size_t table_len;
196
197 if (unlikely(is_compat_task())) {
198 struct lttng_syscall_filter *filter;
199
200 filter = rcu_dereference(chan->sc_filter);
201 if (filter) {
202 if (id >= NR_compat_syscalls
203 || !test_bit(id, filter->sc_compat)) {
204 /* System call filtered out. */
205 return;
206 }
207 }
208 table = compat_sc_table;
209 table_len = ARRAY_SIZE(compat_sc_table);
210 unknown_event = chan->sc_compat_unknown;
211 } else {
212 struct lttng_syscall_filter *filter;
213
214 filter = rcu_dereference(chan->sc_filter);
215 if (filter) {
216 if (id >= NR_syscalls
217 || !test_bit(id, filter->sc)) {
218 /* System call filtered out. */
219 return;
220 }
221 }
222 table = sc_table;
223 table_len = ARRAY_SIZE(sc_table);
224 unknown_event = chan->sc_unknown;
225 }
226 if (unlikely(id >= table_len)) {
227 syscall_entry_unknown(unknown_event, regs, id);
228 return;
229 }
230 if (unlikely(is_compat_task()))
231 event = chan->compat_sc_table[id];
232 else
233 event = chan->sc_table[id];
234 if (unlikely(!event)) {
235 syscall_entry_unknown(unknown_event, regs, id);
236 return;
237 }
238 entry = &table[id];
239 WARN_ON_ONCE(!entry);
240
241 switch (entry->nrargs) {
242 case 0:
243 {
244 void (*fptr)(void *__data) = entry->func;
245
246 fptr(event);
247 break;
248 }
249 case 1:
250 {
251 void (*fptr)(void *__data, unsigned long arg0) = entry->func;
252 unsigned long args[1];
253
254 syscall_get_arguments(current, regs, 0, entry->nrargs, args);
255 fptr(event, args[0]);
256 break;
257 }
258 case 2:
259 {
260 void (*fptr)(void *__data,
261 unsigned long arg0,
262 unsigned long arg1) = entry->func;
263 unsigned long args[2];
264
265 syscall_get_arguments(current, regs, 0, entry->nrargs, args);
266 fptr(event, args[0], args[1]);
267 break;
268 }
269 case 3:
270 {
271 void (*fptr)(void *__data,
272 unsigned long arg0,
273 unsigned long arg1,
274 unsigned long arg2) = entry->func;
275 unsigned long args[3];
276
277 syscall_get_arguments(current, regs, 0, entry->nrargs, args);
278 fptr(event, args[0], args[1], args[2]);
279 break;
280 }
281 case 4:
282 {
283 void (*fptr)(void *__data,
284 unsigned long arg0,
285 unsigned long arg1,
286 unsigned long arg2,
287 unsigned long arg3) = entry->func;
288 unsigned long args[4];
289
290 syscall_get_arguments(current, regs, 0, entry->nrargs, args);
291 fptr(event, args[0], args[1], args[2], args[3]);
292 break;
293 }
294 case 5:
295 {
296 void (*fptr)(void *__data,
297 unsigned long arg0,
298 unsigned long arg1,
299 unsigned long arg2,
300 unsigned long arg3,
301 unsigned long arg4) = entry->func;
302 unsigned long args[5];
303
304 syscall_get_arguments(current, regs, 0, entry->nrargs, args);
305 fptr(event, args[0], args[1], args[2], args[3], args[4]);
306 break;
307 }
308 case 6:
309 {
310 void (*fptr)(void *__data,
311 unsigned long arg0,
312 unsigned long arg1,
313 unsigned long arg2,
314 unsigned long arg3,
315 unsigned long arg4,
316 unsigned long arg5) = entry->func;
317 unsigned long args[6];
318
319 syscall_get_arguments(current, regs, 0, entry->nrargs, args);
320 fptr(event, args[0], args[1], args[2],
321 args[3], args[4], args[5]);
322 break;
323 }
324 default:
325 break;
326 }
327 }
328
329 /* noinline to diminish caller stack size */
330 static
331 int fill_table(const struct trace_syscall_entry *table, size_t table_len,
332 struct lttng_event **chan_table, struct lttng_channel *chan, void *filter)
333 {
334 const struct lttng_event_desc *desc;
335 unsigned int i;
336
337 /* Allocate events for each syscall, insert into table */
338 for (i = 0; i < table_len; i++) {
339 struct lttng_kernel_event ev;
340 desc = table[i].desc;
341
342 if (!desc) {
343 /* Unknown syscall */
344 continue;
345 }
346 /*
347 * Skip those already populated by previous failed
348 * register for this channel.
349 */
350 if (chan_table[i])
351 continue;
352 memset(&ev, 0, sizeof(ev));
353 strncpy(ev.name, desc->name, LTTNG_KERNEL_SYM_NAME_LEN);
354 ev.name[LTTNG_KERNEL_SYM_NAME_LEN - 1] = '\0';
355 ev.instrumentation = LTTNG_KERNEL_NOOP;
356 chan_table[i] = lttng_event_create(chan, &ev, filter,
357 desc);
358 WARN_ON_ONCE(!chan_table[i]);
359 if (IS_ERR(chan_table[i])) {
360 /*
361 * If something goes wrong in event registration
362 * after the first one, we have no choice but to
363 * leave the previous events in there, until
364 * deleted by session teardown.
365 */
366 return PTR_ERR(chan_table[i]);
367 }
368 }
369 return 0;
370 }
371
372 int lttng_syscalls_register(struct lttng_channel *chan, void *filter)
373 {
374 struct lttng_kernel_event ev;
375 int ret;
376
377 wrapper_vmalloc_sync_all();
378
379 if (!chan->sc_table) {
380 /* create syscall table mapping syscall to events */
381 chan->sc_table = kzalloc(sizeof(struct lttng_event *)
382 * ARRAY_SIZE(sc_table), GFP_KERNEL);
383 if (!chan->sc_table)
384 return -ENOMEM;
385 }
386
387 #ifdef CONFIG_COMPAT
388 if (!chan->compat_sc_table) {
389 /* create syscall table mapping compat syscall to events */
390 chan->compat_sc_table = kzalloc(sizeof(struct lttng_event *)
391 * ARRAY_SIZE(compat_sc_table), GFP_KERNEL);
392 if (!chan->compat_sc_table)
393 return -ENOMEM;
394 }
395 #endif
396 if (!chan->sc_unknown) {
397 const struct lttng_event_desc *desc =
398 &__event_desc___sys_unknown;
399
400 memset(&ev, 0, sizeof(ev));
401 strncpy(ev.name, desc->name, LTTNG_KERNEL_SYM_NAME_LEN);
402 ev.name[LTTNG_KERNEL_SYM_NAME_LEN - 1] = '\0';
403 ev.instrumentation = LTTNG_KERNEL_NOOP;
404 chan->sc_unknown = lttng_event_create(chan, &ev, filter,
405 desc);
406 WARN_ON_ONCE(!chan->sc_unknown);
407 if (IS_ERR(chan->sc_unknown)) {
408 return PTR_ERR(chan->sc_unknown);
409 }
410 }
411
412 if (!chan->sc_compat_unknown) {
413 const struct lttng_event_desc *desc =
414 &__event_desc___compat_sys_unknown;
415
416 memset(&ev, 0, sizeof(ev));
417 strncpy(ev.name, desc->name, LTTNG_KERNEL_SYM_NAME_LEN);
418 ev.name[LTTNG_KERNEL_SYM_NAME_LEN - 1] = '\0';
419 ev.instrumentation = LTTNG_KERNEL_NOOP;
420 chan->sc_compat_unknown = lttng_event_create(chan, &ev, filter,
421 desc);
422 WARN_ON_ONCE(!chan->sc_unknown);
423 if (IS_ERR(chan->sc_compat_unknown)) {
424 return PTR_ERR(chan->sc_compat_unknown);
425 }
426 }
427
428 if (!chan->sc_exit) {
429 const struct lttng_event_desc *desc =
430 &__event_desc___exit_syscall;
431
432 memset(&ev, 0, sizeof(ev));
433 strncpy(ev.name, desc->name, LTTNG_KERNEL_SYM_NAME_LEN);
434 ev.name[LTTNG_KERNEL_SYM_NAME_LEN - 1] = '\0';
435 ev.instrumentation = LTTNG_KERNEL_NOOP;
436 chan->sc_exit = lttng_event_create(chan, &ev, filter,
437 desc);
438 WARN_ON_ONCE(!chan->sc_exit);
439 if (IS_ERR(chan->sc_exit)) {
440 return PTR_ERR(chan->sc_exit);
441 }
442 }
443
444 ret = fill_table(sc_table, ARRAY_SIZE(sc_table),
445 chan->sc_table, chan, filter);
446 if (ret)
447 return ret;
448 #ifdef CONFIG_COMPAT
449 ret = fill_table(compat_sc_table, ARRAY_SIZE(compat_sc_table),
450 chan->compat_sc_table, chan, filter);
451 if (ret)
452 return ret;
453 #endif
454 if (!chan->sys_enter_registered) {
455 ret = lttng_wrapper_tracepoint_probe_register("sys_enter",
456 (void *) syscall_entry_probe, chan);
457 if (ret)
458 return ret;
459 chan->sys_enter_registered = 1;
460 }
461 /*
462 * We change the name of sys_exit tracepoint due to namespace
463 * conflict with sys_exit syscall entry.
464 */
465 if (!chan->sys_exit_registered) {
466 ret = lttng_wrapper_tracepoint_probe_register("sys_exit",
467 (void *) __event_probe__exit_syscall,
468 chan->sc_exit);
469 if (ret) {
470 WARN_ON_ONCE(lttng_wrapper_tracepoint_probe_unregister("sys_enter",
471 (void *) syscall_entry_probe, chan));
472 return ret;
473 }
474 chan->sys_exit_registered = 1;
475 }
476 return ret;
477 }
478
479 /*
480 * Only called at session destruction.
481 */
482 int lttng_syscalls_unregister(struct lttng_channel *chan)
483 {
484 int ret;
485
486 if (!chan->sc_table)
487 return 0;
488 if (chan->sys_enter_registered) {
489 ret = lttng_wrapper_tracepoint_probe_unregister("sys_exit",
490 (void *) __event_probe__exit_syscall,
491 chan->sc_exit);
492 if (ret)
493 return ret;
494 chan->sys_enter_registered = 0;
495 }
496 if (chan->sys_exit_registered) {
497 ret = lttng_wrapper_tracepoint_probe_unregister("sys_enter",
498 (void *) syscall_entry_probe, chan);
499 if (ret)
500 return ret;
501 chan->sys_exit_registered = 0;
502 }
503 /* lttng_event destroy will be performed by lttng_session_destroy() */
504 kfree(chan->sc_table);
505 #ifdef CONFIG_COMPAT
506 kfree(chan->compat_sc_table);
507 #endif
508 kfree(chan->sc_filter);
509 return 0;
510 }
511
512 static
513 int get_syscall_nr(const char *syscall_name)
514 {
515 int syscall_nr = -1;
516 int i;
517
518 for (i = 0; i < ARRAY_SIZE(sc_table); i++) {
519 const struct trace_syscall_entry *entry;
520
521 entry = &sc_table[i];
522 if (!entry->desc)
523 continue;
524 if (!strcmp(syscall_name, entry->desc->name)) {
525 syscall_nr = i;
526 break;
527 }
528 }
529 return syscall_nr;
530 }
531
532 static
533 int get_compat_syscall_nr(const char *syscall_name)
534 {
535 int syscall_nr = -1;
536 int i;
537
538 for (i = 0; i < ARRAY_SIZE(compat_sc_table); i++) {
539 const struct trace_syscall_entry *entry;
540
541 entry = &compat_sc_table[i];
542 if (!entry->desc)
543 continue;
544 if (!strcmp(syscall_name, entry->desc->name)) {
545 syscall_nr = i;
546 break;
547 }
548 }
549 return syscall_nr;
550 }
551
552 int lttng_syscall_filter_enable(struct lttng_channel *chan,
553 const char *name)
554 {
555 int syscall_nr, compat_syscall_nr, ret;
556 struct lttng_syscall_filter *filter;
557
558 WARN_ON_ONCE(!chan->sc_table);
559
560 if (!name) {
561 /* Enable all system calls by removing filter */
562 if (chan->sc_filter) {
563 filter = chan->sc_filter;
564 rcu_assign_pointer(chan->sc_filter, NULL);
565 synchronize_trace();
566 kfree(filter);
567 }
568 chan->syscall_all = 1;
569 return 0;
570 }
571
572 if (!chan->sc_filter) {
573 if (chan->syscall_all) {
574 /*
575 * All syscalls are already enabled.
576 */
577 return -EEXIST;
578 }
579 filter = kzalloc(sizeof(struct lttng_syscall_filter),
580 GFP_KERNEL);
581 if (!filter)
582 return -ENOMEM;
583 } else {
584 filter = chan->sc_filter;
585 }
586 syscall_nr = get_syscall_nr(name);
587 compat_syscall_nr = get_compat_syscall_nr(name);
588 if (syscall_nr < 0 && compat_syscall_nr < 0) {
589 ret = -ENOENT;
590 goto error;
591 }
592 if (syscall_nr >= 0) {
593 if (test_bit(syscall_nr, filter->sc)) {
594 ret = -EEXIST;
595 goto error;
596 }
597 bitmap_set(filter->sc, syscall_nr, 1);
598 }
599 if (compat_syscall_nr >= 0) {
600 if (test_bit(compat_syscall_nr, filter->sc_compat)) {
601 ret = -EEXIST;
602 goto error;
603 }
604 bitmap_set(filter->sc_compat, compat_syscall_nr, 1);
605 }
606 if (!chan->sc_filter)
607 rcu_assign_pointer(chan->sc_filter, filter);
608 return 0;
609
610 error:
611 if (!chan->sc_filter)
612 kfree(filter);
613 return ret;
614 }
615
616 int lttng_syscall_filter_disable(struct lttng_channel *chan,
617 const char *name)
618 {
619 int syscall_nr, compat_syscall_nr, ret;
620 struct lttng_syscall_filter *filter;
621
622 WARN_ON_ONCE(!chan->sc_table);
623
624 if (!chan->sc_filter) {
625 filter = kzalloc(sizeof(struct lttng_syscall_filter),
626 GFP_KERNEL);
627 if (!filter)
628 return -ENOMEM;
629 /* Trace all system calls, then apply disable. */
630 bitmap_set(filter->sc, 0, NR_syscalls);
631 bitmap_set(filter->sc_compat, 0, NR_compat_syscalls);
632 } else {
633 filter = chan->sc_filter;
634 }
635
636 syscall_nr = get_syscall_nr(name);
637 compat_syscall_nr = get_compat_syscall_nr(name);
638 if (syscall_nr < 0 && compat_syscall_nr < 0) {
639 ret = -ENOENT;
640 goto error;
641 }
642 if (syscall_nr >= 0) {
643 if (!test_bit(syscall_nr, chan->sc_filter->sc)) {
644 ret = -EEXIST;
645 goto error;
646 }
647 bitmap_clear(chan->sc_filter->sc, syscall_nr, 1);
648 }
649 if (compat_syscall_nr >= 0) {
650 if (!test_bit(compat_syscall_nr, chan->sc_filter->sc_compat)) {
651 ret = -EEXIST;
652 goto error;
653 }
654 bitmap_clear(chan->sc_filter->sc_compat, compat_syscall_nr, 1);
655 }
656 if (!chan->sc_filter)
657 rcu_assign_pointer(chan->sc_filter, filter);
658 chan->syscall_all = 0;
659 return 0;
660
661 error:
662 if (!chan->sc_filter)
663 kfree(filter);
664 return ret;
665 }
This page took 0.041777 seconds and 5 git commands to generate.