mirror of
https://github.com/torvalds/linux.git
synced 2026-10-22 23:01:49 +02:00
cred: clarify that task_struct::cred is only for the current task
The `cred` field in task_struct is currently marked as __rcu, but that's not true: It can point to credentials from access_override_creds(), which do not get freed with RCU delay. What actually protects task_struct::cred is that accessing it is only permitted for the current task (except for setting up a child during fork() or tearing down a dead process). (There is currently code in Smack that violates this rule, but that's a bug and causes UAF, I have sent a separate fix for that.) Clarify this, remove the __rcu marker, and remove RCU helpers from all accesses to this field. Signed-off-by: Jann Horn <jannh@google.com> Reviewed-by: Serge Hallyn <serge@hallyn.com> [PM: style tweak in security_init(), applied fixup from JH] Signed-off-by: Paul Moore <paul@paul-moore.com>
This commit is contained in:
parent
cee9395acd
commit
b030ffaedc
|
|
@ -180,12 +180,18 @@ static inline bool cap_ambient_invariant_ok(const struct cred *cred)
|
|||
|
||||
static inline const struct cred *override_creds(const struct cred *override_cred)
|
||||
{
|
||||
return rcu_replace_pointer(current->cred, override_cred, 1);
|
||||
const struct cred *old = current->cred;
|
||||
|
||||
current->cred = override_cred;
|
||||
return old;
|
||||
}
|
||||
|
||||
static inline const struct cred *revert_creds(const struct cred *revert_cred)
|
||||
{
|
||||
return rcu_replace_pointer(current->cred, revert_cred, 1);
|
||||
const struct cred *override_cred = current->cred;
|
||||
|
||||
current->cred = revert_cred;
|
||||
return override_cred;
|
||||
}
|
||||
|
||||
DEFINE_CLASS(override_creds,
|
||||
|
|
@ -293,11 +299,10 @@ DEFINE_FREE(put_cred, struct cred *, if (!IS_ERR_OR_NULL(_T)) put_cred(_T))
|
|||
/**
|
||||
* current_cred - Access the current task's subjective credentials
|
||||
*
|
||||
* Access the subjective credentials of the current task. RCU-safe,
|
||||
* since nobody else can modify it.
|
||||
* Access the subjective credentials of the current task.
|
||||
* Nobody else can modify it.
|
||||
*/
|
||||
#define current_cred() \
|
||||
rcu_dereference_protected(current->cred, 1)
|
||||
#define current_cred() (current->cred)
|
||||
|
||||
/**
|
||||
* current_real_cred - Access the current task's objective credentials
|
||||
|
|
|
|||
|
|
@ -1172,8 +1172,12 @@ struct task_struct {
|
|||
/* Objective and real subjective task credentials (COW): */
|
||||
const struct cred __rcu *real_cred;
|
||||
|
||||
/* Effective (overridable) subjective task credentials (COW): */
|
||||
const struct cred __rcu *cred;
|
||||
/*
|
||||
* Effective (overridable) subjective task credentials (COW).
|
||||
* Only accessible for the current task and during task creation/freeing.
|
||||
* This pointer is not managed by RCU!
|
||||
*/
|
||||
const struct cred *cred;
|
||||
|
||||
#ifdef CONFIG_KEYS
|
||||
/* Cached requested key. */
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ struct task_struct init_task __aligned(L1_CACHE_BYTES) = {
|
|||
.sibling = LIST_HEAD_INIT(init_task.sibling),
|
||||
.group_leader = &init_task,
|
||||
RCU_POINTER_INITIALIZER(real_cred, &init_cred),
|
||||
RCU_POINTER_INITIALIZER(cred, &init_cred),
|
||||
.cred = &init_cred,
|
||||
.comm = INIT_TASK_COMM,
|
||||
.thread = INIT_THREAD,
|
||||
.real_fs = &init_fs,
|
||||
|
|
|
|||
|
|
@ -459,7 +459,7 @@ static int audit_field_compare(struct task_struct *tsk,
|
|||
*
|
||||
* If task_creation is true, this is an explicit indication that we are
|
||||
* filtering a task rule at task creation time. This and tsk == current are
|
||||
* the only situations where tsk->cred may be accessed without an rcu read lock.
|
||||
* the only situations where tsk->cred may be accessed.
|
||||
*/
|
||||
static int audit_filter_rules(struct task_struct *tsk,
|
||||
struct audit_krule *rule,
|
||||
|
|
@ -476,7 +476,8 @@ static int audit_filter_rules(struct task_struct *tsk,
|
|||
if (ctx && rule->prio <= ctx->prio)
|
||||
return 0;
|
||||
|
||||
cred = rcu_dereference_check(tsk->cred, tsk == current || task_creation);
|
||||
WARN_ON(tsk != current && !task_creation);
|
||||
cred = tsk->cred;
|
||||
|
||||
for (i = 0; i < rule->field_count; i++) {
|
||||
struct audit_field *f = &rule->fields[i];
|
||||
|
|
|
|||
|
|
@ -414,7 +414,7 @@ int commit_creds(struct cred *new)
|
|||
inc_rlimit_ucounts(new->ucounts, UCOUNT_RLIMIT_NPROC, 1);
|
||||
|
||||
rcu_assign_pointer(task->real_cred, new);
|
||||
rcu_assign_pointer(task->cred, new);
|
||||
task->cred = new;
|
||||
if (new->user != old->user || new->user_ns != old->user_ns)
|
||||
dec_rlimit_ucounts(old->ucounts, UCOUNT_RLIMIT_NPROC, 1);
|
||||
if (new->user_ns != old->user_ns)
|
||||
|
|
|
|||
|
|
@ -476,8 +476,7 @@ int __init security_init(void)
|
|||
blob_sizes.lbs_inode, 0,
|
||||
SLAB_PANIC, NULL);
|
||||
|
||||
if (lsm_cred_alloc((struct cred *)unrcu_pointer(current->cred),
|
||||
GFP_KERNEL))
|
||||
if (lsm_cred_alloc((struct cred *)current->cred, GFP_KERNEL))
|
||||
panic("early LSM cred alloc failed\n");
|
||||
if (lsm_task_alloc(current))
|
||||
panic("early LSM task alloc failed\n");
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user