Implement array and sequence bitfields
[lttng-modules.git] / lttng-syscalls.c
... / ...
CommitLineData
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 <linux/in.h>
29#include <linux/in6.h>
30#include <linux/seq_file.h>
31#include <linux/stringify.h>
32#include <linux/file.h>
33#include <linux/anon_inodes.h>
34#include <asm/ptrace.h>
35#include <asm/syscall.h>
36
37#include "lib/bitfield.h"
38#include "wrapper/tracepoint.h"
39#include "wrapper/file.h"
40#include "wrapper/rcu.h"
41#include "lttng-events.h"
42
43#ifndef CONFIG_COMPAT
44# ifndef is_compat_task
45# define is_compat_task() (0)
46# endif
47#endif
48
49enum sc_type {
50 SC_TYPE_ENTRY,
51 SC_TYPE_EXIT,
52 SC_TYPE_COMPAT_ENTRY,
53 SC_TYPE_COMPAT_EXIT,
54};
55
56#define SYSCALL_ENTRY_TOK syscall_entry_
57#define COMPAT_SYSCALL_ENTRY_TOK compat_syscall_entry_
58#define SYSCALL_EXIT_TOK syscall_exit_
59#define COMPAT_SYSCALL_EXIT_TOK compat_syscall_exit_
60
61#define SYSCALL_ENTRY_STR __stringify(SYSCALL_ENTRY_TOK)
62#define COMPAT_SYSCALL_ENTRY_STR __stringify(COMPAT_SYSCALL_ENTRY_TOK)
63#define SYSCALL_EXIT_STR __stringify(SYSCALL_EXIT_TOK)
64#define COMPAT_SYSCALL_EXIT_STR __stringify(COMPAT_SYSCALL_EXIT_TOK)
65
66static
67void syscall_entry_probe(void *__data, struct pt_regs *regs, long id);
68static
69void syscall_exit_probe(void *__data, struct pt_regs *regs, long ret);
70
71/*
72 * Forward declarations for old kernels.
73 */
74struct mmsghdr;
75struct rlimit64;
76struct oldold_utsname;
77struct old_utsname;
78struct sel_arg_struct;
79struct mmap_arg_struct;
80struct file_handle;
81
82#ifdef IA32_NR_syscalls
83#define NR_compat_syscalls IA32_NR_syscalls
84#else
85#define NR_compat_syscalls NR_syscalls
86#endif
87
88/*
89 * Create LTTng tracepoint probes.
90 */
91#define LTTNG_PACKAGE_BUILD
92#define CREATE_TRACE_POINTS
93#define TP_MODULE_NOINIT
94#define TRACE_INCLUDE_PATH ../instrumentation/syscalls/headers
95
96#define PARAMS(args...) args
97
98/* Handle unknown syscalls */
99#undef TRACE_SYSTEM
100#define TRACE_SYSTEM syscalls_unknown
101#include "instrumentation/syscalls/headers/syscalls_unknown.h"
102#undef TRACE_SYSTEM
103
104#define SC_ENTER
105
106#undef sc_exit
107#define sc_exit(...)
108#undef sc_in
109#define sc_in(...) __VA_ARGS__
110#undef sc_out
111#define sc_out(...)
112#undef sc_inout
113#define sc_inout(...) __VA_ARGS__
114
115/* Hijack probe callback for system call enter */
116#undef TP_PROBE_CB
117#define TP_PROBE_CB(_template) &syscall_entry_probe
118#define SC_LTTNG_TRACEPOINT_EVENT(_name, _proto, _args, _fields) \
119 LTTNG_TRACEPOINT_EVENT(syscall_entry_##_name, PARAMS(_proto), PARAMS(_args), \
120 PARAMS(_fields))
121#define SC_LTTNG_TRACEPOINT_EVENT_CODE(_name, _proto, _args, _locvar, _code, _fields) \
122 LTTNG_TRACEPOINT_EVENT_CODE(syscall_entry_##_name, PARAMS(_proto), PARAMS(_args), \
123 PARAMS(_locvar), PARAMS(_code), \
124 PARAMS(_fields))
125#define SC_LTTNG_TRACEPOINT_EVENT_CLASS_NOARGS(_name, _fields) \
126 LTTNG_TRACEPOINT_EVENT_CLASS_NOARGS(syscall_entry_##_name, PARAMS(_fields))
127#define SC_LTTNG_TRACEPOINT_EVENT_INSTANCE_NOARGS(_template, _name) \
128 LTTNG_TRACEPOINT_EVENT_INSTANCE_NOARGS(syscall_entry_##_template, syscall_entry_##_name)
129#undef TRACE_SYSTEM
130#define TRACE_SYSTEM syscall_entry_integers
131#define TRACE_INCLUDE_FILE syscalls_integers
132#include "instrumentation/syscalls/headers/syscalls_integers.h"
133#undef TRACE_INCLUDE_FILE
134#undef TRACE_SYSTEM
135#define TRACE_SYSTEM syscall_entry_pointers
136#define TRACE_INCLUDE_FILE syscalls_pointers
137#include "instrumentation/syscalls/headers/syscalls_pointers.h"
138#undef TRACE_INCLUDE_FILE
139#undef TRACE_SYSTEM
140#undef SC_LTTNG_TRACEPOINT_EVENT_CODE
141#undef SC_LTTNG_TRACEPOINT_EVENT
142#undef SC_LTTNG_TRACEPOINT_EVENT_CLASS_NOARGS
143#undef SC_LTTNG_TRACEPOINT_EVENT_INSTANCE_NOARGS
144#undef TP_PROBE_CB
145#undef _TRACE_SYSCALLS_INTEGERS_H
146#undef _TRACE_SYSCALLS_POINTERS_H
147
148/* Hijack probe callback for compat system call enter */
149#define TP_PROBE_CB(_template) &syscall_entry_probe
150#define SC_LTTNG_TRACEPOINT_EVENT(_name, _proto, _args, _fields) \
151 LTTNG_TRACEPOINT_EVENT(compat_syscall_entry_##_name, PARAMS(_proto), PARAMS(_args), \
152 PARAMS(_fields))
153#define SC_LTTNG_TRACEPOINT_EVENT_CODE(_name, _proto, _args, _locvar, _code, _fields) \
154 LTTNG_TRACEPOINT_EVENT_CODE(compat_syscall_entry_##_name, PARAMS(_proto), PARAMS(_args), \
155 PARAMS(_locvar), PARAMS(_code), PARAMS(_fields))
156#define SC_LTTNG_TRACEPOINT_EVENT_CLASS_NOARGS(_name, _fields) \
157 LTTNG_TRACEPOINT_EVENT_CLASS_NOARGS(compat_syscall_entry_##_name, PARAMS(_fields))
158#define SC_LTTNG_TRACEPOINT_EVENT_INSTANCE_NOARGS(_template, _name) \
159 LTTNG_TRACEPOINT_EVENT_INSTANCE_NOARGS(compat_syscall_entry_##_template, \
160 compat_syscall_entry_##_name)
161#define TRACE_SYSTEM compat_syscall_entry_integers
162#define TRACE_INCLUDE_FILE compat_syscalls_integers
163#include "instrumentation/syscalls/headers/compat_syscalls_integers.h"
164#undef TRACE_INCLUDE_FILE
165#undef TRACE_SYSTEM
166#define TRACE_SYSTEM compat_syscall_entry_pointers
167#define TRACE_INCLUDE_FILE compat_syscalls_pointers
168#include "instrumentation/syscalls/headers/compat_syscalls_pointers.h"
169#undef TRACE_INCLUDE_FILE
170#undef TRACE_SYSTEM
171#undef SC_LTTNG_TRACEPOINT_EVENT_CODE
172#undef SC_LTTNG_TRACEPOINT_EVENT
173#undef SC_LTTNG_TRACEPOINT_EVENT_CLASS_NOARGS
174#undef SC_LTTNG_TRACEPOINT_EVENT_INSTANCE_NOARGS
175#undef TP_PROBE_CB
176#undef _TRACE_SYSCALLS_INTEGERS_H
177#undef _TRACE_SYSCALLS_POINTERS_H
178
179#undef SC_ENTER
180
181#define SC_EXIT
182
183#undef sc_exit
184#define sc_exit(...) __VA_ARGS__
185#undef sc_in
186#define sc_in(...)
187#undef sc_out
188#define sc_out(...) __VA_ARGS__
189#undef sc_inout
190#define sc_inout(...) __VA_ARGS__
191
192/* Hijack probe callback for system call exit */
193#define TP_PROBE_CB(_template) &syscall_exit_probe
194#define SC_LTTNG_TRACEPOINT_EVENT(_name, _proto, _args, _fields) \
195 LTTNG_TRACEPOINT_EVENT(syscall_exit_##_name, PARAMS(_proto), PARAMS(_args), \
196 PARAMS(_fields))
197#define SC_LTTNG_TRACEPOINT_EVENT_CODE(_name, _proto, _args, _locvar, _code, _fields) \
198 LTTNG_TRACEPOINT_EVENT_CODE(syscall_exit_##_name, PARAMS(_proto), PARAMS(_args), \
199 PARAMS(_locvar), PARAMS(_code), PARAMS(_fields))
200#define SC_LTTNG_TRACEPOINT_EVENT_CLASS_NOARGS(_name, _fields) \
201 LTTNG_TRACEPOINT_EVENT_CLASS_NOARGS(syscall_exit_##_name, PARAMS(_fields))
202#define SC_LTTNG_TRACEPOINT_EVENT_INSTANCE_NOARGS(_template, _name) \
203 LTTNG_TRACEPOINT_EVENT_INSTANCE_NOARGS(syscall_exit_##_template, \
204 syscall_exit_##_name)
205#define TRACE_SYSTEM syscall_exit_integers
206#define TRACE_INCLUDE_FILE syscalls_integers
207#include "instrumentation/syscalls/headers/syscalls_integers.h"
208#undef TRACE_INCLUDE_FILE
209#undef TRACE_SYSTEM
210#define TRACE_SYSTEM syscall_exit_pointers
211#define TRACE_INCLUDE_FILE syscalls_pointers
212#include "instrumentation/syscalls/headers/syscalls_pointers.h"
213#undef TRACE_INCLUDE_FILE
214#undef TRACE_SYSTEM
215#undef SC_LTTNG_TRACEPOINT_EVENT_CODE
216#undef SC_LTTNG_TRACEPOINT_EVENT
217#undef SC_LTTNG_TRACEPOINT_EVENT_CLASS_NOARGS
218#undef SC_LTTNG_TRACEPOINT_EVENT_INSTANCE_NOARGS
219#undef TP_PROBE_CB
220#undef _TRACE_SYSCALLS_INTEGERS_H
221#undef _TRACE_SYSCALLS_POINTERS_H
222
223
224/* Hijack probe callback for compat system call exit */
225#define TP_PROBE_CB(_template) &syscall_exit_probe
226#define SC_LTTNG_TRACEPOINT_EVENT(_name, _proto, _args, _fields) \
227 LTTNG_TRACEPOINT_EVENT(compat_syscall_exit_##_name, PARAMS(_proto), PARAMS(_args), \
228 PARAMS(_fields))
229#define SC_LTTNG_TRACEPOINT_EVENT_CODE(_name, _proto, _args, _locvar, _code, _fields) \
230 LTTNG_TRACEPOINT_EVENT_CODE(compat_syscall_exit_##_name, PARAMS(_proto), PARAMS(_args), \
231 PARAMS(_locvar), PARAMS(_code), PARAMS(_fields))
232#define SC_LTTNG_TRACEPOINT_EVENT_CLASS_NOARGS(_name, _fields) \
233 LTTNG_TRACEPOINT_EVENT_CLASS_NOARGS(compat_syscall_exit_##_name, PARAMS(_fields))
234#define SC_LTTNG_TRACEPOINT_EVENT_INSTANCE_NOARGS(_template, _name) \
235 LTTNG_TRACEPOINT_EVENT_INSTANCE_NOARGS(compat_syscall_exit_##_template, \
236 compat_syscall_exit_##_name)
237#define TRACE_SYSTEM compat_syscall_exit_integers
238#define TRACE_INCLUDE_FILE compat_syscalls_integers
239#include "instrumentation/syscalls/headers/compat_syscalls_integers.h"
240#undef TRACE_INCLUDE_FILE
241#undef TRACE_SYSTEM
242#define TRACE_SYSTEM compat_syscall_exit_pointers
243#define TRACE_INCLUDE_FILE compat_syscalls_pointers
244#include "instrumentation/syscalls/headers/compat_syscalls_pointers.h"
245#undef TRACE_INCLUDE_FILE
246#undef TRACE_SYSTEM
247#undef SC_LTTNG_TRACEPOINT_EVENT_CODE
248#undef SC_LTTNG_TRACEPOINT_EVENT
249#undef SC_LTTNG_TRACEPOINT_EVENT_CLASS_NOARGS
250#undef SC_LTTNG_TRACEPOINT_EVENT_INSTANCE_NOARGS
251#undef TP_PROBE_CB
252#undef _TRACE_SYSCALLS_INTEGERS_H
253#undef _TRACE_SYSCALLS_POINTERS_H
254
255#undef SC_EXIT
256
257#undef TP_MODULE_NOINIT
258#undef LTTNG_PACKAGE_BUILD
259#undef CREATE_TRACE_POINTS
260
261struct trace_syscall_entry {
262 void *func;
263 const struct lttng_event_desc *desc;
264 const struct lttng_event_field *fields;
265 unsigned int nrargs;
266};
267
268#define CREATE_SYSCALL_TABLE
269
270#define SC_ENTER
271
272#undef sc_exit
273#define sc_exit(...)
274
275#undef TRACE_SYSCALL_TABLE
276#define TRACE_SYSCALL_TABLE(_template, _name, _nr, _nrargs) \
277 [ _nr ] = { \
278 .func = __event_probe__syscall_entry_##_template, \
279 .nrargs = (_nrargs), \
280 .fields = __event_fields___syscall_entry_##_template, \
281 .desc = &__event_desc___syscall_entry_##_name, \
282 },
283
284/* Syscall enter tracing table */
285static const struct trace_syscall_entry sc_table[] = {
286#include "instrumentation/syscalls/headers/syscalls_integers.h"
287#include "instrumentation/syscalls/headers/syscalls_pointers.h"
288};
289
290#undef TRACE_SYSCALL_TABLE
291#define TRACE_SYSCALL_TABLE(_template, _name, _nr, _nrargs) \
292 [ _nr ] = { \
293 .func = __event_probe__compat_syscall_entry_##_template, \
294 .nrargs = (_nrargs), \
295 .fields = __event_fields___compat_syscall_entry_##_template, \
296 .desc = &__event_desc___compat_syscall_entry_##_name, \
297 },
298
299/* Compat syscall enter table */
300const struct trace_syscall_entry compat_sc_table[] = {
301#include "instrumentation/syscalls/headers/compat_syscalls_integers.h"
302#include "instrumentation/syscalls/headers/compat_syscalls_pointers.h"
303};
304
305#undef SC_ENTER
306
307#define SC_EXIT
308
309#undef sc_exit
310#define sc_exit(...) __VA_ARGS__
311
312#undef TRACE_SYSCALL_TABLE
313#define TRACE_SYSCALL_TABLE(_template, _name, _nr, _nrargs) \
314 [ _nr ] = { \
315 .func = __event_probe__syscall_exit_##_template, \
316 .nrargs = (_nrargs), \
317 .fields = __event_fields___syscall_exit_##_template, \
318 .desc = &__event_desc___syscall_exit_##_name, \
319 },
320
321/* Syscall exit table */
322static const struct trace_syscall_entry sc_exit_table[] = {
323#include "instrumentation/syscalls/headers/syscalls_integers.h"
324#include "instrumentation/syscalls/headers/syscalls_pointers.h"
325};
326
327#undef TRACE_SYSCALL_TABLE
328#define TRACE_SYSCALL_TABLE(_template, _name, _nr, _nrargs) \
329 [ _nr ] = { \
330 .func = __event_probe__compat_syscall_exit_##_template, \
331 .nrargs = (_nrargs), \
332 .fields = __event_fields___compat_syscall_exit_##_template, \
333 .desc = &__event_desc___compat_syscall_exit_##_name, \
334 },
335
336/* Compat syscall exit table */
337const struct trace_syscall_entry compat_sc_exit_table[] = {
338#include "instrumentation/syscalls/headers/compat_syscalls_integers.h"
339#include "instrumentation/syscalls/headers/compat_syscalls_pointers.h"
340};
341
342#undef SC_EXIT
343
344#undef CREATE_SYSCALL_TABLE
345
346struct lttng_syscall_filter {
347 DECLARE_BITMAP(sc, NR_syscalls);
348 DECLARE_BITMAP(sc_compat, NR_compat_syscalls);
349};
350
351static void syscall_entry_unknown(struct lttng_event *event,
352 struct pt_regs *regs, unsigned int id)
353{
354 unsigned long args[UNKNOWN_SYSCALL_NRARGS];
355
356 syscall_get_arguments(current, regs, 0, UNKNOWN_SYSCALL_NRARGS, args);
357 if (unlikely(is_compat_task()))
358 __event_probe__compat_syscall_entry_unknown(event, id, args);
359 else
360 __event_probe__syscall_entry_unknown(event, id, args);
361}
362
363void syscall_entry_probe(void *__data, struct pt_regs *regs, long id)
364{
365 struct lttng_channel *chan = __data;
366 struct lttng_event *event, *unknown_event;
367 const struct trace_syscall_entry *table, *entry;
368 size_t table_len;
369
370 if (unlikely(is_compat_task())) {
371 struct lttng_syscall_filter *filter;
372
373 filter = lttng_rcu_dereference(chan->sc_filter);
374 if (filter) {
375 if (id < 0 || id >= NR_compat_syscalls
376 || !test_bit(id, filter->sc_compat)) {
377 /* System call filtered out. */
378 return;
379 }
380 }
381 table = compat_sc_table;
382 table_len = ARRAY_SIZE(compat_sc_table);
383 unknown_event = chan->sc_compat_unknown;
384 } else {
385 struct lttng_syscall_filter *filter;
386
387 filter = lttng_rcu_dereference(chan->sc_filter);
388 if (filter) {
389 if (id < 0 || id >= NR_syscalls
390 || !test_bit(id, filter->sc)) {
391 /* System call filtered out. */
392 return;
393 }
394 }
395 table = sc_table;
396 table_len = ARRAY_SIZE(sc_table);
397 unknown_event = chan->sc_unknown;
398 }
399 if (unlikely(id < 0 || id >= table_len)) {
400 syscall_entry_unknown(unknown_event, regs, id);
401 return;
402 }
403 if (unlikely(is_compat_task()))
404 event = chan->compat_sc_table[id];
405 else
406 event = chan->sc_table[id];
407 if (unlikely(!event)) {
408 syscall_entry_unknown(unknown_event, regs, id);
409 return;
410 }
411 entry = &table[id];
412 WARN_ON_ONCE(!entry);
413
414 switch (entry->nrargs) {
415 case 0:
416 {
417 void (*fptr)(void *__data) = entry->func;
418
419 fptr(event);
420 break;
421 }
422 case 1:
423 {
424 void (*fptr)(void *__data, unsigned long arg0) = entry->func;
425 unsigned long args[1];
426
427 syscall_get_arguments(current, regs, 0, entry->nrargs, args);
428 fptr(event, args[0]);
429 break;
430 }
431 case 2:
432 {
433 void (*fptr)(void *__data,
434 unsigned long arg0,
435 unsigned long arg1) = entry->func;
436 unsigned long args[2];
437
438 syscall_get_arguments(current, regs, 0, entry->nrargs, args);
439 fptr(event, args[0], args[1]);
440 break;
441 }
442 case 3:
443 {
444 void (*fptr)(void *__data,
445 unsigned long arg0,
446 unsigned long arg1,
447 unsigned long arg2) = entry->func;
448 unsigned long args[3];
449
450 syscall_get_arguments(current, regs, 0, entry->nrargs, args);
451 fptr(event, args[0], args[1], args[2]);
452 break;
453 }
454 case 4:
455 {
456 void (*fptr)(void *__data,
457 unsigned long arg0,
458 unsigned long arg1,
459 unsigned long arg2,
460 unsigned long arg3) = entry->func;
461 unsigned long args[4];
462
463 syscall_get_arguments(current, regs, 0, entry->nrargs, args);
464 fptr(event, args[0], args[1], args[2], args[3]);
465 break;
466 }
467 case 5:
468 {
469 void (*fptr)(void *__data,
470 unsigned long arg0,
471 unsigned long arg1,
472 unsigned long arg2,
473 unsigned long arg3,
474 unsigned long arg4) = entry->func;
475 unsigned long args[5];
476
477 syscall_get_arguments(current, regs, 0, entry->nrargs, args);
478 fptr(event, args[0], args[1], args[2], args[3], args[4]);
479 break;
480 }
481 case 6:
482 {
483 void (*fptr)(void *__data,
484 unsigned long arg0,
485 unsigned long arg1,
486 unsigned long arg2,
487 unsigned long arg3,
488 unsigned long arg4,
489 unsigned long arg5) = entry->func;
490 unsigned long args[6];
491
492 syscall_get_arguments(current, regs, 0, entry->nrargs, args);
493 fptr(event, args[0], args[1], args[2],
494 args[3], args[4], args[5]);
495 break;
496 }
497 default:
498 break;
499 }
500}
501
502static void syscall_exit_unknown(struct lttng_event *event,
503 struct pt_regs *regs, int id, long ret)
504{
505 unsigned long args[UNKNOWN_SYSCALL_NRARGS];
506
507 syscall_get_arguments(current, regs, 0, UNKNOWN_SYSCALL_NRARGS, args);
508 if (unlikely(is_compat_task()))
509 __event_probe__compat_syscall_exit_unknown(event, id, ret,
510 args);
511 else
512 __event_probe__syscall_exit_unknown(event, id, ret, args);
513}
514
515void syscall_exit_probe(void *__data, struct pt_regs *regs, long ret)
516{
517 struct lttng_channel *chan = __data;
518 struct lttng_event *event, *unknown_event;
519 const struct trace_syscall_entry *table, *entry;
520 size_t table_len;
521 long id;
522
523 id = syscall_get_nr(current, regs);
524 if (unlikely(is_compat_task())) {
525 struct lttng_syscall_filter *filter;
526
527 filter = lttng_rcu_dereference(chan->sc_filter);
528 if (filter) {
529 if (id < 0 || id >= NR_compat_syscalls
530 || !test_bit(id, filter->sc_compat)) {
531 /* System call filtered out. */
532 return;
533 }
534 }
535 table = compat_sc_exit_table;
536 table_len = ARRAY_SIZE(compat_sc_exit_table);
537 unknown_event = chan->compat_sc_exit_unknown;
538 } else {
539 struct lttng_syscall_filter *filter;
540
541 filter = lttng_rcu_dereference(chan->sc_filter);
542 if (filter) {
543 if (id < 0 || id >= NR_syscalls
544 || !test_bit(id, filter->sc)) {
545 /* System call filtered out. */
546 return;
547 }
548 }
549 table = sc_exit_table;
550 table_len = ARRAY_SIZE(sc_exit_table);
551 unknown_event = chan->sc_exit_unknown;
552 }
553 if (unlikely(id < 0 || id >= table_len)) {
554 syscall_exit_unknown(unknown_event, regs, id, ret);
555 return;
556 }
557 if (unlikely(is_compat_task()))
558 event = chan->compat_sc_exit_table[id];
559 else
560 event = chan->sc_exit_table[id];
561 if (unlikely(!event)) {
562 syscall_exit_unknown(unknown_event, regs, id, ret);
563 return;
564 }
565 entry = &table[id];
566 WARN_ON_ONCE(!entry);
567
568 switch (entry->nrargs) {
569 case 0:
570 {
571 void (*fptr)(void *__data, long ret) = entry->func;
572
573 fptr(event, ret);
574 break;
575 }
576 case 1:
577 {
578 void (*fptr)(void *__data,
579 long ret,
580 unsigned long arg0) = entry->func;
581 unsigned long args[1];
582
583 syscall_get_arguments(current, regs, 0, entry->nrargs, args);
584 fptr(event, ret, args[0]);
585 break;
586 }
587 case 2:
588 {
589 void (*fptr)(void *__data,
590 long ret,
591 unsigned long arg0,
592 unsigned long arg1) = entry->func;
593 unsigned long args[2];
594
595 syscall_get_arguments(current, regs, 0, entry->nrargs, args);
596 fptr(event, ret, args[0], args[1]);
597 break;
598 }
599 case 3:
600 {
601 void (*fptr)(void *__data,
602 long ret,
603 unsigned long arg0,
604 unsigned long arg1,
605 unsigned long arg2) = entry->func;
606 unsigned long args[3];
607
608 syscall_get_arguments(current, regs, 0, entry->nrargs, args);
609 fptr(event, ret, args[0], args[1], args[2]);
610 break;
611 }
612 case 4:
613 {
614 void (*fptr)(void *__data,
615 long ret,
616 unsigned long arg0,
617 unsigned long arg1,
618 unsigned long arg2,
619 unsigned long arg3) = entry->func;
620 unsigned long args[4];
621
622 syscall_get_arguments(current, regs, 0, entry->nrargs, args);
623 fptr(event, ret, args[0], args[1], args[2], args[3]);
624 break;
625 }
626 case 5:
627 {
628 void (*fptr)(void *__data,
629 long ret,
630 unsigned long arg0,
631 unsigned long arg1,
632 unsigned long arg2,
633 unsigned long arg3,
634 unsigned long arg4) = entry->func;
635 unsigned long args[5];
636
637 syscall_get_arguments(current, regs, 0, entry->nrargs, args);
638 fptr(event, ret, args[0], args[1], args[2], args[3], args[4]);
639 break;
640 }
641 case 6:
642 {
643 void (*fptr)(void *__data,
644 long ret,
645 unsigned long arg0,
646 unsigned long arg1,
647 unsigned long arg2,
648 unsigned long arg3,
649 unsigned long arg4,
650 unsigned long arg5) = entry->func;
651 unsigned long args[6];
652
653 syscall_get_arguments(current, regs, 0, entry->nrargs, args);
654 fptr(event, ret, args[0], args[1], args[2],
655 args[3], args[4], args[5]);
656 break;
657 }
658 default:
659 break;
660 }
661}
662
663/*
664 * noinline to diminish caller stack size.
665 * Should be called with sessions lock held.
666 */
667static
668int fill_table(const struct trace_syscall_entry *table, size_t table_len,
669 struct lttng_event **chan_table, struct lttng_channel *chan,
670 void *filter, enum sc_type type)
671{
672 const struct lttng_event_desc *desc;
673 unsigned int i;
674
675 /* Allocate events for each syscall, insert into table */
676 for (i = 0; i < table_len; i++) {
677 struct lttng_kernel_event ev;
678 desc = table[i].desc;
679
680 if (!desc) {
681 /* Unknown syscall */
682 continue;
683 }
684 /*
685 * Skip those already populated by previous failed
686 * register for this channel.
687 */
688 if (chan_table[i])
689 continue;
690 memset(&ev, 0, sizeof(ev));
691 switch (type) {
692 case SC_TYPE_ENTRY:
693 strncpy(ev.name, SYSCALL_ENTRY_STR,
694 LTTNG_KERNEL_SYM_NAME_LEN);
695 break;
696 case SC_TYPE_EXIT:
697 strncpy(ev.name, SYSCALL_EXIT_STR,
698 LTTNG_KERNEL_SYM_NAME_LEN);
699 break;
700 case SC_TYPE_COMPAT_ENTRY:
701 strncpy(ev.name, COMPAT_SYSCALL_ENTRY_STR,
702 LTTNG_KERNEL_SYM_NAME_LEN);
703 break;
704 case SC_TYPE_COMPAT_EXIT:
705 strncpy(ev.name, COMPAT_SYSCALL_EXIT_STR,
706 LTTNG_KERNEL_SYM_NAME_LEN);
707 break;
708 default:
709 BUG_ON(1);
710 break;
711 }
712 strncat(ev.name, desc->name,
713 LTTNG_KERNEL_SYM_NAME_LEN - strlen(ev.name) - 1);
714 ev.name[LTTNG_KERNEL_SYM_NAME_LEN - 1] = '\0';
715 ev.instrumentation = LTTNG_KERNEL_SYSCALL;
716 chan_table[i] = _lttng_event_create(chan, &ev, filter,
717 desc, ev.instrumentation);
718 WARN_ON_ONCE(!chan_table[i]);
719 if (IS_ERR(chan_table[i])) {
720 /*
721 * If something goes wrong in event registration
722 * after the first one, we have no choice but to
723 * leave the previous events in there, until
724 * deleted by session teardown.
725 */
726 return PTR_ERR(chan_table[i]);
727 }
728 }
729 return 0;
730}
731
732/*
733 * Should be called with sessions lock held.
734 */
735int lttng_syscalls_register(struct lttng_channel *chan, void *filter)
736{
737 struct lttng_kernel_event ev;
738 int ret;
739
740 wrapper_vmalloc_sync_all();
741
742 if (!chan->sc_table) {
743 /* create syscall table mapping syscall to events */
744 chan->sc_table = kzalloc(sizeof(struct lttng_event *)
745 * ARRAY_SIZE(sc_table), GFP_KERNEL);
746 if (!chan->sc_table)
747 return -ENOMEM;
748 }
749 if (!chan->sc_exit_table) {
750 /* create syscall table mapping syscall to events */
751 chan->sc_exit_table = kzalloc(sizeof(struct lttng_event *)
752 * ARRAY_SIZE(sc_exit_table), GFP_KERNEL);
753 if (!chan->sc_exit_table)
754 return -ENOMEM;
755 }
756
757
758#ifdef CONFIG_COMPAT
759 if (!chan->compat_sc_table) {
760 /* create syscall table mapping compat syscall to events */
761 chan->compat_sc_table = kzalloc(sizeof(struct lttng_event *)
762 * ARRAY_SIZE(compat_sc_table), GFP_KERNEL);
763 if (!chan->compat_sc_table)
764 return -ENOMEM;
765 }
766
767 if (!chan->compat_sc_exit_table) {
768 /* create syscall table mapping compat syscall to events */
769 chan->compat_sc_exit_table = kzalloc(sizeof(struct lttng_event *)
770 * ARRAY_SIZE(compat_sc_exit_table), GFP_KERNEL);
771 if (!chan->compat_sc_exit_table)
772 return -ENOMEM;
773 }
774#endif
775 if (!chan->sc_unknown) {
776 const struct lttng_event_desc *desc =
777 &__event_desc___syscall_entry_unknown;
778
779 memset(&ev, 0, sizeof(ev));
780 strncpy(ev.name, desc->name, LTTNG_KERNEL_SYM_NAME_LEN);
781 ev.name[LTTNG_KERNEL_SYM_NAME_LEN - 1] = '\0';
782 ev.instrumentation = LTTNG_KERNEL_SYSCALL;
783 chan->sc_unknown = _lttng_event_create(chan, &ev, filter,
784 desc,
785 ev.instrumentation);
786 WARN_ON_ONCE(!chan->sc_unknown);
787 if (IS_ERR(chan->sc_unknown)) {
788 return PTR_ERR(chan->sc_unknown);
789 }
790 }
791
792 if (!chan->sc_compat_unknown) {
793 const struct lttng_event_desc *desc =
794 &__event_desc___compat_syscall_entry_unknown;
795
796 memset(&ev, 0, sizeof(ev));
797 strncpy(ev.name, desc->name, LTTNG_KERNEL_SYM_NAME_LEN);
798 ev.name[LTTNG_KERNEL_SYM_NAME_LEN - 1] = '\0';
799 ev.instrumentation = LTTNG_KERNEL_SYSCALL;
800 chan->sc_compat_unknown = _lttng_event_create(chan, &ev, filter,
801 desc,
802 ev.instrumentation);
803 WARN_ON_ONCE(!chan->sc_unknown);
804 if (IS_ERR(chan->sc_compat_unknown)) {
805 return PTR_ERR(chan->sc_compat_unknown);
806 }
807 }
808
809 if (!chan->compat_sc_exit_unknown) {
810 const struct lttng_event_desc *desc =
811 &__event_desc___compat_syscall_exit_unknown;
812
813 memset(&ev, 0, sizeof(ev));
814 strncpy(ev.name, desc->name, LTTNG_KERNEL_SYM_NAME_LEN);
815 ev.name[LTTNG_KERNEL_SYM_NAME_LEN - 1] = '\0';
816 ev.instrumentation = LTTNG_KERNEL_SYSCALL;
817 chan->compat_sc_exit_unknown = _lttng_event_create(chan, &ev,
818 filter, desc,
819 ev.instrumentation);
820 WARN_ON_ONCE(!chan->compat_sc_exit_unknown);
821 if (IS_ERR(chan->compat_sc_exit_unknown)) {
822 return PTR_ERR(chan->compat_sc_exit_unknown);
823 }
824 }
825
826 if (!chan->sc_exit_unknown) {
827 const struct lttng_event_desc *desc =
828 &__event_desc___syscall_exit_unknown;
829
830 memset(&ev, 0, sizeof(ev));
831 strncpy(ev.name, desc->name, LTTNG_KERNEL_SYM_NAME_LEN);
832 ev.name[LTTNG_KERNEL_SYM_NAME_LEN - 1] = '\0';
833 ev.instrumentation = LTTNG_KERNEL_SYSCALL;
834 chan->sc_exit_unknown = _lttng_event_create(chan, &ev, filter,
835 desc, ev.instrumentation);
836 WARN_ON_ONCE(!chan->sc_exit_unknown);
837 if (IS_ERR(chan->sc_exit_unknown)) {
838 return PTR_ERR(chan->sc_exit_unknown);
839 }
840 }
841
842 ret = fill_table(sc_table, ARRAY_SIZE(sc_table),
843 chan->sc_table, chan, filter, SC_TYPE_ENTRY);
844 if (ret)
845 return ret;
846 ret = fill_table(sc_exit_table, ARRAY_SIZE(sc_exit_table),
847 chan->sc_exit_table, chan, filter, SC_TYPE_EXIT);
848 if (ret)
849 return ret;
850
851#ifdef CONFIG_COMPAT
852 ret = fill_table(compat_sc_table, ARRAY_SIZE(compat_sc_table),
853 chan->compat_sc_table, chan, filter,
854 SC_TYPE_COMPAT_ENTRY);
855 if (ret)
856 return ret;
857 ret = fill_table(compat_sc_exit_table, ARRAY_SIZE(compat_sc_exit_table),
858 chan->compat_sc_exit_table, chan, filter,
859 SC_TYPE_COMPAT_EXIT);
860 if (ret)
861 return ret;
862#endif
863 if (!chan->sys_enter_registered) {
864 ret = lttng_wrapper_tracepoint_probe_register("sys_enter",
865 (void *) syscall_entry_probe, chan);
866 if (ret)
867 return ret;
868 chan->sys_enter_registered = 1;
869 }
870 /*
871 * We change the name of sys_exit tracepoint due to namespace
872 * conflict with sys_exit syscall entry.
873 */
874 if (!chan->sys_exit_registered) {
875 ret = lttng_wrapper_tracepoint_probe_register("sys_exit",
876 (void *) syscall_exit_probe, chan);
877 if (ret) {
878 WARN_ON_ONCE(lttng_wrapper_tracepoint_probe_unregister("sys_enter",
879 (void *) syscall_entry_probe, chan));
880 return ret;
881 }
882 chan->sys_exit_registered = 1;
883 }
884 return ret;
885}
886
887/*
888 * Only called at session destruction.
889 */
890int lttng_syscalls_unregister(struct lttng_channel *chan)
891{
892 int ret;
893
894 if (!chan->sc_table)
895 return 0;
896 if (chan->sys_enter_registered) {
897 ret = lttng_wrapper_tracepoint_probe_unregister("sys_exit",
898 (void *) syscall_exit_probe, chan);
899 if (ret)
900 return ret;
901 chan->sys_enter_registered = 0;
902 }
903 if (chan->sys_exit_registered) {
904 ret = lttng_wrapper_tracepoint_probe_unregister("sys_enter",
905 (void *) syscall_entry_probe, chan);
906 if (ret)
907 return ret;
908 chan->sys_exit_registered = 0;
909 }
910 /* lttng_event destroy will be performed by lttng_session_destroy() */
911 kfree(chan->sc_table);
912 kfree(chan->sc_exit_table);
913#ifdef CONFIG_COMPAT
914 kfree(chan->compat_sc_table);
915 kfree(chan->compat_sc_exit_table);
916#endif
917 kfree(chan->sc_filter);
918 return 0;
919}
920
921static
922int get_syscall_nr(const char *syscall_name)
923{
924 int syscall_nr = -1;
925 int i;
926
927 for (i = 0; i < ARRAY_SIZE(sc_table); i++) {
928 const struct trace_syscall_entry *entry;
929 const char *it_name;
930
931 entry = &sc_table[i];
932 if (!entry->desc)
933 continue;
934 it_name = entry->desc->name;
935 it_name += strlen(SYSCALL_ENTRY_STR);
936 if (!strcmp(syscall_name, it_name)) {
937 syscall_nr = i;
938 break;
939 }
940 }
941 return syscall_nr;
942}
943
944static
945int get_compat_syscall_nr(const char *syscall_name)
946{
947 int syscall_nr = -1;
948 int i;
949
950 for (i = 0; i < ARRAY_SIZE(compat_sc_table); i++) {
951 const struct trace_syscall_entry *entry;
952 const char *it_name;
953
954 entry = &compat_sc_table[i];
955 if (!entry->desc)
956 continue;
957 it_name = entry->desc->name;
958 it_name += strlen(COMPAT_SYSCALL_ENTRY_STR);
959 if (!strcmp(syscall_name, it_name)) {
960 syscall_nr = i;
961 break;
962 }
963 }
964 return syscall_nr;
965}
966
967static
968uint32_t get_sc_tables_len(void)
969{
970 return ARRAY_SIZE(sc_table) + ARRAY_SIZE(compat_sc_table);
971}
972
973int lttng_syscall_filter_enable(struct lttng_channel *chan,
974 const char *name)
975{
976 int syscall_nr, compat_syscall_nr, ret;
977 struct lttng_syscall_filter *filter;
978
979 WARN_ON_ONCE(!chan->sc_table);
980
981 if (!name) {
982 /* Enable all system calls by removing filter */
983 if (chan->sc_filter) {
984 filter = chan->sc_filter;
985 rcu_assign_pointer(chan->sc_filter, NULL);
986 synchronize_trace();
987 kfree(filter);
988 }
989 chan->syscall_all = 1;
990 return 0;
991 }
992
993 if (!chan->sc_filter) {
994 if (chan->syscall_all) {
995 /*
996 * All syscalls are already enabled.
997 */
998 return -EEXIST;
999 }
1000 filter = kzalloc(sizeof(struct lttng_syscall_filter),
1001 GFP_KERNEL);
1002 if (!filter)
1003 return -ENOMEM;
1004 } else {
1005 filter = chan->sc_filter;
1006 }
1007 syscall_nr = get_syscall_nr(name);
1008 compat_syscall_nr = get_compat_syscall_nr(name);
1009 if (syscall_nr < 0 && compat_syscall_nr < 0) {
1010 ret = -ENOENT;
1011 goto error;
1012 }
1013 if (syscall_nr >= 0) {
1014 if (test_bit(syscall_nr, filter->sc)) {
1015 ret = -EEXIST;
1016 goto error;
1017 }
1018 bitmap_set(filter->sc, syscall_nr, 1);
1019 }
1020 if (compat_syscall_nr >= 0) {
1021 if (test_bit(compat_syscall_nr, filter->sc_compat)) {
1022 ret = -EEXIST;
1023 goto error;
1024 }
1025 bitmap_set(filter->sc_compat, compat_syscall_nr, 1);
1026 }
1027 if (!chan->sc_filter)
1028 rcu_assign_pointer(chan->sc_filter, filter);
1029 return 0;
1030
1031error:
1032 if (!chan->sc_filter)
1033 kfree(filter);
1034 return ret;
1035}
1036
1037int lttng_syscall_filter_disable(struct lttng_channel *chan,
1038 const char *name)
1039{
1040 int syscall_nr, compat_syscall_nr, ret;
1041 struct lttng_syscall_filter *filter;
1042
1043 WARN_ON_ONCE(!chan->sc_table);
1044
1045 if (!chan->sc_filter) {
1046 if (!chan->syscall_all)
1047 return -EEXIST;
1048 filter = kzalloc(sizeof(struct lttng_syscall_filter),
1049 GFP_KERNEL);
1050 if (!filter)
1051 return -ENOMEM;
1052 /* Trace all system calls, then apply disable. */
1053 bitmap_set(filter->sc, 0, NR_syscalls);
1054 bitmap_set(filter->sc_compat, 0, NR_compat_syscalls);
1055 } else {
1056 filter = chan->sc_filter;
1057 }
1058
1059 if (!name) {
1060 /* Fail if all syscalls are already disabled. */
1061 if (bitmap_empty(filter->sc, NR_syscalls)
1062 && bitmap_empty(filter->sc_compat,
1063 NR_compat_syscalls)) {
1064 ret = -EEXIST;
1065 goto error;
1066 }
1067
1068 /* Disable all system calls */
1069 bitmap_clear(filter->sc, 0, NR_syscalls);
1070 bitmap_clear(filter->sc_compat, 0, NR_compat_syscalls);
1071 goto apply_filter;
1072 }
1073 syscall_nr = get_syscall_nr(name);
1074 compat_syscall_nr = get_compat_syscall_nr(name);
1075 if (syscall_nr < 0 && compat_syscall_nr < 0) {
1076 ret = -ENOENT;
1077 goto error;
1078 }
1079 if (syscall_nr >= 0) {
1080 if (!test_bit(syscall_nr, filter->sc)) {
1081 ret = -EEXIST;
1082 goto error;
1083 }
1084 bitmap_clear(filter->sc, syscall_nr, 1);
1085 }
1086 if (compat_syscall_nr >= 0) {
1087 if (!test_bit(compat_syscall_nr, filter->sc_compat)) {
1088 ret = -EEXIST;
1089 goto error;
1090 }
1091 bitmap_clear(filter->sc_compat, compat_syscall_nr, 1);
1092 }
1093apply_filter:
1094 if (!chan->sc_filter)
1095 rcu_assign_pointer(chan->sc_filter, filter);
1096 chan->syscall_all = 0;
1097 return 0;
1098
1099error:
1100 if (!chan->sc_filter)
1101 kfree(filter);
1102 return ret;
1103}
1104
1105static
1106const struct trace_syscall_entry *syscall_list_get_entry(loff_t *pos)
1107{
1108 const struct trace_syscall_entry *entry;
1109 int iter = 0;
1110
1111 for (entry = sc_table;
1112 entry < sc_table + ARRAY_SIZE(sc_table);
1113 entry++) {
1114 if (iter++ >= *pos)
1115 return entry;
1116 }
1117 for (entry = compat_sc_table;
1118 entry < compat_sc_table + ARRAY_SIZE(compat_sc_table);
1119 entry++) {
1120 if (iter++ >= *pos)
1121 return entry;
1122 }
1123 /* End of list */
1124 return NULL;
1125}
1126
1127static
1128void *syscall_list_start(struct seq_file *m, loff_t *pos)
1129{
1130 return (void *) syscall_list_get_entry(pos);
1131}
1132
1133static
1134void *syscall_list_next(struct seq_file *m, void *p, loff_t *ppos)
1135{
1136 (*ppos)++;
1137 return (void *) syscall_list_get_entry(ppos);
1138}
1139
1140static
1141void syscall_list_stop(struct seq_file *m, void *p)
1142{
1143}
1144
1145static
1146int get_sc_table(const struct trace_syscall_entry *entry,
1147 const struct trace_syscall_entry **table,
1148 unsigned int *bitness)
1149{
1150 if (entry >= sc_table && entry < sc_table + ARRAY_SIZE(sc_table)) {
1151 if (bitness)
1152 *bitness = BITS_PER_LONG;
1153 if (table)
1154 *table = sc_table;
1155 return 0;
1156 }
1157 if (!(entry >= compat_sc_table
1158 && entry < compat_sc_table + ARRAY_SIZE(compat_sc_table))) {
1159 return -EINVAL;
1160 }
1161 if (bitness)
1162 *bitness = 32;
1163 if (table)
1164 *table = compat_sc_table;
1165 return 0;
1166}
1167
1168static
1169int syscall_list_show(struct seq_file *m, void *p)
1170{
1171 const struct trace_syscall_entry *table, *entry = p;
1172 unsigned int bitness;
1173 unsigned long index;
1174 int ret;
1175 const char *name;
1176
1177 ret = get_sc_table(entry, &table, &bitness);
1178 if (ret)
1179 return ret;
1180 if (!entry->desc)
1181 return 0;
1182 if (table == sc_table) {
1183 index = entry - table;
1184 name = &entry->desc->name[strlen(SYSCALL_ENTRY_STR)];
1185 } else {
1186 index = (entry - table) + ARRAY_SIZE(sc_table);
1187 name = &entry->desc->name[strlen(COMPAT_SYSCALL_ENTRY_STR)];
1188 }
1189 seq_printf(m, "syscall { index = %lu; name = %s; bitness = %u; };\n",
1190 index, name, bitness);
1191 return 0;
1192}
1193
1194static
1195const struct seq_operations lttng_syscall_list_seq_ops = {
1196 .start = syscall_list_start,
1197 .next = syscall_list_next,
1198 .stop = syscall_list_stop,
1199 .show = syscall_list_show,
1200};
1201
1202static
1203int lttng_syscall_list_open(struct inode *inode, struct file *file)
1204{
1205 return seq_open(file, &lttng_syscall_list_seq_ops);
1206}
1207
1208const struct file_operations lttng_syscall_list_fops = {
1209 .owner = THIS_MODULE,
1210 .open = lttng_syscall_list_open,
1211 .read = seq_read,
1212 .llseek = seq_lseek,
1213 .release = seq_release,
1214};
1215
1216long lttng_channel_syscall_mask(struct lttng_channel *channel,
1217 struct lttng_kernel_syscall_mask __user *usyscall_mask)
1218{
1219 uint32_t len, sc_tables_len, bitmask_len;
1220 int ret = 0, bit;
1221 char *tmp_mask;
1222 struct lttng_syscall_filter *filter;
1223
1224 ret = get_user(len, &usyscall_mask->len);
1225 if (ret)
1226 return ret;
1227 sc_tables_len = get_sc_tables_len();
1228 bitmask_len = ALIGN(sc_tables_len, 8) >> 3;
1229 if (len < sc_tables_len) {
1230 return put_user(sc_tables_len, &usyscall_mask->len);
1231 }
1232 /* Array is large enough, we can copy array to user-space. */
1233 tmp_mask = kzalloc(bitmask_len, GFP_KERNEL);
1234 if (!tmp_mask)
1235 return -ENOMEM;
1236 filter = channel->sc_filter;
1237
1238 for (bit = 0; bit < ARRAY_SIZE(sc_table); bit++) {
1239 bool state;
1240
1241 if (channel->sc_table) {
1242 if (filter)
1243 state = test_bit(bit, filter->sc);
1244 else
1245 state = 1;
1246 } else {
1247 state = 0;
1248 }
1249 bt_bitfield_write_be(tmp_mask, char, bit, 1, state);
1250 }
1251 for (; bit < sc_tables_len; bit++) {
1252 bool state;
1253
1254 if (channel->compat_sc_table) {
1255 if (filter)
1256 state = test_bit(bit - ARRAY_SIZE(sc_table),
1257 filter->sc_compat);
1258 else
1259 state = 1;
1260 } else {
1261 state = 0;
1262 }
1263 bt_bitfield_write_be(tmp_mask, char, bit, 1, state);
1264 }
1265 if (copy_to_user(usyscall_mask->mask, tmp_mask, bitmask_len))
1266 ret = -EFAULT;
1267 kfree(tmp_mask);
1268 return ret;
1269}
1270
1271int lttng_abi_syscall_list(void)
1272{
1273 struct file *syscall_list_file;
1274 int file_fd, ret;
1275
1276 file_fd = lttng_get_unused_fd();
1277 if (file_fd < 0) {
1278 ret = file_fd;
1279 goto fd_error;
1280 }
1281
1282 syscall_list_file = anon_inode_getfile("[lttng_syscall_list]",
1283 &lttng_syscall_list_fops,
1284 NULL, O_RDWR);
1285 if (IS_ERR(syscall_list_file)) {
1286 ret = PTR_ERR(syscall_list_file);
1287 goto file_error;
1288 }
1289 ret = lttng_syscall_list_fops.open(NULL, syscall_list_file);
1290 if (ret < 0)
1291 goto open_error;
1292 fd_install(file_fd, syscall_list_file);
1293 return file_fd;
1294
1295open_error:
1296 fput(syscall_list_file);
1297file_error:
1298 put_unused_fd(file_fd);
1299fd_error:
1300 return ret;
1301}
This page took 0.026327 seconds and 4 git commands to generate.