summaryrefslogtreecommitdiffstats
path: root/src/analyze/analyze-syscall-filter.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/analyze/analyze-syscall-filter.c202
1 files changed, 202 insertions, 0 deletions
diff --git a/src/analyze/analyze-syscall-filter.c b/src/analyze/analyze-syscall-filter.c
new file mode 100644
index 0000000..66a52da
--- /dev/null
+++ b/src/analyze/analyze-syscall-filter.c
@@ -0,0 +1,202 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "analyze-syscall-filter.h"
+#include "analyze.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "nulstr-util.h"
+#include "seccomp-util.h"
+#include "set.h"
+#include "strv.h"
+#include "terminal-util.h"
+
+#if HAVE_SECCOMP
+
+static int load_kernel_syscalls(Set **ret) {
+ _cleanup_set_free_ Set *syscalls = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ int r;
+
+ /* Let's read the available system calls from the list of available tracing events. Slightly dirty,
+ * but good enough for analysis purposes. */
+
+ f = fopen("/sys/kernel/tracing/available_events", "re");
+ if (!f) {
+ /* We tried the non-debugfs mount point and that didn't work. If it wasn't mounted, maybe the
+ * old debugfs mount point works? */
+ f = fopen("/sys/kernel/debug/tracing/available_events", "re");
+ if (!f)
+ return log_full_errno(IN_SET(errno, EPERM, EACCES, ENOENT) ? LOG_DEBUG : LOG_WARNING, errno,
+ "Can't read open tracefs' available_events file: %m");
+ }
+
+ for (;;) {
+ _cleanup_free_ char *line = NULL;
+ const char *e;
+
+ r = read_line(f, LONG_LINE_MAX, &line);
+ if (r < 0)
+ return log_error_errno(r, "Failed to read system call list: %m");
+ if (r == 0)
+ break;
+
+ e = startswith(line, "syscalls:sys_enter_");
+ if (!e)
+ continue;
+
+ /* These are named differently inside the kernel than their external name for historical
+ * reasons. Let's hide them here. */
+ if (STR_IN_SET(e, "newuname", "newfstat", "newstat", "newlstat", "sysctl"))
+ continue;
+
+ r = set_put_strdup(&syscalls, e);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add system call to list: %m");
+ }
+
+ *ret = TAKE_PTR(syscalls);
+ return 0;
+}
+
+static int syscall_set_add(Set **s, const SyscallFilterSet *set) {
+ int r;
+
+ assert(s);
+
+ if (!set)
+ return 0;
+
+ NULSTR_FOREACH(sc, set->value) {
+ if (sc[0] == '@')
+ continue;
+
+ r = set_put_strdup(s, sc);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static void syscall_set_remove(Set *s, const SyscallFilterSet *set) {
+ if (!set)
+ return;
+
+ NULSTR_FOREACH(sc, set->value) {
+ if (sc[0] == '@')
+ continue;
+
+ free(set_remove(s, sc));
+ }
+}
+
+static void dump_syscall_filter(const SyscallFilterSet *set) {
+ printf("%s%s%s\n"
+ " # %s\n",
+ ansi_highlight(),
+ set->name,
+ ansi_normal(),
+ set->help);
+
+ NULSTR_FOREACH(syscall, set->value)
+ printf(" %s%s%s\n", syscall[0] == '@' ? ansi_underline() : "", syscall, ansi_normal());
+}
+
+int verb_syscall_filters(int argc, char *argv[], void *userdata) {
+ bool first = true;
+ int r;
+
+ pager_open(arg_pager_flags);
+
+ if (strv_isempty(strv_skip(argv, 1))) {
+ _cleanup_set_free_ Set *kernel = NULL, *known = NULL;
+ int k = 0; /* explicit initialization to appease gcc */
+
+ r = syscall_set_add(&known, syscall_filter_sets + SYSCALL_FILTER_SET_KNOWN);
+ if (r < 0)
+ return log_error_errno(r, "Failed to prepare set of known system calls: %m");
+
+ if (!arg_quiet)
+ k = load_kernel_syscalls(&kernel);
+
+ for (int i = 0; i < _SYSCALL_FILTER_SET_MAX; i++) {
+ const SyscallFilterSet *set = syscall_filter_sets + i;
+ if (!first)
+ puts("");
+
+ dump_syscall_filter(set);
+ syscall_set_remove(kernel, set);
+ if (i != SYSCALL_FILTER_SET_KNOWN)
+ syscall_set_remove(known, set);
+ first = false;
+ }
+
+ if (arg_quiet) /* Let's not show the extra stuff in quiet mode */
+ return 0;
+
+ if (!set_isempty(known)) {
+ _cleanup_free_ char **l = NULL;
+
+ printf("\n"
+ "# %sUngrouped System Calls%s (known but not included in any of the groups except @known):\n",
+ ansi_highlight(), ansi_normal());
+
+ l = set_get_strv(known);
+ if (!l)
+ return log_oom();
+
+ strv_sort(l);
+
+ STRV_FOREACH(syscall, l)
+ printf("# %s\n", *syscall);
+ }
+
+ if (k < 0) {
+ fputc('\n', stdout);
+ fflush(stdout);
+ if (!arg_quiet)
+ log_notice_errno(k, "# Not showing unlisted system calls, couldn't retrieve kernel system call list: %m");
+ } else if (!set_isempty(kernel)) {
+ _cleanup_free_ char **l = NULL;
+
+ printf("\n"
+ "# %sUnlisted System Calls%s (supported by the local kernel, but not included in any of the groups listed above):\n",
+ ansi_highlight(), ansi_normal());
+
+ l = set_get_strv(kernel);
+ if (!l)
+ return log_oom();
+
+ strv_sort(l);
+
+ STRV_FOREACH(syscall, l)
+ printf("# %s\n", *syscall);
+ }
+ } else
+ STRV_FOREACH(name, strv_skip(argv, 1)) {
+ const SyscallFilterSet *set;
+
+ if (!first)
+ puts("");
+
+ set = syscall_filter_set_find(*name);
+ if (!set) {
+ /* make sure the error appears below normal output */
+ fflush(stdout);
+
+ return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
+ "Filter set \"%s\" not found.", *name);
+ }
+
+ dump_syscall_filter(set);
+ first = false;
+ }
+
+ return EXIT_SUCCESS;
+}
+
+#else
+int verb_syscall_filters(int argc, char *argv[], void *userdata) {
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Not compiled with syscall filters, sorry.");
+}
+#endif