Commit 6d6f3328 authored by Tetsuo Handa's avatar Tetsuo Handa Committed by James Morris

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: default avatarKevin Hao <haokexin@gmail.com>
Signed-off-by: default avatarTetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Reviewed-by: default avatarPaul Moore <paul@paul-moore.com>
Acked-by: default avatarSerge E. Hallyn <serge.hallyn@ubuntu.com>
Reviewed-by: default avatarKees Cook <keescook@chromium.org>
Signed-off-by: default avatarJames Morris <james.l.morris@oracle.com>
parent fd33c436
...@@ -822,15 +822,20 @@ int cap_task_setnice(struct task_struct *p, int nice) ...@@ -822,15 +822,20 @@ int cap_task_setnice(struct task_struct *p, int nice)
* Implement PR_CAPBSET_DROP. Attempt to remove the specified capability from * Implement PR_CAPBSET_DROP. Attempt to remove the specified capability from
* the current task's bounding set. Returns 0 on success, -ve on error. * the current task's bounding set. Returns 0 on success, -ve on error.
*/ */
static long cap_prctl_drop(struct cred *new, unsigned long cap) static int cap_prctl_drop(unsigned long cap)
{ {
struct cred *new;
if (!ns_capable(current_user_ns(), CAP_SETPCAP)) if (!ns_capable(current_user_ns(), CAP_SETPCAP))
return -EPERM; return -EPERM;
if (!cap_valid(cap)) if (!cap_valid(cap))
return -EINVAL; return -EINVAL;
new = prepare_creds();
if (!new)
return -ENOMEM;
cap_lower(new->cap_bset, cap); cap_lower(new->cap_bset, cap);
return 0; return commit_creds(new);
} }
/** /**
...@@ -848,26 +853,17 @@ static long cap_prctl_drop(struct cred *new, unsigned long cap) ...@@ -848,26 +853,17 @@ static long cap_prctl_drop(struct cred *new, unsigned long cap)
int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3, int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,
unsigned long arg4, unsigned long arg5) unsigned long arg4, unsigned long arg5)
{ {
const struct cred *old = current_cred();
struct cred *new; struct cred *new;
long error = 0;
new = prepare_creds();
if (!new)
return -ENOMEM;
switch (option) { switch (option) {
case PR_CAPBSET_READ: case PR_CAPBSET_READ:
error = -EINVAL;
if (!cap_valid(arg2)) if (!cap_valid(arg2))
goto error; return -EINVAL;
error = !!cap_raised(new->cap_bset, arg2); return !!cap_raised(old->cap_bset, arg2);
goto no_change;
case PR_CAPBSET_DROP: case PR_CAPBSET_DROP:
error = cap_prctl_drop(new, arg2); return cap_prctl_drop(arg2);
if (error < 0)
goto error;
goto changed;
/* /*
* The next four prctl's remain to assist with transitioning a * 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,10 +885,9 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,
* capability-based-privilege environment. * capability-based-privilege environment.
*/ */
case PR_SET_SECUREBITS: case PR_SET_SECUREBITS:
error = -EPERM; if ((((old->securebits & SECURE_ALL_LOCKS) >> 1)
if ((((new->securebits & SECURE_ALL_LOCKS) >> 1) & (old->securebits ^ arg2)) /*[1]*/
& (new->securebits ^ arg2)) /*[1]*/ || ((old->securebits & SECURE_ALL_LOCKS & ~arg2)) /*[2]*/
|| ((new->securebits & SECURE_ALL_LOCKS & ~arg2)) /*[2]*/
|| (arg2 & ~(SECURE_ALL_LOCKS | SECURE_ALL_BITS)) /*[3]*/ || (arg2 & ~(SECURE_ALL_LOCKS | SECURE_ALL_BITS)) /*[3]*/
|| (cap_capable(current_cred(), || (cap_capable(current_cred(),
current_cred()->user_ns, CAP_SETPCAP, current_cred()->user_ns, CAP_SETPCAP,
...@@ -906,46 +901,39 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3, ...@@ -906,46 +901,39 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,
*/ */
) )
/* cannot change a locked bit */ /* cannot change a locked bit */
goto error; return -EPERM;
new = prepare_creds();
if (!new)
return -ENOMEM;
new->securebits = arg2; new->securebits = arg2;
goto changed; return commit_creds(new);
case PR_GET_SECUREBITS: case PR_GET_SECUREBITS:
error = new->securebits; return old->securebits;
goto no_change;
case PR_GET_KEEPCAPS: case PR_GET_KEEPCAPS:
if (issecure(SECURE_KEEP_CAPS)) return !!issecure(SECURE_KEEP_CAPS);
error = 1;
goto no_change;
case PR_SET_KEEPCAPS: case PR_SET_KEEPCAPS:
error = -EINVAL;
if (arg2 > 1) /* Note, we rely on arg2 being unsigned here */ if (arg2 > 1) /* Note, we rely on arg2 being unsigned here */
goto error; return -EINVAL;
error = -EPERM;
if (issecure(SECURE_KEEP_CAPS_LOCKED)) if (issecure(SECURE_KEEP_CAPS_LOCKED))
goto error; return -EPERM;
new = prepare_creds();
if (!new)
return -ENOMEM;
if (arg2) if (arg2)
new->securebits |= issecure_mask(SECURE_KEEP_CAPS); new->securebits |= issecure_mask(SECURE_KEEP_CAPS);
else else
new->securebits &= ~issecure_mask(SECURE_KEEP_CAPS); new->securebits &= ~issecure_mask(SECURE_KEEP_CAPS);
goto changed; return commit_creds(new);
default: default:
/* No functionality available - continue with default */ /* No functionality available - continue with default */
error = -ENOSYS; return -ENOSYS;
goto error;
} }
/* Functionality provided */
changed:
return commit_creds(new);
no_change:
error:
abort_creds(new);
return error;
} }
/** /**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment