aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorWill Drewry <wad@chromium.org>2012-04-12 17:47:59 -0400
committerJames Morris <james.l.morris@oracle.com>2012-04-13 21:13:21 -0400
commitacf3b2c71ed20c53dc69826683417703c2a88059 (patch)
tree99ced75da46a0ab7953f0c173dd885c09f570fc0 /kernel
parent3dc1c1b2d2ed7507ce8a379814ad75745ff97ebe (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.c42
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)
199static u32 seccomp_run_filters(int syscall) 199static 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
349void __secure_computing(int this_syscall) 354int __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);
403skip:
404 audit_seccomp(this_syscall, exit_sig, ret);
405 return -1;
384} 406}
385 407
386long prctl_get_seccomp(void) 408long prctl_get_seccomp(void)