diff options
author | Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> | 2014-07-22 08:20:01 -0400 |
---|---|---|
committer | James Morris <james.l.morris@oracle.com> | 2014-07-24 07:12:30 -0400 |
commit | 6d6f3328422a3bc56b0d8dd026a5de845d2abfa7 (patch) | |
tree | 51c24037355a91d098568a88f7dd9715dee8008c /security/commoncap.c | |
parent | fd33c43677a7965624b46352a686a7c1e72ae4aa (diff) |
commoncap: don't alloc the credential unless needed in cap_task_prctl
In function cap_task_prctl(), we would allocate a credential
unconditionally and then check if we support the requested function.
If not we would release this credential with abort_creds() by using
RCU method. But on some archs such as powerpc, the sys_prctl is heavily
used to get/set the floating point exception mode. So the unnecessary
allocating/releasing of credential not only introduce runtime overhead
but also do cause OOM due to the RCU implementation.
This patch removes abort_creds() from cap_task_prctl() by calling
prepare_creds() only when we need to modify it.
Reported-by: Kevin Hao <haokexin@gmail.com>
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Reviewed-by: Paul Moore <paul@paul-moore.com>
Acked-by: Serge E. Hallyn <serge.hallyn@ubuntu.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
Signed-off-by: James Morris <james.l.morris@oracle.com>
Diffstat (limited to 'security/commoncap.c')
-rw-r--r-- | security/commoncap.c | 72 |
1 files changed, 30 insertions, 42 deletions
diff --git a/security/commoncap.c b/security/commoncap.c index b9d613e0ef14..9fe46e22c7f2 100644 --- a/security/commoncap.c +++ b/security/commoncap.c | |||
@@ -822,15 +822,20 @@ int cap_task_setnice(struct task_struct *p, int nice) | |||
822 | * Implement PR_CAPBSET_DROP. Attempt to remove the specified capability from | 822 | * Implement PR_CAPBSET_DROP. Attempt to remove the specified capability from |
823 | * the current task's bounding set. Returns 0 on success, -ve on error. | 823 | * the current task's bounding set. Returns 0 on success, -ve on error. |
824 | */ | 824 | */ |
825 | static long cap_prctl_drop(struct cred *new, unsigned long cap) | 825 | static int cap_prctl_drop(unsigned long cap) |
826 | { | 826 | { |
827 | struct cred *new; | ||
828 | |||
827 | if (!ns_capable(current_user_ns(), CAP_SETPCAP)) | 829 | if (!ns_capable(current_user_ns(), CAP_SETPCAP)) |
828 | return -EPERM; | 830 | return -EPERM; |
829 | if (!cap_valid(cap)) | 831 | if (!cap_valid(cap)) |
830 | return -EINVAL; | 832 | return -EINVAL; |
831 | 833 | ||
834 | new = prepare_creds(); | ||
835 | if (!new) | ||
836 | return -ENOMEM; | ||
832 | cap_lower(new->cap_bset, cap); | 837 | cap_lower(new->cap_bset, cap); |
833 | return 0; | 838 | return commit_creds(new); |
834 | } | 839 | } |
835 | 840 | ||
836 | /** | 841 | /** |
@@ -848,26 +853,17 @@ static long cap_prctl_drop(struct cred *new, unsigned long cap) | |||
848 | int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3, | 853 | int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3, |
849 | unsigned long arg4, unsigned long arg5) | 854 | unsigned long arg4, unsigned long arg5) |
850 | { | 855 | { |
856 | const struct cred *old = current_cred(); | ||
851 | struct cred *new; | 857 | struct cred *new; |
852 | long error = 0; | ||
853 | |||
854 | new = prepare_creds(); | ||
855 | if (!new) | ||
856 | return -ENOMEM; | ||
857 | 858 | ||
858 | switch (option) { | 859 | switch (option) { |
859 | case PR_CAPBSET_READ: | 860 | case PR_CAPBSET_READ: |
860 | error = -EINVAL; | ||
861 | if (!cap_valid(arg2)) | 861 | if (!cap_valid(arg2)) |
862 | goto error; | 862 | return -EINVAL; |
863 | error = !!cap_raised(new->cap_bset, arg2); | 863 | return !!cap_raised(old->cap_bset, arg2); |
864 | goto no_change; | ||
865 | 864 | ||
866 | case PR_CAPBSET_DROP: | 865 | case PR_CAPBSET_DROP: |
867 | error = cap_prctl_drop(new, arg2); | 866 | return cap_prctl_drop(arg2); |
868 | if (error < 0) | ||
869 | goto error; | ||
870 | goto changed; | ||
871 | 867 | ||
872 | /* | 868 | /* |
873 | * The next four prctl's remain to assist with transitioning a | 869 | * The next four prctl's remain to assist with transitioning a |
@@ -889,10 +885,9 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3, | |||
889 | * capability-based-privilege environment. | 885 | * capability-based-privilege environment. |
890 | */ | 886 | */ |
891 | case PR_SET_SECUREBITS: | 887 | case PR_SET_SECUREBITS: |
892 | error = -EPERM; | 888 | if ((((old->securebits & SECURE_ALL_LOCKS) >> 1) |
893 | if ((((new->securebits & SECURE_ALL_LOCKS) >> 1) | 889 | & (old->securebits ^ arg2)) /*[1]*/ |
894 | & (new->securebits ^ arg2)) /*[1]*/ | 890 | || ((old->securebits & SECURE_ALL_LOCKS & ~arg2)) /*[2]*/ |
895 | || ((new->securebits & SECURE_ALL_LOCKS & ~arg2)) /*[2]*/ | ||
896 | || (arg2 & ~(SECURE_ALL_LOCKS | SECURE_ALL_BITS)) /*[3]*/ | 891 | || (arg2 & ~(SECURE_ALL_LOCKS | SECURE_ALL_BITS)) /*[3]*/ |
897 | || (cap_capable(current_cred(), | 892 | || (cap_capable(current_cred(), |
898 | current_cred()->user_ns, CAP_SETPCAP, | 893 | current_cred()->user_ns, CAP_SETPCAP, |
@@ -906,46 +901,39 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3, | |||
906 | */ | 901 | */ |
907 | ) | 902 | ) |
908 | /* cannot change a locked bit */ | 903 | /* cannot change a locked bit */ |
909 | goto error; | 904 | return -EPERM; |
905 | |||
906 | new = prepare_creds(); | ||
907 | if (!new) | ||
908 | return -ENOMEM; | ||
910 | new->securebits = arg2; | 909 | new->securebits = arg2; |
911 | goto changed; | 910 | return commit_creds(new); |
912 | 911 | ||
913 | case PR_GET_SECUREBITS: | 912 | case PR_GET_SECUREBITS: |
914 | error = new->securebits; | 913 | return old->securebits; |
915 | goto no_change; | ||
916 | 914 | ||
917 | case PR_GET_KEEPCAPS: | 915 | case PR_GET_KEEPCAPS: |
918 | if (issecure(SECURE_KEEP_CAPS)) | 916 | return !!issecure(SECURE_KEEP_CAPS); |
919 | error = 1; | ||
920 | goto no_change; | ||
921 | 917 | ||
922 | case PR_SET_KEEPCAPS: | 918 | case PR_SET_KEEPCAPS: |
923 | error = -EINVAL; | ||
924 | if (arg2 > 1) /* Note, we rely on arg2 being unsigned here */ | 919 | if (arg2 > 1) /* Note, we rely on arg2 being unsigned here */ |
925 | goto error; | 920 | return -EINVAL; |
926 | error = -EPERM; | ||
927 | if (issecure(SECURE_KEEP_CAPS_LOCKED)) | 921 | if (issecure(SECURE_KEEP_CAPS_LOCKED)) |
928 | goto error; | 922 | return -EPERM; |
923 | |||
924 | new = prepare_creds(); | ||
925 | if (!new) | ||
926 | return -ENOMEM; | ||
929 | if (arg2) | 927 | if (arg2) |
930 | new->securebits |= issecure_mask(SECURE_KEEP_CAPS); | 928 | new->securebits |= issecure_mask(SECURE_KEEP_CAPS); |
931 | else | 929 | else |
932 | new->securebits &= ~issecure_mask(SECURE_KEEP_CAPS); | 930 | new->securebits &= ~issecure_mask(SECURE_KEEP_CAPS); |
933 | goto changed; | 931 | return commit_creds(new); |
934 | 932 | ||
935 | default: | 933 | default: |
936 | /* No functionality available - continue with default */ | 934 | /* No functionality available - continue with default */ |
937 | error = -ENOSYS; | 935 | return -ENOSYS; |
938 | goto error; | ||
939 | } | 936 | } |
940 | |||
941 | /* Functionality provided */ | ||
942 | changed: | ||
943 | return commit_creds(new); | ||
944 | |||
945 | no_change: | ||
946 | error: | ||
947 | abort_creds(new); | ||
948 | return error; | ||
949 | } | 937 | } |
950 | 938 | ||
951 | /** | 939 | /** |