aboutsummaryrefslogtreecommitdiffstats
path: root/security/commoncap.c
diff options
context:
space:
mode:
authorTetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>2014-07-22 08:20:01 -0400
committerJames Morris <james.l.morris@oracle.com>2014-07-24 07:12:30 -0400
commit6d6f3328422a3bc56b0d8dd026a5de845d2abfa7 (patch)
tree51c24037355a91d098568a88f7dd9715dee8008c /security/commoncap.c
parentfd33c43677a7965624b46352a686a7c1e72ae4aa (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.c72
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 */
825static long cap_prctl_drop(struct cred *new, unsigned long cap) 825static 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)
848int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3, 853int 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 */
942changed:
943 return commit_creds(new);
944
945no_change:
946error:
947 abort_creds(new);
948 return error;
949} 937}
950 938
951/** 939/**