It is currently impossible to enable `SECCOMP_MODE_STRICT` if `SECCOMP_MODE_FILTER` is enabled, and vice-versa. This makes using seccomp difficult in environments such as Docker, which installs a seccomp filter by default.
Introduce a new internal `SECCOMP_MODE_COMBINED` that runs `strict` checks, followed by any installed filters. Link: https://github.com/moby/moby/issues/42082 Signed-off-by: Jamie Hill-Daniel <[email protected]> --- kernel/seccomp.c | 46 ++++++++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/kernel/seccomp.c b/kernel/seccomp.c index 25f62867a16d..8201a050d358 100644 --- a/kernel/seccomp.c +++ b/kernel/seccomp.c @@ -33,6 +33,8 @@ /* Not exposed in headers: strictly internal use only. */ #define SECCOMP_MODE_DEAD (SECCOMP_MODE_FILTER + 1) +/* Run SECCOMP_MODE_STRICT checks, followed by SECCOMP_MODE_FILTER */ +#define SECCOMP_MODE_COMBINED (SECCOMP_MODE_DEAD + 1) #ifdef CONFIG_SECCOMP_FILTER #include <linux/file.h> @@ -432,14 +434,21 @@ static u32 seccomp_run_filters(const struct seccomp_data *sd, } #endif /* CONFIG_SECCOMP_FILTER */ -static inline bool seccomp_may_assign_mode(unsigned long seccomp_mode) +/** + * seccomp_needs_combined: internal function for checking if requested mode + * needs to be upgraded to `SECCOMP_MODE_COMBINED`. + * + */ +static inline bool seccomp_needs_combined(unsigned long seccomp_mode) { assert_spin_locked(¤t->sighand->siglock); - if (current->seccomp.mode && current->seccomp.mode != seccomp_mode) - return false; + if ((current->seccomp.mode == SECCOMP_MODE_STRICT || + current->seccomp.mode == SECCOMP_MODE_FILTER) && + current->seccomp.mode != seccomp_mode) + return true; - return true; + return false; } void __weak arch_seccomp_spec_mitigate(struct task_struct *task) { } @@ -1407,6 +1416,9 @@ int __secure_computing(void) WARN_ON_ONCE(1); do_exit(SIGKILL); return -1; + case SECCOMP_MODE_COMBINED: + __secure_computing_strict(this_syscall); + return __seccomp_filter(this_syscall, false); default: BUG(); } @@ -1421,30 +1433,23 @@ long prctl_get_seccomp(void) /** * seccomp_set_mode_strict: internal function for setting strict seccomp * - * Once current->seccomp.mode is non-zero, it may not be changed. + * Once current->seccomp.mode is non-zero, it may only be changed to `COMBINED` or `DEAD`. * - * Returns 0 on success or -EINVAL on failure. */ -static long seccomp_set_mode_strict(void) +static void seccomp_set_mode_strict(void) { - const unsigned long seccomp_mode = SECCOMP_MODE_STRICT; - long ret = -EINVAL; + unsigned long seccomp_mode = SECCOMP_MODE_STRICT; spin_lock_irq(¤t->sighand->siglock); - if (!seccomp_may_assign_mode(seccomp_mode)) - goto out; + if (seccomp_needs_combined(seccomp_mode)) + seccomp_mode = SECCOMP_MODE_COMBINED; #ifdef TIF_NOTSC disable_TSC(); #endif seccomp_assign_mode(current, seccomp_mode, 0); - ret = 0; - -out: spin_unlock_irq(¤t->sighand->siglock); - - return ret; } #ifdef CONFIG_SECCOMP_FILTER @@ -1956,7 +1961,7 @@ static bool has_duplicate_listener(struct seccomp_filter *new_child) static long seccomp_set_mode_filter(unsigned int flags, const char __user *filter) { - const unsigned long seccomp_mode = SECCOMP_MODE_FILTER; + long seccomp_mode = SECCOMP_MODE_FILTER; struct seccomp_filter *prepared = NULL; long ret = -EINVAL; int listener = -1; @@ -2016,8 +2021,8 @@ static long seccomp_set_mode_filter(unsigned int flags, spin_lock_irq(¤t->sighand->siglock); - if (!seccomp_may_assign_mode(seccomp_mode)) - goto out; + if (seccomp_needs_combined(seccomp_mode)) + seccomp_mode = SECCOMP_MODE_COMBINED; if (has_duplicate_listener(prepared)) { ret = -EBUSY; @@ -2105,7 +2110,8 @@ static long do_seccomp(unsigned int op, unsigned int flags, case SECCOMP_SET_MODE_STRICT: if (flags != 0 || uargs != NULL) return -EINVAL; - return seccomp_set_mode_strict(); + seccomp_set_mode_strict(); + return 0; case SECCOMP_SET_MODE_FILTER: return seccomp_set_mode_filter(flags, uargs); case SECCOMP_GET_ACTION_AVAIL: -- 2.53.0

