seccomp/cache: Lookup syscall allowlist bitmap for fast path
[linux-block.git] / kernel / seccomp.c
index 8ad7a293255a02de60665b68726576c3b07cdd9e..fe35f4f389496e7c512e9b5b6513e3b5e9d9ae63 100644 (file)
@@ -143,6 +143,34 @@ struct notification {
        struct list_head notifications;
 };
 
+#ifdef SECCOMP_ARCH_NATIVE
+/**
+ * struct action_cache - per-filter cache of seccomp actions per
+ * arch/syscall pair
+ *
+ * @allow_native: A bitmap where each bit represents whether the
+ *               filter will always allow the syscall, for the
+ *               native architecture.
+ * @allow_compat: A bitmap where each bit represents whether the
+ *               filter will always allow the syscall, for the
+ *               compat architecture.
+ */
+struct action_cache {
+       DECLARE_BITMAP(allow_native, SECCOMP_ARCH_NATIVE_NR);
+#ifdef SECCOMP_ARCH_COMPAT
+       DECLARE_BITMAP(allow_compat, SECCOMP_ARCH_COMPAT_NR);
+#endif
+};
+#else
+struct action_cache { };
+
+static inline bool seccomp_cache_check_allow(const struct seccomp_filter *sfilter,
+                                            const struct seccomp_data *sd)
+{
+       return false;
+}
+#endif /* SECCOMP_ARCH_NATIVE */
+
 /**
  * struct seccomp_filter - container for seccomp BPF programs
  *
@@ -298,6 +326,52 @@ static int seccomp_check_filter(struct sock_filter *filter, unsigned int flen)
        return 0;
 }
 
+#ifdef SECCOMP_ARCH_NATIVE
+static inline bool seccomp_cache_check_allow_bitmap(const void *bitmap,
+                                                   size_t bitmap_size,
+                                                   int syscall_nr)
+{
+       if (unlikely(syscall_nr < 0 || syscall_nr >= bitmap_size))
+               return false;
+       syscall_nr = array_index_nospec(syscall_nr, bitmap_size);
+
+       return test_bit(syscall_nr, bitmap);
+}
+
+/**
+ * seccomp_cache_check_allow - lookup seccomp cache
+ * @sfilter: The seccomp filter
+ * @sd: The seccomp data to lookup the cache with
+ *
+ * Returns true if the seccomp_data is cached and allowed.
+ */
+static inline bool seccomp_cache_check_allow(const struct seccomp_filter *sfilter,
+                                            const struct seccomp_data *sd)
+{
+       int syscall_nr = sd->nr;
+       const struct action_cache *cache = &sfilter->cache;
+
+#ifndef SECCOMP_ARCH_COMPAT
+       /* A native-only architecture doesn't need to check sd->arch. */
+       return seccomp_cache_check_allow_bitmap(cache->allow_native,
+                                               SECCOMP_ARCH_NATIVE_NR,
+                                               syscall_nr);
+#else
+       if (likely(sd->arch == SECCOMP_ARCH_NATIVE))
+               return seccomp_cache_check_allow_bitmap(cache->allow_native,
+                                                       SECCOMP_ARCH_NATIVE_NR,
+                                                       syscall_nr);
+       if (likely(sd->arch == SECCOMP_ARCH_COMPAT))
+               return seccomp_cache_check_allow_bitmap(cache->allow_compat,
+                                                       SECCOMP_ARCH_COMPAT_NR,
+                                                       syscall_nr);
+#endif /* SECCOMP_ARCH_COMPAT */
+
+       WARN_ON_ONCE(true);
+       return false;
+}
+#endif /* SECCOMP_ARCH_NATIVE */
+
 /**
  * seccomp_run_filters - evaluates all seccomp filters against @sd
  * @sd: optional seccomp data to be passed to filters
@@ -320,6 +394,9 @@ static u32 seccomp_run_filters(const struct seccomp_data *sd,
        if (WARN_ON(f == NULL))
                return SECCOMP_RET_KILL_PROCESS;
 
+       if (seccomp_cache_check_allow(f, sd))
+               return SECCOMP_RET_ALLOW;
+
        /*
         * All filters in the list are evaluated and the lowest BPF return
         * value always takes priority (ignoring the DATA).