userns: Kill task_user_ns
authorEric W. Biederman <ebiederm@xmission.com>
Thu, 26 Jul 2012 12:05:21 +0000 (05:05 -0700)
committerEric W. Biederman <ebiederm@xmission.com>
Tue, 20 Nov 2012 12:17:44 +0000 (04:17 -0800)
The task_user_ns function hides the fact that it is getting the user
namespace from struct cred on the task.  struct cred may go away as
soon as the rcu lock is released.  This leads to a race where we
can dereference a stale user namespace pointer.

To make it obvious a struct cred is involved kill task_user_ns.

To kill the race modify the users of task_user_ns to only
reference the user namespace while the rcu lock is held.

Cc: Kees Cook <keescook@chromium.org>
Cc: James Morris <james.l.morris@oracle.com>
Acked-by: Kees Cook <keescook@chromium.org>
Acked-by: Serge Hallyn <serge.hallyn@canonical.com>
Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
include/linux/cred.h
kernel/ptrace.c
kernel/sched/core.c
security/yama/yama_lsm.c

index ebbed2ce66379bd986fbf83f11e7ae8c32bf6070..856d2622d832eeab89abde1de6ce5d07d58b69d3 100644 (file)
@@ -357,10 +357,8 @@ static inline void put_cred(const struct cred *_cred)
 extern struct user_namespace init_user_ns;
 #ifdef CONFIG_USER_NS
 #define current_user_ns()      (current_cred_xxx(user_ns))
-#define task_user_ns(task)     (task_cred_xxx((task), user_ns))
 #else
 #define current_user_ns()      (&init_user_ns)
-#define task_user_ns(task)     (&init_user_ns)
 #endif
 
 
index 1f5e55dda955544ca4a3f1f944e967f6b561bea2..7b09b88862cc8fdb78a893e3fbbd93418a5858aa 100644 (file)
@@ -215,8 +215,12 @@ ok:
        smp_rmb();
        if (task->mm)
                dumpable = get_dumpable(task->mm);
-       if (!dumpable  && !ptrace_has_cap(task_user_ns(task), mode))
+       rcu_read_lock();
+       if (!dumpable && !ptrace_has_cap(__task_cred(task)->user_ns, mode)) {
+               rcu_read_unlock();
                return -EPERM;
+       }
+       rcu_read_unlock();
 
        return security_ptrace_access_check(task, mode);
 }
@@ -280,8 +284,10 @@ static int ptrace_attach(struct task_struct *task, long request,
 
        if (seize)
                flags |= PT_SEIZED;
-       if (ns_capable(task_user_ns(task), CAP_SYS_PTRACE))
+       rcu_read_lock();
+       if (ns_capable(__task_cred(task)->user_ns, CAP_SYS_PTRACE))
                flags |= PT_PTRACE_CAP;
+       rcu_read_unlock();
        task->ptrace = flags;
 
        __ptrace_link(task, current);
index 2d8927fda712f5ee1e19f1fe364fbad557d8a736..2f5eb1838b3eb5c2b1e569e8a0b2ac762203bc93 100644 (file)
@@ -4029,8 +4029,14 @@ long sched_setaffinity(pid_t pid, const struct cpumask *in_mask)
                goto out_free_cpus_allowed;
        }
        retval = -EPERM;
-       if (!check_same_owner(p) && !ns_capable(task_user_ns(p), CAP_SYS_NICE))
-               goto out_unlock;
+       if (!check_same_owner(p)) {
+               rcu_read_lock();
+               if (!ns_capable(__task_cred(p)->user_ns, CAP_SYS_NICE)) {
+                       rcu_read_unlock();
+                       goto out_unlock;
+               }
+               rcu_read_unlock();
+       }
 
        retval = security_task_setscheduler(p);
        if (retval)
index b4c29848b49d2ab2741ade9f1aece147ac02e6a3..0e72239aeb053ddf5b7c589aeeff9563a673321f 100644 (file)
@@ -262,14 +262,18 @@ int yama_ptrace_access_check(struct task_struct *child,
                        /* No additional restrictions. */
                        break;
                case YAMA_SCOPE_RELATIONAL:
+                       rcu_read_lock();
                        if (!task_is_descendant(current, child) &&
                            !ptracer_exception_found(current, child) &&
-                           !ns_capable(task_user_ns(child), CAP_SYS_PTRACE))
+                           !ns_capable(__task_cred(child)->user_ns, CAP_SYS_PTRACE))
                                rc = -EPERM;
+                       rcu_read_unlock();
                        break;
                case YAMA_SCOPE_CAPABILITY:
-                       if (!ns_capable(task_user_ns(child), CAP_SYS_PTRACE))
+                       rcu_read_lock();
+                       if (!ns_capable(__task_cred(child)->user_ns, CAP_SYS_PTRACE))
                                rc = -EPERM;
+                       rcu_read_unlock();
                        break;
                case YAMA_SCOPE_NO_ATTACH:
                default:
@@ -307,8 +311,10 @@ int yama_ptrace_traceme(struct task_struct *parent)
        /* Only disallow PTRACE_TRACEME on more aggressive settings. */
        switch (ptrace_scope) {
        case YAMA_SCOPE_CAPABILITY:
-               if (!ns_capable(task_user_ns(parent), CAP_SYS_PTRACE))
+               rcu_read_lock();
+               if (!ns_capable(__task_cred(parent)->user_ns, CAP_SYS_PTRACE))
                        rc = -EPERM;
+               rcu_read_unlock();
                break;
        case YAMA_SCOPE_NO_ATTACH:
                rc = -EPERM;