diff options
author | Will Drewry <wad@chromium.org> | 2012-04-12 16:47:59 -0500 |
---|---|---|
committer | James Morris <james.l.morris@oracle.com> | 2012-04-14 11:13:21 +1000 |
commit | acf3b2c71ed20c53dc69826683417703c2a88059 (patch) | |
tree | 99ced75da46a0ab7953f0c173dd885c09f570fc0 /kernel/seccomp.c | |
parent | 3dc1c1b2d2ed7507ce8a379814ad75745ff97ebe (diff) | |
download | linux-acf3b2c71ed20c53dc69826683417703c2a88059.tar.gz linux-acf3b2c71ed20c53dc69826683417703c2a88059.tar.bz2 linux-acf3b2c71ed20c53dc69826683417703c2a88059.zip |
seccomp: add SECCOMP_RET_ERRNO
This change adds the SECCOMP_RET_ERRNO as a valid return value from a
seccomp filter. Additionally, it makes the first use of the lower
16-bits for storing a filter-supplied errno. 16-bits is more than
enough for the errno-base.h calls.
Returning errors instead of immediately terminating processes that
violate seccomp policy allow for broader use of this functionality
for kernel attack surface reduction. For example, a linux container
could maintain a whitelist of pre-existing system calls but drop
all new ones with errnos. This would keep a logically static attack
surface while providing errnos that may allow for graceful failure
without the downside of do_exit() on a bad call.
This change also changes the signature of __secure_computing. It
appears the only direct caller is the arm entry code and it clobbers
any possible return value (register) immediately.
Signed-off-by: Will Drewry <wad@chromium.org>
Acked-by: Serge Hallyn <serge.hallyn@canonical.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
Acked-by: Eric Paris <eparis@redhat.com>
v18: - fix up comments and rebase
- fix bad var name which was fixed in later revs
- remove _int() and just change the __secure_computing signature
v16-v17: ...
v15: - use audit_seccomp and add a skip label. (eparis@redhat.com)
- clean up and pad out return codes (indan@nul.nu)
v14: - no change/rebase
v13: - rebase on to 88ebdda6159ffc15699f204c33feb3e431bf9bdc
v12: - move to WARN_ON if filter is NULL
(oleg@redhat.com, luto@mit.edu, keescook@chromium.org)
- return immediately for filter==NULL (keescook@chromium.org)
- change evaluation to only compare the ACTION so that layered
errnos don't result in the lowest one being returned.
(keeschook@chromium.org)
v11: - check for NULL filter (keescook@chromium.org)
v10: - change loaders to fn
v9: - n/a
v8: - update Kconfig to note new need for syscall_set_return_value.
- reordered such that TRAP behavior follows on later.
- made the for loop a little less indent-y
v7: - introduced
Signed-off-by: James Morris <james.l.morris@oracle.com>
Diffstat (limited to 'kernel/seccomp.c')
-rw-r--r-- | kernel/seccomp.c | 42 |
1 files changed, 32 insertions, 10 deletions
diff --git a/kernel/seccomp.c b/kernel/seccomp.c index 0f7c709a523e..5f78fb6d2212 100644 --- a/kernel/seccomp.c +++ b/kernel/seccomp.c @@ -199,15 +199,20 @@ static int seccomp_check_filter(struct sock_filter *filter, unsigned int flen) static u32 seccomp_run_filters(int syscall) { struct seccomp_filter *f; - u32 ret = SECCOMP_RET_KILL; + u32 ret = SECCOMP_RET_ALLOW; + + /* Ensure unexpected behavior doesn't result in failing open. */ + if (WARN_ON(current->seccomp.filter == NULL)) + return SECCOMP_RET_KILL; + /* * All filters in the list are evaluated and the lowest BPF return - * value always takes priority. + * value always takes priority (ignoring the DATA). */ for (f = current->seccomp.filter; f; f = f->prev) { - ret = sk_run_filter(NULL, f->insns); - if (ret != SECCOMP_RET_ALLOW) - break; + u32 cur_ret = sk_run_filter(NULL, f->insns); + if ((cur_ret & SECCOMP_RET_ACTION) < (ret & SECCOMP_RET_ACTION)) + ret = cur_ret; } return ret; } @@ -346,11 +351,13 @@ static int mode1_syscalls_32[] = { }; #endif -void __secure_computing(int this_syscall) +int __secure_computing(int this_syscall) { int mode = current->seccomp.mode; int exit_sig = 0; int *syscall; + u32 ret = SECCOMP_RET_KILL; + int data; switch (mode) { case SECCOMP_MODE_STRICT: @@ -361,14 +368,26 @@ void __secure_computing(int this_syscall) #endif do { if (*syscall == this_syscall) - return; + return 0; } while (*++syscall); exit_sig = SIGKILL; break; #ifdef CONFIG_SECCOMP_FILTER case SECCOMP_MODE_FILTER: - if (seccomp_run_filters(this_syscall) == SECCOMP_RET_ALLOW) - return; + ret = seccomp_run_filters(this_syscall); + data = ret & SECCOMP_RET_DATA; + switch (ret & SECCOMP_RET_ACTION) { + case SECCOMP_RET_ERRNO: + /* Set the low-order 16-bits as a errno. */ + syscall_set_return_value(current, task_pt_regs(current), + -data, 0); + goto skip; + case SECCOMP_RET_ALLOW: + return 0; + case SECCOMP_RET_KILL: + default: + break; + } exit_sig = SIGSYS; break; #endif @@ -379,8 +398,11 @@ void __secure_computing(int this_syscall) #ifdef SECCOMP_DEBUG dump_stack(); #endif - audit_seccomp(this_syscall, exit_code, SECCOMP_RET_KILL); + audit_seccomp(this_syscall, exit_sig, ret); do_exit(exit_sig); +skip: + audit_seccomp(this_syscall, exit_sig, ret); + return -1; } long prctl_get_seccomp(void) |