Fix: system call filter table
authorMathieu Desnoyers <mathieu.desnoyers@efficios.com>
Tue, 28 Jan 2020 21:02:44 +0000 (16:02 -0500)
committerMathieu Desnoyers <mathieu.desnoyers@efficios.com>
Tue, 8 Sep 2020 13:44:21 +0000 (09:44 -0400)
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,

Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
include/lttng/abi.h
include/lttng/events.h
src/lttng-abi.c
src/lttng-events.c
src/lttng-syscalls.c

index 7456f4a12550b1ef3f31d6c9a31265b1b5ff660e..086a5834fe764b9e0342b7a4c72c296be1ae1903 100644 (file)
@@ -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));
index 4c376b395da8f9e22769ef5e17176aa23321d254..3ba8525f3f2a7da566f50cc04bfa58ef25320c10 100644 (file)
@@ -273,6 +273,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.
@@ -299,6 +309,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;
@@ -438,10 +453,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 */
 };
 
@@ -634,10 +649,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
@@ -651,14 +667,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;
 }
index 198987bd5a74f25775ef0e269699a11182b6bdc0..219d20a044e284c63f1688a9a06df514b28bceb6 100644 (file)
@@ -1266,6 +1266,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)
@@ -1307,6 +1347,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;
index 3986217f73f242ea1ded61a37c9bb9391dc212bc..5023d450b1bd77f50969776c32b8e2735a192317 100644 (file)
@@ -205,6 +205,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);
@@ -744,6 +748,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;
@@ -830,8 +856,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:
@@ -874,8 +899,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;
@@ -1207,39 +1231,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
@@ -1365,9 +1437,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);
 
index 0c28a467872b27d07bec36ba5ef4dbbed21cc16f..6d0f3554509a74304a6db6cd74210a9c1bb9a6db 100644 (file)
@@ -369,8 +369,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,
@@ -393,29 +395,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);
@@ -547,29 +543,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);
@@ -715,27 +705,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,
@@ -805,6 +791,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);
@@ -822,6 +810,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);
@@ -839,6 +829,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);
@@ -856,6 +848,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);
@@ -885,6 +879,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);
@@ -932,7 +934,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
@@ -995,136 +1001,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
@@ -1238,6 +1258,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)
 {
@@ -1264,8 +1287,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 {
@@ -1277,9 +1301,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 {
This page took 0.036788 seconds and 4 git commands to generate.