diff options
-rw-r--r-- | include/linux/seccomp.h | 6 | ||||
-rw-r--r-- | kernel/seccomp.c | 190 |
2 files changed, 130 insertions, 66 deletions
diff --git a/include/linux/seccomp.h b/include/linux/seccomp.h index aa3c040230be..38851085e481 100644 --- a/include/linux/seccomp.h +++ b/include/linux/seccomp.h | |||
@@ -35,6 +35,12 @@ static inline int secure_computing(void) | |||
35 | return __secure_computing(); | 35 | return __secure_computing(); |
36 | return 0; | 36 | return 0; |
37 | } | 37 | } |
38 | |||
39 | #define SECCOMP_PHASE1_OK 0 | ||
40 | #define SECCOMP_PHASE1_SKIP 1 | ||
41 | |||
42 | extern u32 seccomp_phase1(void); | ||
43 | int seccomp_phase2(u32 phase1_result); | ||
38 | #else | 44 | #else |
39 | extern void secure_computing_strict(int this_syscall); | 45 | extern void secure_computing_strict(int this_syscall); |
40 | #endif | 46 | #endif |
diff --git a/kernel/seccomp.c b/kernel/seccomp.c index 5e738e0dd2e9..6c8528ce9df9 100644 --- a/kernel/seccomp.c +++ b/kernel/seccomp.c | |||
@@ -21,8 +21,6 @@ | |||
21 | #include <linux/slab.h> | 21 | #include <linux/slab.h> |
22 | #include <linux/syscalls.h> | 22 | #include <linux/syscalls.h> |
23 | 23 | ||
24 | /* #define SECCOMP_DEBUG 1 */ | ||
25 | |||
26 | #ifdef CONFIG_HAVE_ARCH_SECCOMP_FILTER | 24 | #ifdef CONFIG_HAVE_ARCH_SECCOMP_FILTER |
27 | #include <asm/syscall.h> | 25 | #include <asm/syscall.h> |
28 | #endif | 26 | #endif |
@@ -601,10 +599,21 @@ void secure_computing_strict(int this_syscall) | |||
601 | #else | 599 | #else |
602 | int __secure_computing(void) | 600 | int __secure_computing(void) |
603 | { | 601 | { |
604 | struct pt_regs *regs = task_pt_regs(current); | 602 | u32 phase1_result = seccomp_phase1(); |
605 | int this_syscall = syscall_get_nr(current, regs); | 603 | |
606 | int exit_sig = 0; | 604 | if (likely(phase1_result == SECCOMP_PHASE1_OK)) |
607 | u32 ret; | 605 | return 0; |
606 | else if (likely(phase1_result == SECCOMP_PHASE1_SKIP)) | ||
607 | return -1; | ||
608 | else | ||
609 | return seccomp_phase2(phase1_result); | ||
610 | } | ||
611 | |||
612 | #ifdef CONFIG_SECCOMP_FILTER | ||
613 | static u32 __seccomp_phase1_filter(int this_syscall, struct pt_regs *regs) | ||
614 | { | ||
615 | u32 filter_ret, action; | ||
616 | int data; | ||
608 | 617 | ||
609 | /* | 618 | /* |
610 | * Make sure that any changes to mode from another thread have | 619 | * Make sure that any changes to mode from another thread have |
@@ -612,73 +621,122 @@ int __secure_computing(void) | |||
612 | */ | 621 | */ |
613 | rmb(); | 622 | rmb(); |
614 | 623 | ||
615 | switch (current->seccomp.mode) { | 624 | filter_ret = seccomp_run_filters(); |
625 | data = filter_ret & SECCOMP_RET_DATA; | ||
626 | action = filter_ret & SECCOMP_RET_ACTION; | ||
627 | |||
628 | switch (action) { | ||
629 | case SECCOMP_RET_ERRNO: | ||
630 | /* Set the low-order 16-bits as a errno. */ | ||
631 | syscall_set_return_value(current, regs, | ||
632 | -data, 0); | ||
633 | goto skip; | ||
634 | |||
635 | case SECCOMP_RET_TRAP: | ||
636 | /* Show the handler the original registers. */ | ||
637 | syscall_rollback(current, regs); | ||
638 | /* Let the filter pass back 16 bits of data. */ | ||
639 | seccomp_send_sigsys(this_syscall, data); | ||
640 | goto skip; | ||
641 | |||
642 | case SECCOMP_RET_TRACE: | ||
643 | return filter_ret; /* Save the rest for phase 2. */ | ||
644 | |||
645 | case SECCOMP_RET_ALLOW: | ||
646 | return SECCOMP_PHASE1_OK; | ||
647 | |||
648 | case SECCOMP_RET_KILL: | ||
649 | default: | ||
650 | audit_seccomp(this_syscall, SIGSYS, action); | ||
651 | do_exit(SIGSYS); | ||
652 | } | ||
653 | |||
654 | unreachable(); | ||
655 | |||
656 | skip: | ||
657 | audit_seccomp(this_syscall, 0, action); | ||
658 | return SECCOMP_PHASE1_SKIP; | ||
659 | } | ||
660 | #endif | ||
661 | |||
662 | /** | ||
663 | * seccomp_phase1() - run fast path seccomp checks on the current syscall | ||
664 | * | ||
665 | * This only reads pt_regs via the syscall_xyz helpers. The only change | ||
666 | * it will make to pt_regs is via syscall_set_return_value, and it will | ||
667 | * only do that if it returns SECCOMP_PHASE1_SKIP. | ||
668 | * | ||
669 | * It may also call do_exit or force a signal; these actions must be | ||
670 | * safe. | ||
671 | * | ||
672 | * If it returns SECCOMP_PHASE1_OK, the syscall passes checks and should | ||
673 | * be processed normally. | ||
674 | * | ||
675 | * If it returns SECCOMP_PHASE1_SKIP, then the syscall should not be | ||
676 | * invoked. In this case, seccomp_phase1 will have set the return value | ||
677 | * using syscall_set_return_value. | ||
678 | * | ||
679 | * If it returns anything else, then the return value should be passed | ||
680 | * to seccomp_phase2 from a context in which ptrace hooks are safe. | ||
681 | */ | ||
682 | u32 seccomp_phase1(void) | ||
683 | { | ||
684 | int mode = current->seccomp.mode; | ||
685 | struct pt_regs *regs = task_pt_regs(current); | ||
686 | int this_syscall = syscall_get_nr(current, regs); | ||
687 | |||
688 | switch (mode) { | ||
616 | case SECCOMP_MODE_STRICT: | 689 | case SECCOMP_MODE_STRICT: |
617 | __secure_computing_strict(this_syscall); | 690 | __secure_computing_strict(this_syscall); /* may call do_exit */ |
618 | return 0; | 691 | return SECCOMP_PHASE1_OK; |
619 | #ifdef CONFIG_SECCOMP_FILTER | 692 | #ifdef CONFIG_SECCOMP_FILTER |
620 | case SECCOMP_MODE_FILTER: { | 693 | case SECCOMP_MODE_FILTER: |
621 | int data; | 694 | return __seccomp_phase1_filter(this_syscall, regs); |
622 | ret = seccomp_run_filters(); | ||
623 | data = ret & SECCOMP_RET_DATA; | ||
624 | ret &= SECCOMP_RET_ACTION; | ||
625 | switch (ret) { | ||
626 | case SECCOMP_RET_ERRNO: | ||
627 | /* Set the low-order 16-bits as a errno. */ | ||
628 | syscall_set_return_value(current, regs, | ||
629 | -data, 0); | ||
630 | goto skip; | ||
631 | case SECCOMP_RET_TRAP: | ||
632 | /* Show the handler the original registers. */ | ||
633 | syscall_rollback(current, regs); | ||
634 | /* Let the filter pass back 16 bits of data. */ | ||
635 | seccomp_send_sigsys(this_syscall, data); | ||
636 | goto skip; | ||
637 | case SECCOMP_RET_TRACE: | ||
638 | /* Skip these calls if there is no tracer. */ | ||
639 | if (!ptrace_event_enabled(current, PTRACE_EVENT_SECCOMP)) { | ||
640 | syscall_set_return_value(current, regs, | ||
641 | -ENOSYS, 0); | ||
642 | goto skip; | ||
643 | } | ||
644 | /* Allow the BPF to provide the event message */ | ||
645 | ptrace_event(PTRACE_EVENT_SECCOMP, data); | ||
646 | /* | ||
647 | * The delivery of a fatal signal during event | ||
648 | * notification may silently skip tracer notification. | ||
649 | * Terminating the task now avoids executing a system | ||
650 | * call that may not be intended. | ||
651 | */ | ||
652 | if (fatal_signal_pending(current)) | ||
653 | break; | ||
654 | if (syscall_get_nr(current, regs) < 0) | ||
655 | goto skip; /* Explicit request to skip. */ | ||
656 | |||
657 | return 0; | ||
658 | case SECCOMP_RET_ALLOW: | ||
659 | return 0; | ||
660 | case SECCOMP_RET_KILL: | ||
661 | default: | ||
662 | break; | ||
663 | } | ||
664 | exit_sig = SIGSYS; | ||
665 | break; | ||
666 | } | ||
667 | #endif | 695 | #endif |
668 | default: | 696 | default: |
669 | BUG(); | 697 | BUG(); |
670 | } | 698 | } |
699 | } | ||
671 | 700 | ||
672 | #ifdef SECCOMP_DEBUG | 701 | /** |
673 | dump_stack(); | 702 | * seccomp_phase2() - finish slow path seccomp work for the current syscall |
674 | #endif | 703 | * @phase1_result: The return value from seccomp_phase1() |
675 | audit_seccomp(this_syscall, exit_sig, ret); | 704 | * |
676 | do_exit(exit_sig); | 705 | * This must be called from a context in which ptrace hooks can be used. |
677 | #ifdef CONFIG_SECCOMP_FILTER | 706 | * |
678 | skip: | 707 | * Returns 0 if the syscall should be processed or -1 to skip the syscall. |
679 | audit_seccomp(this_syscall, exit_sig, ret); | 708 | */ |
680 | return -1; | 709 | int seccomp_phase2(u32 phase1_result) |
681 | #endif | 710 | { |
711 | struct pt_regs *regs = task_pt_regs(current); | ||
712 | u32 action = phase1_result & SECCOMP_RET_ACTION; | ||
713 | int data = phase1_result & SECCOMP_RET_DATA; | ||
714 | |||
715 | BUG_ON(action != SECCOMP_RET_TRACE); | ||
716 | |||
717 | audit_seccomp(syscall_get_nr(current, regs), 0, action); | ||
718 | |||
719 | /* Skip these calls if there is no tracer. */ | ||
720 | if (!ptrace_event_enabled(current, PTRACE_EVENT_SECCOMP)) { | ||
721 | syscall_set_return_value(current, regs, | ||
722 | -ENOSYS, 0); | ||
723 | return -1; | ||
724 | } | ||
725 | |||
726 | /* Allow the BPF to provide the event message */ | ||
727 | ptrace_event(PTRACE_EVENT_SECCOMP, data); | ||
728 | /* | ||
729 | * The delivery of a fatal signal during event | ||
730 | * notification may silently skip tracer notification. | ||
731 | * Terminating the task now avoids executing a system | ||
732 | * call that may not be intended. | ||
733 | */ | ||
734 | if (fatal_signal_pending(current)) | ||
735 | do_exit(SIGSYS); | ||
736 | if (syscall_get_nr(current, regs) < 0) | ||
737 | return -1; /* Explicit request to skip. */ | ||
738 | |||
739 | return 0; | ||
682 | } | 740 | } |
683 | #endif /* CONFIG_HAVE_ARCH_SECCOMP_FILTER */ | 741 | #endif /* CONFIG_HAVE_ARCH_SECCOMP_FILTER */ |
684 | 742 | ||