From 80f87dd2321bb99f5b8cdab7ea6ff33f6ba99b9e Mon Sep 17 00:00:00 2001 From: Mathieu Desnoyers Date: Sat, 19 Jul 2014 17:18:25 -0400 Subject: [PATCH] System call filtering Signed-off-by: Mathieu Desnoyers --- lttng-abi.c | 18 +++- lttng-abi.h | 5 + lttng-events.h | 24 ++++- lttng-syscalls.c | 239 +++++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 261 insertions(+), 25 deletions(-) diff --git a/lttng-abi.c b/lttng-abi.c index cffebcd8..02918114 100644 --- a/lttng-abi.c +++ b/lttng-abi.c @@ -925,15 +925,23 @@ int lttng_abi_create_event(struct file *channel_file, atomic_long_inc(&channel_file->f_count); break; case LTTNG_KERNEL_SYSCALL: - /* - * Only all-syscall tracing supported for now. - */ - if (event_param->name[0] != '\0') - return -EINVAL; ret = lttng_syscalls_register(channel, NULL); if (ret) goto fd_error; event_fd = 0; + if (event_param->u.syscall.disable) { + ret = lttng_syscall_filter_disable(channel, + event_param->name[0] == '\0' ? + NULL : event_param->name); + if (ret) + goto fd_error; + } else { + ret = lttng_syscall_filter_enable(channel, + event_param->name[0] == '\0' ? + NULL : event_param->name); + if (ret) + goto fd_error; + } break; } return event_fd; diff --git a/lttng-abi.h b/lttng-abi.h index 3f2aefbe..b626b88e 100644 --- a/lttng-abi.h +++ b/lttng-abi.h @@ -79,6 +79,10 @@ struct lttng_kernel_function_tracer { char symbol_name[LTTNG_KERNEL_SYM_NAME_LEN]; }__attribute__((packed)); +struct lttng_kernel_syscall { + char disable; +} __attribute__((packed)); + /* * For syscall tracing, name = '\0' means "enable all". */ @@ -94,6 +98,7 @@ struct lttng_kernel_event { struct lttng_kernel_kretprobe kretprobe; struct lttng_kernel_kprobe kprobe; struct lttng_kernel_function_tracer ftrace; + struct lttng_kernel_syscall syscall; char padding[LTTNG_KERNEL_EVENT_PADDING2]; } u; }__attribute__((packed)); diff --git a/lttng-events.h b/lttng-events.h index 64a1ac56..e0337993 100644 --- a/lttng-events.h +++ b/lttng-events.h @@ -280,6 +280,8 @@ struct lttng_transport { struct lttng_channel_ops ops; }; +struct lttng_syscall_filter; + struct lttng_channel { unsigned int id; struct channel *chan; /* Channel buffers */ @@ -297,9 +299,13 @@ struct lttng_channel { struct lttng_event *sc_unknown; /* for unknown syscalls */ struct lttng_event *sc_compat_unknown; struct lttng_event *sc_exit; /* for syscall exit */ + struct lttng_syscall_filter *sc_filter; int header_type; /* 0: unset, 1: compact, 2: large */ enum channel_type channel_type; - unsigned int metadata_dumped:1; + unsigned int metadata_dumped:1, + sys_enter_registered:1, + sys_exit_registered:1, + syscall_all:1; }; struct lttng_metadata_stream { @@ -392,6 +398,10 @@ int lttng_metadata_output_channel(struct lttng_metadata_stream *stream, #if defined(CONFIG_HAVE_SYSCALL_TRACEPOINTS) int lttng_syscalls_register(struct lttng_channel *chan, void *filter); int lttng_syscalls_unregister(struct lttng_channel *chan); +int lttng_syscall_filter_enable(struct lttng_channel *chan, + const char *name); +int lttng_syscall_filter_disable(struct lttng_channel *chan, + const char *name); #else static inline int lttng_syscalls_register(struct lttng_channel *chan, void *filter) { @@ -402,6 +412,18 @@ static inline int lttng_syscalls_unregister(struct lttng_channel *chan) { return 0; } + +int lttng_syscall_filter_enable(struct lttng_channel *chan, + const char *name) +{ + return -ENOSYS; +} + +int lttng_syscall_filter_disable(struct lttng_channel *chan, + const char *name) +{ + return -ENOSYS; +} #endif struct lttng_ctx_field *lttng_append_context(struct lttng_ctx **ctx); diff --git a/lttng-syscalls.c b/lttng-syscalls.c index f452b487..f12c81c0 100644 --- a/lttng-syscalls.c +++ b/lttng-syscalls.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -49,6 +50,12 @@ struct old_utsname; struct sel_arg_struct; struct mmap_arg_struct; +#ifdef IA32_NR_syscalls +#define NR_compat_syscalls IA32_NR_syscalls +#else +#define NR_compat_syscalls NR_syscalls +#endif + /* * Take care of NOARGS not supported by mainline. */ @@ -163,6 +170,11 @@ const struct trace_syscall_entry compat_sc_table[] = { #undef CREATE_SYSCALL_TABLE +struct lttng_syscall_filter { + DECLARE_BITMAP(sc, NR_syscalls); + DECLARE_BITMAP(sc_compat, NR_compat_syscalls); +}; + static void syscall_entry_unknown(struct lttng_event *event, struct pt_regs *regs, unsigned int id) { @@ -183,10 +195,30 @@ void syscall_entry_probe(void *__data, struct pt_regs *regs, long id) size_t table_len; if (unlikely(is_compat_task())) { + struct lttng_syscall_filter *filter; + + filter = rcu_dereference(chan->sc_filter); + if (filter) { + if (id >= NR_compat_syscalls + || !test_bit(id, filter->sc_compat)) { + /* System call filtered out. */ + return; + } + } table = compat_sc_table; table_len = ARRAY_SIZE(compat_sc_table); unknown_event = chan->sc_compat_unknown; } else { + struct lttng_syscall_filter *filter; + + filter = rcu_dereference(chan->sc_filter); + if (filter) { + if (id >= NR_syscalls + || !test_bit(id, filter->sc)) { + /* System call filtered out. */ + return; + } + } table = sc_table; table_len = ARRAY_SIZE(sc_table); unknown_event = chan->sc_unknown; @@ -419,20 +451,27 @@ int lttng_syscalls_register(struct lttng_channel *chan, void *filter) if (ret) return ret; #endif - ret = lttng_wrapper_tracepoint_probe_register("sys_enter", - (void *) syscall_entry_probe, chan); - if (ret) - return ret; + if (!chan->sys_enter_registered) { + ret = lttng_wrapper_tracepoint_probe_register("sys_enter", + (void *) syscall_entry_probe, chan); + if (ret) + return ret; + chan->sys_enter_registered = 1; + } /* * We change the name of sys_exit tracepoint due to namespace * conflict with sys_exit syscall entry. */ - ret = lttng_wrapper_tracepoint_probe_register("sys_exit", - (void *) __event_probe__exit_syscall, - chan->sc_exit); - if (ret) { - WARN_ON_ONCE(lttng_wrapper_tracepoint_probe_unregister("sys_enter", - (void *) syscall_entry_probe, chan)); + if (!chan->sys_exit_registered) { + ret = lttng_wrapper_tracepoint_probe_register("sys_exit", + (void *) __event_probe__exit_syscall, + chan->sc_exit); + if (ret) { + WARN_ON_ONCE(lttng_wrapper_tracepoint_probe_unregister("sys_enter", + (void *) syscall_entry_probe, chan)); + return ret; + } + chan->sys_exit_registered = 1; } return ret; } @@ -446,19 +485,181 @@ int lttng_syscalls_unregister(struct lttng_channel *chan) if (!chan->sc_table) return 0; - ret = lttng_wrapper_tracepoint_probe_unregister("sys_exit", - (void *) __event_probe__exit_syscall, - chan->sc_exit); - if (ret) - return ret; - ret = lttng_wrapper_tracepoint_probe_unregister("sys_enter", - (void *) syscall_entry_probe, chan); - if (ret) - return ret; + if (chan->sys_enter_registered) { + ret = lttng_wrapper_tracepoint_probe_unregister("sys_exit", + (void *) __event_probe__exit_syscall, + chan->sc_exit); + if (ret) + return ret; + chan->sys_enter_registered = 0; + } + if (chan->sys_exit_registered) { + ret = lttng_wrapper_tracepoint_probe_unregister("sys_enter", + (void *) syscall_entry_probe, chan); + if (ret) + return ret; + chan->sys_exit_registered = 0; + } /* lttng_event destroy will be performed by lttng_session_destroy() */ kfree(chan->sc_table); #ifdef CONFIG_COMPAT kfree(chan->compat_sc_table); #endif + kfree(chan->sc_filter); + return 0; +} + +static +int get_syscall_nr(const char *syscall_name) +{ + int syscall_nr = -1; + int i; + + for (i = 0; i < ARRAY_SIZE(sc_table); i++) { + const struct trace_syscall_entry *entry; + + entry = &sc_table[i]; + if (!entry->desc) + continue; + if (!strcmp(syscall_name, entry->desc->name)) { + syscall_nr = i; + break; + } + } + return syscall_nr; +} + +static +int get_compat_syscall_nr(const char *syscall_name) +{ + int syscall_nr = -1; + int i; + + for (i = 0; i < ARRAY_SIZE(compat_sc_table); i++) { + const struct trace_syscall_entry *entry; + + entry = &compat_sc_table[i]; + if (!entry->desc) + continue; + if (!strcmp(syscall_name, entry->desc->name)) { + syscall_nr = i; + break; + } + } + return syscall_nr; +} + +int lttng_syscall_filter_enable(struct lttng_channel *chan, + const char *name) +{ + int syscall_nr, compat_syscall_nr, ret; + struct lttng_syscall_filter *filter; + + WARN_ON_ONCE(!chan->sc_table); + + if (!name) { + /* Enable all system calls by removing filter */ + if (chan->sc_filter) { + filter = chan->sc_filter; + rcu_assign_pointer(chan->sc_filter, NULL); + synchronize_trace(); + kfree(filter); + } + chan->syscall_all = 1; + return 0; + } + + if (!chan->sc_filter) { + if (chan->syscall_all) { + /* + * All syscalls are already enabled. + */ + return -EEXIST; + } + filter = kzalloc(sizeof(struct lttng_syscall_filter), + GFP_KERNEL); + if (!filter) + return -ENOMEM; + } else { + filter = chan->sc_filter; + } + syscall_nr = get_syscall_nr(name); + compat_syscall_nr = get_compat_syscall_nr(name); + if (syscall_nr < 0 && compat_syscall_nr < 0) { + ret = -ENOENT; + goto error; + } + if (syscall_nr >= 0) { + if (test_bit(syscall_nr, filter->sc)) { + ret = -EEXIST; + goto error; + } + bitmap_set(filter->sc, syscall_nr, 1); + } + if (compat_syscall_nr >= 0) { + if (test_bit(compat_syscall_nr, filter->sc_compat)) { + ret = -EEXIST; + goto error; + } + bitmap_set(filter->sc_compat, compat_syscall_nr, 1); + } + if (!chan->sc_filter) + rcu_assign_pointer(chan->sc_filter, filter); + return 0; + +error: + if (!chan->sc_filter) + kfree(filter); + return ret; +} + +int lttng_syscall_filter_disable(struct lttng_channel *chan, + const char *name) +{ + int syscall_nr, compat_syscall_nr, ret; + struct lttng_syscall_filter *filter; + + WARN_ON_ONCE(!chan->sc_table); + + if (!chan->sc_filter) { + filter = kzalloc(sizeof(struct lttng_syscall_filter), + GFP_KERNEL); + if (!filter) + return -ENOMEM; + /* Trace all system calls, then apply disable. */ + bitmap_set(filter->sc, 0, NR_syscalls); + bitmap_set(filter->sc_compat, 0, NR_compat_syscalls); + } else { + filter = chan->sc_filter; + } + + syscall_nr = get_syscall_nr(name); + compat_syscall_nr = get_compat_syscall_nr(name); + if (syscall_nr < 0 && compat_syscall_nr < 0) { + ret = -ENOENT; + goto error; + } + if (syscall_nr >= 0) { + if (!test_bit(syscall_nr, chan->sc_filter->sc)) { + ret = -EEXIST; + goto error; + } + bitmap_clear(chan->sc_filter->sc, syscall_nr, 1); + } + if (compat_syscall_nr >= 0) { + if (!test_bit(compat_syscall_nr, chan->sc_filter->sc_compat)) { + ret = -EEXIST; + goto error; + } + bitmap_clear(chan->sc_filter->sc_compat, compat_syscall_nr, 1); + } + if (!chan->sc_filter) + rcu_assign_pointer(chan->sc_filter, filter); + chan->syscall_all = 0; return 0; + +error: + if (!chan->sc_filter) + kfree(filter); + return ret; } -- 2.34.1