]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - kernel/cred.c
bpf: Fix passing modified ctx to ld/abs/ind instruction
[mirror_ubuntu-bionic-kernel.git] / kernel / cred.c
index ecf03657e71c9c2eb959cc2ee46cee0414f5f0ca..eed2bc6aa7d85a386e27fe1c14f724b2542faac8 100644 (file)
@@ -147,7 +147,10 @@ void __put_cred(struct cred *cred)
        BUG_ON(cred == current->cred);
        BUG_ON(cred == current->real_cred);
 
-       call_rcu(&cred->rcu, put_cred_rcu);
+       if (cred->non_rcu)
+               put_cred_rcu(&cred->rcu);
+       else
+               call_rcu(&cred->rcu, put_cred_rcu);
 }
 EXPORT_SYMBOL(__put_cred);
 
@@ -217,7 +220,7 @@ struct cred *cred_alloc_blank(void)
        new->magic = CRED_MAGIC;
 #endif
 
-       if (security_cred_alloc_blank(new, GFP_KERNEL) < 0)
+       if (security_cred_alloc_blank(new, GFP_KERNEL_ACCOUNT) < 0)
                goto error;
 
        return new;
@@ -258,6 +261,7 @@ struct cred *prepare_creds(void)
        old = task->cred;
        memcpy(new, old, sizeof(struct cred));
 
+       new->non_rcu = 0;
        atomic_set(&new->usage, 1);
        set_cred_subscribers(new, 0);
        get_group_info(new->group_info);
@@ -275,7 +279,7 @@ struct cred *prepare_creds(void)
        new->security = NULL;
 #endif
 
-       if (security_prepare_creds(new, old, GFP_KERNEL) < 0)
+       if (security_prepare_creds(new, old, GFP_KERNEL_ACCOUNT) < 0)
                goto error;
        validate_creds(new);
        return new;
@@ -448,6 +452,15 @@ int commit_creds(struct cred *new)
                if (task->mm)
                        set_dumpable(task->mm, suid_dumpable);
                task->pdeath_signal = 0;
+               /*
+                * If a task drops privileges and becomes nondumpable,
+                * the dumpability change must become visible before
+                * the credential change; otherwise, a __ptrace_may_access()
+                * racing with this change may be able to attach to a task it
+                * shouldn't be able to attach to (as if the task had dropped
+                * privileges without becoming nondumpable).
+                * Pairs with a read barrier in __ptrace_may_access().
+                */
                smp_wmb();
        }
 
@@ -528,7 +541,19 @@ const struct cred *override_creds(const struct cred *new)
 
        validate_creds(old);
        validate_creds(new);
-       get_cred(new);
+
+       /*
+        * NOTE! This uses 'get_new_cred()' rather than 'get_cred()'.
+        *
+        * That means that we do not clear the 'non_rcu' flag, since
+        * we are only installing the cred into the thread-synchronous
+        * '->cred' pointer, not the '->real_cred' pointer that is
+        * visible to other threads under RCU.
+        *
+        * Also note that we did validate_creds() manually, not depending
+        * on the validation in 'get_cred()'.
+        */
+       get_new_cred((struct cred *)new);
        alter_cred_subscribers(new, 1);
        rcu_assign_pointer(current->cred, new);
        alter_cred_subscribers(old, -1);
@@ -575,42 +600,35 @@ void __init cred_init(void)
 }
 
 /**
- * prepare_kernel_cred - Prepare a set of credentials for a kernel service
- * @daemon: A userspace daemon to be used as a reference
+ * clone_cred - Create a new copy of a set of credentials
+ * @old: Credentials to be copied
  *
- * Prepare a set of credentials for a kernel service.  This can then be used to
- * override a task's own credentials so that work can be done on behalf of that
- * task that requires a different subjective context.
+ * Prepare a new set of credentials that is an exact copy of @old. This can
+ * optionally be modified and used to override a task's own credentials so
+ * that work can be done on behalf of that task that requires a different
+ * subjective context.
  *
- * @daemon is used to provide a base for the security record, but can be NULL.
- * If @daemon is supplied, then the security data will be derived from that;
- * otherwise they'll be set to 0 and no groups, full capabilities and no keys.
- *
- * The caller may change these controls afterwards if desired.
- *
- * Returns the new credentials or NULL if out of memory.
+ * Returns the new credentials or NULL if @old is NULL or if out of memory.
  *
  * Does not take, and does not return holding current->cred_replace_mutex.
  */
-struct cred *prepare_kernel_cred(struct task_struct *daemon)
+struct cred *clone_cred(const struct cred *old)
 {
-       const struct cred *old;
        struct cred *new;
 
+       if (!old)
+               return NULL;
+
        new = kmem_cache_alloc(cred_jar, GFP_KERNEL);
        if (!new)
                return NULL;
 
-       kdebug("prepare_kernel_cred() alloc %p", new);
-
-       if (daemon)
-               old = get_task_cred(daemon);
-       else
-               old = get_cred(&init_cred);
+       kdebug("clone_cred() alloc %p", new);
 
        validate_creds(old);
 
        *new = *old;
+       new->non_rcu = 0;
        atomic_set(&new->usage, 1);
        set_cred_subscribers(new, 0);
        get_uid(new->user);
@@ -628,18 +646,50 @@ struct cred *prepare_kernel_cred(struct task_struct *daemon)
 #ifdef CONFIG_SECURITY
        new->security = NULL;
 #endif
-       if (security_prepare_creds(new, old, GFP_KERNEL) < 0)
+       if (security_prepare_creds(new, old, GFP_KERNEL_ACCOUNT) < 0)
                goto error;
 
-       put_cred(old);
        validate_creds(new);
        return new;
 
 error:
        put_cred(new);
-       put_cred(old);
        return NULL;
 }
+EXPORT_SYMBOL(clone_cred);
+
+/**
+ * prepare_kernel_cred - Prepare a set of credentials for a kernel service
+ * @daemon: A userspace daemon to be used as a reference
+ *
+ * Prepare a set of credentials for a kernel service.  This can then be used to
+ * override a task's own credentials so that work can be done on behalf of that
+ * task that requires a different subjective context.
+ *
+ * @daemon is used to provide a base for the security record, but can be NULL.
+ * If @daemon is supplied, then the security data will be derived from that;
+ * otherwise they'll be set to 0 and no groups, full capabilities and no keys.
+ *
+ * The caller may change these controls afterwards if desired.
+ *
+ * Returns the new credentials or NULL if out of memory.
+ *
+ * Does not take, and does not return holding current->cred_replace_mutex.
+ */
+struct cred *prepare_kernel_cred(struct task_struct *daemon)
+{
+       const struct cred *old;
+       struct cred *new;
+
+       if (daemon)
+               old = get_task_cred(daemon);
+       else
+               old = get_cred(&init_cred);
+
+       new = clone_cred(old);
+       put_cred(old);
+       return new;
+}
 EXPORT_SYMBOL(prepare_kernel_cred);
 
 /**
@@ -704,19 +754,6 @@ bool creds_are_invalid(const struct cred *cred)
 {
        if (cred->magic != CRED_MAGIC)
                return true;
-#ifdef CONFIG_SECURITY_SELINUX
-       /*
-        * cred->security == NULL if security_cred_alloc_blank() or
-        * security_prepare_creds() returned an error.
-        */
-       if (selinux_is_enabled() && cred->security) {
-               if ((unsigned long) cred->security < PAGE_SIZE)
-                       return true;
-               if ((*(u32 *)cred->security & 0xffffff00) ==
-                   (POISON_FREE << 24 | POISON_FREE << 16 | POISON_FREE << 8))
-                       return true;
-       }
-#endif
        return false;
 }
 EXPORT_SYMBOL(creds_are_invalid);