summaryrefslogtreecommitdiffstats
path: root/meta/recipes-kernel/lttng/lttng-modules/0010-Fix-system-call-filter-table.patch
diff options
context:
space:
mode:
Diffstat (limited to 'meta/recipes-kernel/lttng/lttng-modules/0010-Fix-system-call-filter-table.patch')
-rw-r--r--meta/recipes-kernel/lttng/lttng-modules/0010-Fix-system-call-filter-table.patch918
1 files changed, 918 insertions, 0 deletions
diff --git a/meta/recipes-kernel/lttng/lttng-modules/0010-Fix-system-call-filter-table.patch b/meta/recipes-kernel/lttng/lttng-modules/0010-Fix-system-call-filter-table.patch
new file mode 100644
index 0000000000..a16750ddb3
--- /dev/null
+++ b/meta/recipes-kernel/lttng/lttng-modules/0010-Fix-system-call-filter-table.patch
@@ -0,0 +1,918 @@
+From ad594e3a953db1b0c3c059fde45b5a5494f6be78 Mon Sep 17 00:00:00 2001
+From: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+Date: Tue, 28 Jan 2020 16:02:44 -0500
+Subject: [PATCH 10/10] Fix: system call filter table
+
+The system call filter table has effectively been unused for a long
+time due to system call name prefix mismatch. This means the overhead of
+selective system call tracing was larger than it should have been because
+the event payload preparation would be done for all system calls as soon
+as a single system call is traced.
+
+However, fixing this underlying issue unearths several issues that crept
+unnoticed when the "enabler" concept was introduced (after the original
+implementation of the system call filter table).
+
+Here is a list of the issues which are resolved here:
+
+- Split lttng_syscalls_unregister into an unregister and destroy
+ function, thus awaiting for a grace period (and therefore quiescence
+ of the users) after unregistering the system call tracepoints before
+ freeing the system call filter data structures. This effectively fixes
+ a use-after-free.
+
+- The state for enabling "all" system calls vs enabling specific system
+ calls (and sequences of enable-disable) was incorrect with respect to
+ the "enablers" semantic. This is solved by always tracking the
+ bitmap of enabled system calls, and keeping this bitmap even when
+ enabling all system calls. The sc_filter is now always allocated
+ before system call tracing is registered to tracepoints, which means
+ it does not need to be RCU dereferenced anymore.
+
+Padding fields in the ABI are reserved to select whether to:
+
+- Trace either native or compat system call (or both, which is the
+ behavior currently implemented),
+- Trace either system call entry or exit (or both, which is the
+ behavior currently implemented),
+- Select the system call to trace by name (behavior currently
+ implemented) or by system call number,
+
+Upstream-Status: Backport
+
+Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+---
+ lttng-abi.c | 43 ++++++
+ lttng-abi.h | 26 ++++
+ lttng-events.c | 112 +++++++++++++--
+ lttng-events.h | 31 ++++-
+ lttng-syscalls.c | 348 +++++++++++++++++++++++++----------------------
+ 5 files changed, 380 insertions(+), 180 deletions(-)
+
+diff --git a/lttng-abi.c b/lttng-abi.c
+index 64ea99d..b33879d 100644
+--- a/lttng-abi.c
++++ b/lttng-abi.c
+@@ -1264,6 +1264,46 @@ nomem:
+ return ret;
+ }
+
++static
++int lttng_abi_validate_event_param(struct lttng_kernel_event *event_param)
++{
++ /* Limit ABI to implemented features. */
++ switch (event_param->instrumentation) {
++ case LTTNG_KERNEL_SYSCALL:
++ switch (event_param->u.syscall.entryexit) {
++ case LTTNG_KERNEL_SYSCALL_ENTRYEXIT:
++ break;
++ default:
++ return -EINVAL;
++ }
++ switch (event_param->u.syscall.abi) {
++ case LTTNG_KERNEL_SYSCALL_ABI_ALL:
++ break;
++ default:
++ return -EINVAL;
++ }
++ switch (event_param->u.syscall.match) {
++ case LTTNG_SYSCALL_MATCH_NAME:
++ break;
++ default:
++ return -EINVAL;
++ }
++ break;
++
++ case LTTNG_KERNEL_TRACEPOINT: /* Fallthrough */
++ case LTTNG_KERNEL_KPROBE: /* Fallthrough */
++ case LTTNG_KERNEL_KRETPROBE: /* Fallthrough */
++ case LTTNG_KERNEL_NOOP: /* Fallthrough */
++ case LTTNG_KERNEL_UPROBE:
++ break;
++
++ case LTTNG_KERNEL_FUNCTION: /* Fallthrough */
++ default:
++ return -EINVAL;
++ }
++ return 0;
++}
++
+ static
+ int lttng_abi_create_event(struct file *channel_file,
+ struct lttng_kernel_event *event_param)
+@@ -1305,6 +1345,9 @@ int lttng_abi_create_event(struct file *channel_file,
+ ret = -EOVERFLOW;
+ goto refcount_error;
+ }
++ ret = lttng_abi_validate_event_param(event_param);
++ if (ret)
++ goto event_error;
+ if (event_param->instrumentation == LTTNG_KERNEL_TRACEPOINT
+ || event_param->instrumentation == LTTNG_KERNEL_SYSCALL) {
+ struct lttng_enabler *enabler;
+diff --git a/lttng-abi.h b/lttng-abi.h
+index 1d356ab..51d60e5 100644
+--- a/lttng-abi.h
++++ b/lttng-abi.h
+@@ -90,6 +90,31 @@ struct lttng_kernel_event_callsite {
+ } u;
+ } __attribute__((packed));
+
++enum lttng_kernel_syscall_entryexit {
++ LTTNG_KERNEL_SYSCALL_ENTRYEXIT = 0,
++ LTTNG_KERNEL_SYSCALL_ENTRY = 1, /* Not implemented. */
++ LTTNG_KERNEL_SYSCALL_EXIT = 2, /* Not implemented. */
++};
++
++enum lttng_kernel_syscall_abi {
++ LTTNG_KERNEL_SYSCALL_ABI_ALL = 0,
++ LTTNG_KERNEL_SYSCALL_ABI_NATIVE = 1, /* Not implemented. */
++ LTTNG_KERNEL_SYSCALL_ABI_COMPAT = 2, /* Not implemented. */
++};
++
++enum lttng_kernel_syscall_match {
++ LTTNG_SYSCALL_MATCH_NAME = 0,
++ LTTNG_SYSCALL_MATCH_NR = 1, /* Not implemented. */
++};
++
++struct lttng_kernel_syscall {
++ uint8_t entryexit; /* enum lttng_kernel_syscall_entryexit */
++ uint8_t abi; /* enum lttng_kernel_syscall_abi */
++ uint8_t match; /* enum lttng_kernel_syscall_match */
++ uint8_t padding;
++ uint32_t nr; /* For LTTNG_SYSCALL_MATCH_NR */
++} __attribute__((packed));
++
+ /*
+ * For syscall tracing, name = "*" means "enable all".
+ */
+@@ -106,6 +131,7 @@ struct lttng_kernel_event {
+ struct lttng_kernel_kprobe kprobe;
+ struct lttng_kernel_function_tracer ftrace;
+ struct lttng_kernel_uprobe uprobe;
++ struct lttng_kernel_syscall syscall;
+ char padding[LTTNG_KERNEL_EVENT_PADDING2];
+ } u;
+ } __attribute__((packed));
+diff --git a/lttng-events.c b/lttng-events.c
+index d719294..4c0b04a 100644
+--- a/lttng-events.c
++++ b/lttng-events.c
+@@ -201,6 +201,10 @@ void lttng_session_destroy(struct lttng_session *session)
+ WARN_ON(ret);
+ }
+ synchronize_trace(); /* Wait for in-flight events to complete */
++ list_for_each_entry(chan, &session->chan, list) {
++ ret = lttng_syscalls_destroy(chan);
++ WARN_ON(ret);
++ }
+ list_for_each_entry_safe(enabler, tmpenabler,
+ &session->enablers_head, node)
+ lttng_enabler_destroy(enabler);
+@@ -740,6 +744,28 @@ struct lttng_event *_lttng_event_create(struct lttng_channel *chan,
+ event->enabled = 0;
+ event->registered = 0;
+ event->desc = event_desc;
++ switch (event_param->u.syscall.entryexit) {
++ case LTTNG_KERNEL_SYSCALL_ENTRYEXIT:
++ ret = -EINVAL;
++ goto register_error;
++ case LTTNG_KERNEL_SYSCALL_ENTRY:
++ event->u.syscall.entryexit = LTTNG_SYSCALL_ENTRY;
++ break;
++ case LTTNG_KERNEL_SYSCALL_EXIT:
++ event->u.syscall.entryexit = LTTNG_SYSCALL_EXIT;
++ break;
++ }
++ switch (event_param->u.syscall.abi) {
++ case LTTNG_KERNEL_SYSCALL_ABI_ALL:
++ ret = -EINVAL;
++ goto register_error;
++ case LTTNG_KERNEL_SYSCALL_ABI_NATIVE:
++ event->u.syscall.abi = LTTNG_SYSCALL_ABI_NATIVE;
++ break;
++ case LTTNG_KERNEL_SYSCALL_ABI_COMPAT:
++ event->u.syscall.abi = LTTNG_SYSCALL_ABI_COMPAT;
++ break;
++ }
+ if (!event->desc) {
+ ret = -EINVAL;
+ goto register_error;
+@@ -826,8 +852,7 @@ void register_event(struct lttng_event *event)
+ event);
+ break;
+ case LTTNG_KERNEL_SYSCALL:
+- ret = lttng_syscall_filter_enable(event->chan,
+- desc->name);
++ ret = lttng_syscall_filter_enable(event->chan, event);
+ break;
+ case LTTNG_KERNEL_KPROBE:
+ case LTTNG_KERNEL_UPROBE:
+@@ -870,8 +895,7 @@ int _lttng_event_unregister(struct lttng_event *event)
+ ret = 0;
+ break;
+ case LTTNG_KERNEL_SYSCALL:
+- ret = lttng_syscall_filter_disable(event->chan,
+- desc->name);
++ ret = lttng_syscall_filter_disable(event->chan, event);
+ break;
+ case LTTNG_KERNEL_NOOP:
+ ret = 0;
+@@ -1203,39 +1227,87 @@ int lttng_desc_match_enabler(const struct lttng_event_desc *desc,
+ struct lttng_enabler *enabler)
+ {
+ const char *desc_name, *enabler_name;
++ bool compat = false, entry = false;
+
+ enabler_name = enabler->event_param.name;
+ switch (enabler->event_param.instrumentation) {
+ case LTTNG_KERNEL_TRACEPOINT:
+ desc_name = desc->name;
++ switch (enabler->type) {
++ case LTTNG_ENABLER_STAR_GLOB:
++ return lttng_match_enabler_star_glob(desc_name, enabler_name);
++ case LTTNG_ENABLER_NAME:
++ return lttng_match_enabler_name(desc_name, enabler_name);
++ default:
++ return -EINVAL;
++ }
+ break;
+ case LTTNG_KERNEL_SYSCALL:
+ desc_name = desc->name;
+- if (!strncmp(desc_name, "compat_", strlen("compat_")))
++ if (!strncmp(desc_name, "compat_", strlen("compat_"))) {
+ desc_name += strlen("compat_");
++ compat = true;
++ }
+ if (!strncmp(desc_name, "syscall_exit_",
+ strlen("syscall_exit_"))) {
+ desc_name += strlen("syscall_exit_");
+ } else if (!strncmp(desc_name, "syscall_entry_",
+ strlen("syscall_entry_"))) {
+ desc_name += strlen("syscall_entry_");
++ entry = true;
+ } else {
+ WARN_ON_ONCE(1);
+ return -EINVAL;
+ }
++ switch (enabler->event_param.u.syscall.entryexit) {
++ case LTTNG_KERNEL_SYSCALL_ENTRYEXIT:
++ break;
++ case LTTNG_KERNEL_SYSCALL_ENTRY:
++ if (!entry)
++ return 0;
++ break;
++ case LTTNG_KERNEL_SYSCALL_EXIT:
++ if (entry)
++ return 0;
++ break;
++ default:
++ return -EINVAL;
++ }
++ switch (enabler->event_param.u.syscall.abi) {
++ case LTTNG_KERNEL_SYSCALL_ABI_ALL:
++ break;
++ case LTTNG_KERNEL_SYSCALL_ABI_NATIVE:
++ if (compat)
++ return 0;
++ break;
++ case LTTNG_KERNEL_SYSCALL_ABI_COMPAT:
++ if (!compat)
++ return 0;
++ break;
++ default:
++ return -EINVAL;
++ }
++ switch (enabler->event_param.u.syscall.match) {
++ case LTTNG_SYSCALL_MATCH_NAME:
++ switch (enabler->type) {
++ case LTTNG_ENABLER_STAR_GLOB:
++ return lttng_match_enabler_star_glob(desc_name, enabler_name);
++ case LTTNG_ENABLER_NAME:
++ return lttng_match_enabler_name(desc_name, enabler_name);
++ default:
++ return -EINVAL;
++ }
++ break;
++ case LTTNG_SYSCALL_MATCH_NR:
++ return -EINVAL; /* Not implemented. */
++ default:
++ return -EINVAL;
++ }
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ return -EINVAL;
+ }
+- switch (enabler->type) {
+- case LTTNG_ENABLER_STAR_GLOB:
+- return lttng_match_enabler_star_glob(desc_name, enabler_name);
+- case LTTNG_ENABLER_NAME:
+- return lttng_match_enabler_name(desc_name, enabler_name);
+- default:
+- return -EINVAL;
+- }
+ }
+
+ static
+@@ -1361,9 +1433,21 @@ void lttng_create_event_if_missing(struct lttng_enabler *enabler)
+ static
+ int lttng_enabler_ref_events(struct lttng_enabler *enabler)
+ {
+- struct lttng_session *session = enabler->chan->session;
++ struct lttng_channel *chan = enabler->chan;
++ struct lttng_session *session = chan->session;
+ struct lttng_event *event;
+
++ if (enabler->event_param.instrumentation == LTTNG_KERNEL_SYSCALL &&
++ enabler->event_param.u.syscall.entryexit == LTTNG_KERNEL_SYSCALL_ENTRYEXIT &&
++ enabler->event_param.u.syscall.abi == LTTNG_KERNEL_SYSCALL_ABI_ALL &&
++ enabler->event_param.u.syscall.match == LTTNG_SYSCALL_MATCH_NAME &&
++ !strcmp(enabler->event_param.name, "*")) {
++ if (enabler->enabled)
++ WRITE_ONCE(chan->syscall_all, 1);
++ else
++ WRITE_ONCE(chan->syscall_all, 0);
++ }
++
+ /* First ensure that probe events are created for this enabler. */
+ lttng_create_event_if_missing(enabler);
+
+diff --git a/lttng-events.h b/lttng-events.h
+index a36a312..d4d9976 100644
+--- a/lttng-events.h
++++ b/lttng-events.h
+@@ -292,6 +292,16 @@ struct lttng_uprobe_handler {
+ struct list_head node;
+ };
+
++enum lttng_syscall_entryexit {
++ LTTNG_SYSCALL_ENTRY,
++ LTTNG_SYSCALL_EXIT,
++};
++
++enum lttng_syscall_abi {
++ LTTNG_SYSCALL_ABI_NATIVE,
++ LTTNG_SYSCALL_ABI_COMPAT,
++};
++
+ /*
+ * lttng_event structure is referred to by the tracing fast path. It must be
+ * kept small.
+@@ -318,6 +328,11 @@ struct lttng_event {
+ struct inode *inode;
+ struct list_head head;
+ } uprobe;
++ struct {
++ char *syscall_name;
++ enum lttng_syscall_entryexit entryexit;
++ enum lttng_syscall_abi abi;
++ } syscall;
+ } u;
+ struct list_head list; /* Event list in session */
+ unsigned int metadata_dumped:1;
+@@ -457,10 +472,10 @@ struct lttng_channel {
+ struct lttng_syscall_filter *sc_filter;
+ int header_type; /* 0: unset, 1: compact, 2: large */
+ enum channel_type channel_type;
++ int syscall_all;
+ unsigned int metadata_dumped:1,
+ sys_enter_registered:1,
+ sys_exit_registered:1,
+- syscall_all:1,
+ tstate:1; /* Transient enable state */
+ };
+
+@@ -653,10 +668,11 @@ void lttng_clock_unref(void);
+ #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_syscalls_destroy(struct lttng_channel *chan);
+ int lttng_syscall_filter_enable(struct lttng_channel *chan,
+- const char *name);
++ struct lttng_event *event);
+ int lttng_syscall_filter_disable(struct lttng_channel *chan,
+- const char *name);
++ struct lttng_event *event);
+ long lttng_channel_syscall_mask(struct lttng_channel *channel,
+ struct lttng_kernel_syscall_mask __user *usyscall_mask);
+ #else
+@@ -670,14 +686,19 @@ static inline int lttng_syscalls_unregister(struct lttng_channel *chan)
+ return 0;
+ }
+
++static inline int lttng_syscalls_destroy(struct lttng_channel *chan)
++{
++ return 0;
++}
++
+ static inline int lttng_syscall_filter_enable(struct lttng_channel *chan,
+- const char *name)
++ struct lttng_event *event);
+ {
+ return -ENOSYS;
+ }
+
+ static inline int lttng_syscall_filter_disable(struct lttng_channel *chan,
+- const char *name)
++ struct lttng_event *event);
+ {
+ return -ENOSYS;
+ }
+diff --git a/lttng-syscalls.c b/lttng-syscalls.c
+index 97f1ba9..26cead6 100644
+--- a/lttng-syscalls.c
++++ b/lttng-syscalls.c
+@@ -367,8 +367,10 @@ const struct trace_syscall_entry compat_sc_exit_table[] = {
+ #undef CREATE_SYSCALL_TABLE
+
+ struct lttng_syscall_filter {
+- DECLARE_BITMAP(sc, NR_syscalls);
+- DECLARE_BITMAP(sc_compat, NR_compat_syscalls);
++ DECLARE_BITMAP(sc_entry, NR_syscalls);
++ DECLARE_BITMAP(sc_exit, NR_syscalls);
++ DECLARE_BITMAP(sc_compat_entry, NR_compat_syscalls);
++ DECLARE_BITMAP(sc_compat_exit, NR_compat_syscalls);
+ };
+
+ static void syscall_entry_unknown(struct lttng_event *event,
+@@ -391,29 +393,23 @@ void syscall_entry_probe(void *__data, struct pt_regs *regs, long id)
+ size_t table_len;
+
+ if (unlikely(in_compat_syscall())) {
+- struct lttng_syscall_filter *filter;
+-
+- filter = lttng_rcu_dereference(chan->sc_filter);
+- if (filter) {
+- if (id < 0 || id >= NR_compat_syscalls
+- || !test_bit(id, filter->sc_compat)) {
+- /* System call filtered out. */
+- return;
+- }
++ struct lttng_syscall_filter *filter = chan->sc_filter;
++
++ if (id < 0 || id >= NR_compat_syscalls
++ || (!READ_ONCE(chan->syscall_all) && !test_bit(id, filter->sc_compat_entry))) {
++ /* 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 = lttng_rcu_dereference(chan->sc_filter);
+- if (filter) {
+- if (id < 0 || id >= NR_syscalls
+- || !test_bit(id, filter->sc)) {
+- /* System call filtered out. */
+- return;
+- }
++ struct lttng_syscall_filter *filter = chan->sc_filter;
++
++ if (id < 0 || id >= NR_syscalls
++ || (!READ_ONCE(chan->syscall_all) && !test_bit(id, filter->sc_entry))) {
++ /* System call filtered out. */
++ return;
+ }
+ table = sc_table;
+ table_len = ARRAY_SIZE(sc_table);
+@@ -545,29 +541,23 @@ void syscall_exit_probe(void *__data, struct pt_regs *regs, long ret)
+
+ id = syscall_get_nr(current, regs);
+ if (unlikely(in_compat_syscall())) {
+- struct lttng_syscall_filter *filter;
+-
+- filter = lttng_rcu_dereference(chan->sc_filter);
+- if (filter) {
+- if (id < 0 || id >= NR_compat_syscalls
+- || !test_bit(id, filter->sc_compat)) {
+- /* System call filtered out. */
+- return;
+- }
++ struct lttng_syscall_filter *filter = chan->sc_filter;
++
++ if (id < 0 || id >= NR_compat_syscalls
++ || (!READ_ONCE(chan->syscall_all) && !test_bit(id, filter->sc_compat_exit))) {
++ /* System call filtered out. */
++ return;
+ }
+ table = compat_sc_exit_table;
+ table_len = ARRAY_SIZE(compat_sc_exit_table);
+ unknown_event = chan->compat_sc_exit_unknown;
+ } else {
+- struct lttng_syscall_filter *filter;
+-
+- filter = lttng_rcu_dereference(chan->sc_filter);
+- if (filter) {
+- if (id < 0 || id >= NR_syscalls
+- || !test_bit(id, filter->sc)) {
+- /* System call filtered out. */
+- return;
+- }
++ struct lttng_syscall_filter *filter = chan->sc_filter;
++
++ if (id < 0 || id >= NR_syscalls
++ || (!READ_ONCE(chan->syscall_all) && !test_bit(id, filter->sc_exit))) {
++ /* System call filtered out. */
++ return;
+ }
+ table = sc_exit_table;
+ table_len = ARRAY_SIZE(sc_exit_table);
+@@ -713,27 +703,23 @@ int fill_table(const struct trace_syscall_entry *table, size_t table_len,
+ memset(&ev, 0, sizeof(ev));
+ switch (type) {
+ case SC_TYPE_ENTRY:
+- strncpy(ev.name, SYSCALL_ENTRY_STR,
+- LTTNG_KERNEL_SYM_NAME_LEN);
++ ev.u.syscall.entryexit = LTTNG_KERNEL_SYSCALL_ENTRY;
++ ev.u.syscall.abi = LTTNG_KERNEL_SYSCALL_ABI_NATIVE;
+ break;
+ case SC_TYPE_EXIT:
+- strncpy(ev.name, SYSCALL_EXIT_STR,
+- LTTNG_KERNEL_SYM_NAME_LEN);
++ ev.u.syscall.entryexit = LTTNG_KERNEL_SYSCALL_EXIT;
++ ev.u.syscall.abi = LTTNG_KERNEL_SYSCALL_ABI_NATIVE;
+ break;
+ case SC_TYPE_COMPAT_ENTRY:
+- strncpy(ev.name, COMPAT_SYSCALL_ENTRY_STR,
+- LTTNG_KERNEL_SYM_NAME_LEN);
++ ev.u.syscall.entryexit = LTTNG_KERNEL_SYSCALL_ENTRY;
++ ev.u.syscall.abi = LTTNG_KERNEL_SYSCALL_ABI_COMPAT;
+ break;
+ case SC_TYPE_COMPAT_EXIT:
+- strncpy(ev.name, COMPAT_SYSCALL_EXIT_STR,
+- LTTNG_KERNEL_SYM_NAME_LEN);
+- break;
+- default:
+- BUG_ON(1);
++ ev.u.syscall.entryexit = LTTNG_KERNEL_SYSCALL_EXIT;
++ ev.u.syscall.abi = LTTNG_KERNEL_SYSCALL_ABI_COMPAT;
+ break;
+ }
+- strncat(ev.name, desc->name,
+- LTTNG_KERNEL_SYM_NAME_LEN - strlen(ev.name) - 1);
++ strncpy(ev.name, desc->name, LTTNG_KERNEL_SYM_NAME_LEN);
+ ev.name[LTTNG_KERNEL_SYM_NAME_LEN - 1] = '\0';
+ ev.instrumentation = LTTNG_KERNEL_SYSCALL;
+ chan_table[i] = _lttng_event_create(chan, &ev, filter,
+@@ -803,6 +789,8 @@ int lttng_syscalls_register(struct lttng_channel *chan, void *filter)
+ strncpy(ev.name, desc->name, LTTNG_KERNEL_SYM_NAME_LEN);
+ ev.name[LTTNG_KERNEL_SYM_NAME_LEN - 1] = '\0';
+ ev.instrumentation = LTTNG_KERNEL_SYSCALL;
++ ev.u.syscall.entryexit = LTTNG_KERNEL_SYSCALL_ENTRY;
++ ev.u.syscall.abi = LTTNG_KERNEL_SYSCALL_ABI_NATIVE;
+ chan->sc_unknown = _lttng_event_create(chan, &ev, filter,
+ desc,
+ ev.instrumentation);
+@@ -820,6 +808,8 @@ int lttng_syscalls_register(struct lttng_channel *chan, void *filter)
+ strncpy(ev.name, desc->name, LTTNG_KERNEL_SYM_NAME_LEN);
+ ev.name[LTTNG_KERNEL_SYM_NAME_LEN - 1] = '\0';
+ ev.instrumentation = LTTNG_KERNEL_SYSCALL;
++ ev.u.syscall.entryexit = LTTNG_KERNEL_SYSCALL_ENTRY;
++ ev.u.syscall.abi = LTTNG_KERNEL_SYSCALL_ABI_COMPAT;
+ chan->sc_compat_unknown = _lttng_event_create(chan, &ev, filter,
+ desc,
+ ev.instrumentation);
+@@ -837,6 +827,8 @@ int lttng_syscalls_register(struct lttng_channel *chan, void *filter)
+ strncpy(ev.name, desc->name, LTTNG_KERNEL_SYM_NAME_LEN);
+ ev.name[LTTNG_KERNEL_SYM_NAME_LEN - 1] = '\0';
+ ev.instrumentation = LTTNG_KERNEL_SYSCALL;
++ ev.u.syscall.entryexit = LTTNG_KERNEL_SYSCALL_EXIT;
++ ev.u.syscall.abi = LTTNG_KERNEL_SYSCALL_ABI_COMPAT;
+ chan->compat_sc_exit_unknown = _lttng_event_create(chan, &ev,
+ filter, desc,
+ ev.instrumentation);
+@@ -854,6 +846,8 @@ int lttng_syscalls_register(struct lttng_channel *chan, void *filter)
+ strncpy(ev.name, desc->name, LTTNG_KERNEL_SYM_NAME_LEN);
+ ev.name[LTTNG_KERNEL_SYM_NAME_LEN - 1] = '\0';
+ ev.instrumentation = LTTNG_KERNEL_SYSCALL;
++ ev.u.syscall.entryexit = LTTNG_KERNEL_SYSCALL_EXIT;
++ ev.u.syscall.abi = LTTNG_KERNEL_SYSCALL_ABI_NATIVE;
+ chan->sc_exit_unknown = _lttng_event_create(chan, &ev, filter,
+ desc, ev.instrumentation);
+ WARN_ON_ONCE(!chan->sc_exit_unknown);
+@@ -883,6 +877,14 @@ int lttng_syscalls_register(struct lttng_channel *chan, void *filter)
+ if (ret)
+ return ret;
+ #endif
++
++ if (!chan->sc_filter) {
++ chan->sc_filter = kzalloc(sizeof(struct lttng_syscall_filter),
++ GFP_KERNEL);
++ if (!chan->sc_filter)
++ return -ENOMEM;
++ }
++
+ if (!chan->sys_enter_registered) {
+ ret = lttng_wrapper_tracepoint_probe_register("sys_enter",
+ (void *) syscall_entry_probe, chan);
+@@ -930,7 +932,11 @@ int lttng_syscalls_unregister(struct lttng_channel *chan)
+ return ret;
+ chan->sys_exit_registered = 0;
+ }
+- /* lttng_event destroy will be performed by lttng_session_destroy() */
++ return 0;
++}
++
++int lttng_syscalls_destroy(struct lttng_channel *chan)
++{
+ kfree(chan->sc_table);
+ kfree(chan->sc_exit_table);
+ #ifdef CONFIG_COMPAT
+@@ -993,136 +999,150 @@ uint32_t get_sc_tables_len(void)
+ return ARRAY_SIZE(sc_table) + ARRAY_SIZE(compat_sc_table);
+ }
+
+-int lttng_syscall_filter_enable(struct lttng_channel *chan,
+- const char *name)
++static
++const char *get_syscall_name(struct lttng_event *event)
+ {
+- int syscall_nr, compat_syscall_nr, ret;
+- struct lttng_syscall_filter *filter;
++ size_t prefix_len = 0;
+
+- WARN_ON_ONCE(!chan->sc_table);
++ WARN_ON_ONCE(event->instrumentation != LTTNG_KERNEL_SYSCALL);
+
+- 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);
++ switch (event->u.syscall.entryexit) {
++ case LTTNG_SYSCALL_ENTRY:
++ switch (event->u.syscall.abi) {
++ case LTTNG_SYSCALL_ABI_NATIVE:
++ prefix_len = strlen(SYSCALL_ENTRY_STR);
++ break;
++ case LTTNG_SYSCALL_ABI_COMPAT:
++ prefix_len = strlen(COMPAT_SYSCALL_ENTRY_STR);
++ break;
+ }
+- chan->syscall_all = 1;
+- return 0;
+- }
+-
+- if (!chan->sc_filter) {
+- if (chan->syscall_all) {
+- /*
+- * All syscalls are already enabled.
+- */
+- return -EEXIST;
++ break;
++ case LTTNG_SYSCALL_EXIT:
++ switch (event->u.syscall.abi) {
++ case LTTNG_SYSCALL_ABI_NATIVE:
++ prefix_len = strlen(SYSCALL_EXIT_STR);
++ break;
++ case LTTNG_SYSCALL_ABI_COMPAT:
++ prefix_len = strlen(COMPAT_SYSCALL_EXIT_STR);
++ break;
+ }
+- filter = kzalloc(sizeof(struct lttng_syscall_filter),
+- GFP_KERNEL);
+- if (!filter)
+- return -ENOMEM;
+- } else {
+- filter = chan->sc_filter;
++ break;
+ }
+- 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;
++ WARN_ON_ONCE(prefix_len == 0);
++ return event->desc->name + prefix_len;
++}
++
++int lttng_syscall_filter_enable(struct lttng_channel *chan,
++ struct lttng_event *event)
++{
++ struct lttng_syscall_filter *filter = chan->sc_filter;
++ const char *syscall_name;
++ unsigned long *bitmap;
++ int syscall_nr;
++
++ WARN_ON_ONCE(!chan->sc_table);
++
++ syscall_name = get_syscall_name(event);
++
++ switch (event->u.syscall.abi) {
++ case LTTNG_SYSCALL_ABI_NATIVE:
++ syscall_nr = get_syscall_nr(syscall_name);
++ break;
++ case LTTNG_SYSCALL_ABI_COMPAT:
++ syscall_nr = get_compat_syscall_nr(syscall_name);
++ break;
++ default:
++ return -EINVAL;
+ }
+- if (syscall_nr >= 0) {
+- if (test_bit(syscall_nr, filter->sc)) {
+- ret = -EEXIST;
+- goto error;
++ if (syscall_nr < 0)
++ return -ENOENT;
++
++
++ switch (event->u.syscall.entryexit) {
++ case LTTNG_SYSCALL_ENTRY:
++ switch (event->u.syscall.abi) {
++ case LTTNG_SYSCALL_ABI_NATIVE:
++ bitmap = filter->sc_entry;
++ break;
++ case LTTNG_SYSCALL_ABI_COMPAT:
++ bitmap = filter->sc_compat_entry;
++ break;
+ }
+- 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;
++ break;
++ case LTTNG_SYSCALL_EXIT:
++ switch (event->u.syscall.abi) {
++ case LTTNG_SYSCALL_ABI_NATIVE:
++ bitmap = filter->sc_exit;
++ break;
++ case LTTNG_SYSCALL_ABI_COMPAT:
++ bitmap = filter->sc_compat_exit;
++ break;
+ }
+- bitmap_set(filter->sc_compat, compat_syscall_nr, 1);
++ break;
++ default:
++ return -EINVAL;
+ }
+- if (!chan->sc_filter)
+- rcu_assign_pointer(chan->sc_filter, filter);
++ if (test_bit(syscall_nr, bitmap))
++ return -EEXIST;
++ bitmap_set(bitmap, syscall_nr, 1);
+ return 0;
+-
+-error:
+- if (!chan->sc_filter)
+- kfree(filter);
+- return ret;
+ }
+
+ int lttng_syscall_filter_disable(struct lttng_channel *chan,
+- const char *name)
++ struct lttng_event *event)
+ {
+- int syscall_nr, compat_syscall_nr, ret;
+- struct lttng_syscall_filter *filter;
++ struct lttng_syscall_filter *filter = chan->sc_filter;
++ const char *syscall_name;
++ unsigned long *bitmap;
++ int syscall_nr;
+
+ WARN_ON_ONCE(!chan->sc_table);
+
+- if (!chan->sc_filter) {
+- if (!chan->syscall_all)
+- return -EEXIST;
+- 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_name = get_syscall_name(event);
++
++ switch (event->u.syscall.abi) {
++ case LTTNG_SYSCALL_ABI_NATIVE:
++ syscall_nr = get_syscall_nr(syscall_name);
++ break;
++ case LTTNG_SYSCALL_ABI_COMPAT:
++ syscall_nr = get_compat_syscall_nr(syscall_name);
++ break;
++ default:
++ return -EINVAL;
+ }
++ if (syscall_nr < 0)
++ return -ENOENT;
+
+- if (!name) {
+- /* Fail if all syscalls are already disabled. */
+- if (bitmap_empty(filter->sc, NR_syscalls)
+- && bitmap_empty(filter->sc_compat,
+- NR_compat_syscalls)) {
+- ret = -EEXIST;
+- goto error;
+- }
+
+- /* Disable all system calls */
+- bitmap_clear(filter->sc, 0, NR_syscalls);
+- bitmap_clear(filter->sc_compat, 0, NR_compat_syscalls);
+- goto apply_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;
++ switch (event->u.syscall.entryexit) {
++ case LTTNG_SYSCALL_ENTRY:
++ switch (event->u.syscall.abi) {
++ case LTTNG_SYSCALL_ABI_NATIVE:
++ bitmap = filter->sc_entry;
++ break;
++ case LTTNG_SYSCALL_ABI_COMPAT:
++ bitmap = filter->sc_compat_entry;
++ break;
+ }
+- bitmap_clear(filter->sc, syscall_nr, 1);
+- }
+- if (compat_syscall_nr >= 0) {
+- if (!test_bit(compat_syscall_nr, filter->sc_compat)) {
+- ret = -EEXIST;
+- goto error;
++ break;
++ case LTTNG_SYSCALL_EXIT:
++ switch (event->u.syscall.abi) {
++ case LTTNG_SYSCALL_ABI_NATIVE:
++ bitmap = filter->sc_exit;
++ break;
++ case LTTNG_SYSCALL_ABI_COMPAT:
++ bitmap = filter->sc_compat_exit;
++ break;
+ }
+- bitmap_clear(filter->sc_compat, compat_syscall_nr, 1);
++ break;
++ default:
++ return -EINVAL;
+ }
+-apply_filter:
+- if (!chan->sc_filter)
+- rcu_assign_pointer(chan->sc_filter, filter);
+- chan->syscall_all = 0;
+- return 0;
++ if (!test_bit(syscall_nr, bitmap))
++ return -EEXIST;
++ bitmap_clear(bitmap, syscall_nr, 1);
+
+-error:
+- if (!chan->sc_filter)
+- kfree(filter);
+- return ret;
++ return 0;
+ }
+
+ static
+@@ -1236,6 +1256,9 @@ const struct file_operations lttng_syscall_list_fops = {
+ .release = seq_release,
+ };
+
++/*
++ * A syscall is enabled if it is traced for either entry or exit.
++ */
+ long lttng_channel_syscall_mask(struct lttng_channel *channel,
+ struct lttng_kernel_syscall_mask __user *usyscall_mask)
+ {
+@@ -1262,8 +1285,9 @@ long lttng_channel_syscall_mask(struct lttng_channel *channel,
+ char state;
+
+ if (channel->sc_table) {
+- if (filter)
+- state = test_bit(bit, filter->sc);
++ if (!READ_ONCE(channel->syscall_all) && filter)
++ state = test_bit(bit, filter->sc_entry)
++ || test_bit(bit, filter->sc_exit);
+ else
+ state = 1;
+ } else {
+@@ -1275,9 +1299,11 @@ long lttng_channel_syscall_mask(struct lttng_channel *channel,
+ char state;
+
+ if (channel->compat_sc_table) {
+- if (filter)
++ if (!READ_ONCE(channel->syscall_all) && filter)
+ state = test_bit(bit - ARRAY_SIZE(sc_table),
+- filter->sc_compat);
++ filter->sc_compat_entry)
++ || test_bit(bit - ARRAY_SIZE(sc_table),
++ filter->sc_compat_exit);
+ else
+ state = 1;
+ } else {
+--
+2.19.1
+