diff options
author | Will Drewry <wad@chromium.org> | 2012-04-12 17:47:59 -0400 |
---|---|---|
committer | James Morris <james.l.morris@oracle.com> | 2012-04-13 21:13:21 -0400 |
commit | acf3b2c71ed20c53dc69826683417703c2a88059 (patch) | |
tree | 99ced75da46a0ab7953f0c173dd885c09f570fc0 /kernel | |
parent | 3dc1c1b2d2ed7507ce8a379814ad75745ff97ebe (diff) |
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')
-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) | |||
199 | static u32 seccomp_run_filters(int syscall) | 199 | static u32 seccomp_run_filters(int syscall) |
200 | { | 200 | { |
201 | struct seccomp_filter *f; | 201 | struct seccomp_filter *f; |
202 | u32 ret = SECCOMP_RET_KILL; | 202 | u32 ret = SECCOMP_RET_ALLOW; |
203 | |||
204 | /* Ensure unexpected behavior doesn't result in failing open. */ | ||
205 | if (WARN_ON(current->seccomp.filter == NULL)) | ||
206 | return SECCOMP_RET_KILL; | ||
207 | |||
203 | /* | 208 | /* |
204 | * All filters in the list are evaluated and the lowest BPF return | 209 | * All filters in the list are evaluated and the lowest BPF return |
205 | * value always takes priority. | 210 | * value always takes priority (ignoring the DATA). |
206 | */ | 211 | */ |
207 | for (f = current->seccomp.filter; f; f = f->prev) { | 212 | for (f = current->seccomp.filter; f; f = f->prev) { |
208 | ret = sk_run_filter(NULL, f->insns); | 213 | u32 cur_ret = sk_run_filter(NULL, f->insns); |
209 | if (ret != SECCOMP_RET_ALLOW) | 214 | if ((cur_ret & SECCOMP_RET_ACTION) < (ret & SECCOMP_RET_ACTION)) |
210 | break; | 215 | ret = cur_ret; |
211 | } | 216 | } |
212 | return ret; | 217 | return ret; |
213 | } | 218 | } |
@@ -346,11 +351,13 @@ static int mode1_syscalls_32[] = { | |||
346 | }; | 351 | }; |
347 | #endif | 352 | #endif |
348 | 353 | ||
349 | void __secure_computing(int this_syscall) | 354 | int __secure_computing(int this_syscall) |
350 | { | 355 | { |
351 | int mode = current->seccomp.mode; | 356 | int mode = current->seccomp.mode; |
352 | int exit_sig = 0; | 357 | int exit_sig = 0; |
353 | int *syscall; | 358 | int *syscall; |
359 | u32 ret = SECCOMP_RET_KILL; | ||
360 | int data; | ||
354 | 361 | ||
355 | switch (mode) { | 362 | switch (mode) { |
356 | case SECCOMP_MODE_STRICT: | 363 | case SECCOMP_MODE_STRICT: |
@@ -361,14 +368,26 @@ void __secure_computing(int this_syscall) | |||
361 | #endif | 368 | #endif |
362 | do { | 369 | do { |
363 | if (*syscall == this_syscall) | 370 | if (*syscall == this_syscall) |
364 | return; | 371 | return 0; |
365 | } while (*++syscall); | 372 | } while (*++syscall); |
366 | exit_sig = SIGKILL; | 373 | exit_sig = SIGKILL; |
367 | break; | 374 | break; |
368 | #ifdef CONFIG_SECCOMP_FILTER | 375 | #ifdef CONFIG_SECCOMP_FILTER |
369 | case SECCOMP_MODE_FILTER: | 376 | case SECCOMP_MODE_FILTER: |
370 | if (seccomp_run_filters(this_syscall) == SECCOMP_RET_ALLOW) | 377 | ret = seccomp_run_filters(this_syscall); |
371 | return; | 378 | data = ret & SECCOMP_RET_DATA; |
379 | switch (ret & SECCOMP_RET_ACTION) { | ||
380 | case SECCOMP_RET_ERRNO: | ||
381 | /* Set the low-order 16-bits as a errno. */ | ||
382 | syscall_set_return_value(current, task_pt_regs(current), | ||
383 | -data, 0); | ||
384 | goto skip; | ||
385 | case SECCOMP_RET_ALLOW: | ||
386 | return 0; | ||
387 | case SECCOMP_RET_KILL: | ||
388 | default: | ||
389 | break; | ||
390 | } | ||
372 | exit_sig = SIGSYS; | 391 | exit_sig = SIGSYS; |
373 | break; | 392 | break; |
374 | #endif | 393 | #endif |
@@ -379,8 +398,11 @@ void __secure_computing(int this_syscall) | |||
379 | #ifdef SECCOMP_DEBUG | 398 | #ifdef SECCOMP_DEBUG |
380 | dump_stack(); | 399 | dump_stack(); |
381 | #endif | 400 | #endif |
382 | audit_seccomp(this_syscall, exit_code, SECCOMP_RET_KILL); | 401 | audit_seccomp(this_syscall, exit_sig, ret); |
383 | do_exit(exit_sig); | 402 | do_exit(exit_sig); |
403 | skip: | ||
404 | audit_seccomp(this_syscall, exit_sig, ret); | ||
405 | return -1; | ||
384 | } | 406 | } |
385 | 407 | ||
386 | long prctl_get_seccomp(void) | 408 | long prctl_get_seccomp(void) |