Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm...
authorLinus Torvalds <torvalds@linux-foundation.org>
Sat, 7 Sep 2013 21:35:32 +0000 (14:35 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 7 Sep 2013 21:35:32 +0000 (14:35 -0700)
Pull namespace changes from Eric Biederman:
 "This is an assorted mishmash of small cleanups, enhancements and bug
  fixes.

  The major theme is user namespace mount restrictions.  nsown_capable
  is killed as it encourages not thinking about details that need to be
  considered.  A very hard to hit pid namespace exiting bug was finally
  tracked and fixed.  A couple of cleanups to the basic namespace
  infrastructure.

  Finally there is an enhancement that makes per user namespace
  capabilities usable as capabilities, and an enhancement that allows
  the per userns root to nice other processes in the user namespace"

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace:
  userns:  Kill nsown_capable it makes the wrong thing easy
  capabilities: allow nice if we are privileged
  pidns: Don't have unshare(CLONE_NEWPID) imply CLONE_THREAD
  userns: Allow PR_CAPBSET_DROP in a user namespace.
  namespaces: Simplify copy_namespaces so it is clear what is going on.
  pidns: Fix hang in zap_pid_ns_processes by sending a potentially extra wakeup
  sysfs: Restrict mounting sysfs
  userns: Better restrictions on when proc and sysfs can be mounted
  vfs: Don't copy mount bind mounts of /proc/<pid>/ns/mnt between namespaces
  kernel/nsproxy.c: Improving a snippet of code.
  proc: Restrict mounting the proc filesystem
  vfs: Lock in place mounts from more privileged users

13 files changed:
1  2 
fs/namespace.c
fs/open.c
fs/proc/root.c
fs/sysfs/mount.c
include/linux/fs.h
include/linux/user_namespace.h
kernel/fork.c
kernel/nsproxy.c
kernel/pid_namespace.c
kernel/user_namespace.c
lib/kobject.c
net/core/net-sysfs.c
net/core/scm.c

diff --combined fs/namespace.c
index ad8ea9bc2518e7fcf6fc3914d095eb6338a99693,dc519a1437ee949da2e549886c838fc4698dad5c..ef69fa5d2e5bd4806eda5d82f3c30e47ebf19c35
@@@ -831,6 -831,10 +831,10 @@@ static struct mount *clone_mnt(struct m
        if ((flag & CL_UNPRIVILEGED) && (mnt->mnt.mnt_flags & MNT_READONLY))
                mnt->mnt.mnt_flags |= MNT_LOCK_READONLY;
  
+       /* Don't allow unprivileged users to reveal what is under a mount */
+       if ((flag & CL_UNPRIVILEGED) && list_empty(&old->mnt_expire))
+               mnt->mnt.mnt_flags |= MNT_LOCKED;
        atomic_inc(&sb->s_active);
        mnt->mnt.mnt_sb = sb;
        mnt->mnt.mnt_root = dget(root);
@@@ -1318,7 -1322,7 +1322,7 @@@ SYSCALL_DEFINE2(umount, char __user *, 
        if (!(flags & UMOUNT_NOFOLLOW))
                lookup_flags |= LOOKUP_FOLLOW;
  
 -      retval = user_path_at(AT_FDCWD, name, lookup_flags, &path);
 +      retval = user_path_umountat(AT_FDCWD, name, lookup_flags, &path);
        if (retval)
                goto out;
        mnt = real_mount(path.mnt);
                goto dput_and_out;
        if (!check_mnt(mnt))
                goto dput_and_out;
+       if (mnt->mnt.mnt_flags & MNT_LOCKED)
+               goto dput_and_out;
  
        retval = do_umount(mnt, flags);
  dput_and_out:
@@@ -1349,14 -1355,11 +1355,11 @@@ SYSCALL_DEFINE1(oldumount, char __user 
  
  #endif
  
- static bool mnt_ns_loop(struct path *path)
+ static bool is_mnt_ns_file(struct dentry *dentry)
  {
-       /* Could bind mounting the mount namespace inode cause a
-        * mount namespace loop?
-        */
-       struct inode *inode = path->dentry->d_inode;
+       /* Is this a proxy for a mount namespace? */
+       struct inode *inode = dentry->d_inode;
        struct proc_ns *ei;
-       struct mnt_namespace *mnt_ns;
  
        if (!proc_ns_inode(inode))
                return false;
        if (ei->ns_ops != &mntns_operations)
                return false;
  
-       mnt_ns = ei->ns;
+       return true;
+ }
+ static bool mnt_ns_loop(struct dentry *dentry)
+ {
+       /* Could bind mounting the mount namespace inode cause a
+        * mount namespace loop?
+        */
+       struct mnt_namespace *mnt_ns;
+       if (!is_mnt_ns_file(dentry))
+               return false;
+       mnt_ns = get_proc_ns(dentry->d_inode)->ns;
        return current->nsproxy->mnt_ns->seq >= mnt_ns->seq;
  }
  
@@@ -1374,13 -1389,17 +1389,17 @@@ struct mount *copy_tree(struct mount *m
  {
        struct mount *res, *p, *q, *r, *parent;
  
-       if (!(flag & CL_COPY_ALL) && IS_MNT_UNBINDABLE(mnt))
+       if (!(flag & CL_COPY_UNBINDABLE) && IS_MNT_UNBINDABLE(mnt))
+               return ERR_PTR(-EINVAL);
+       if (!(flag & CL_COPY_MNT_NS_FILE) && is_mnt_ns_file(dentry))
                return ERR_PTR(-EINVAL);
  
        res = q = clone_mnt(mnt, dentry, flag);
        if (IS_ERR(q))
                return q;
  
+       q->mnt.mnt_flags &= ~MNT_LOCKED;
        q->mnt_mountpoint = mnt->mnt_mountpoint;
  
        p = mnt;
                        continue;
  
                for (s = r; s; s = next_mnt(s, r)) {
-                       if (!(flag & CL_COPY_ALL) && IS_MNT_UNBINDABLE(s)) {
+                       if (!(flag & CL_COPY_UNBINDABLE) &&
+                           IS_MNT_UNBINDABLE(s)) {
+                               s = skip_mnt_tree(s);
+                               continue;
+                       }
+                       if (!(flag & CL_COPY_MNT_NS_FILE) &&
+                           is_mnt_ns_file(s->mnt.mnt_root)) {
                                s = skip_mnt_tree(s);
                                continue;
                        }
@@@ -1429,7 -1454,7 +1454,7 @@@ struct vfsmount *collect_mounts(struct 
                         CL_COPY_ALL | CL_PRIVATE);
        namespace_unlock();
        if (IS_ERR(tree))
 -              return NULL;
 +              return ERR_CAST(tree);
        return &tree->mnt;
  }
  
@@@ -1696,6 -1721,19 +1721,19 @@@ static int do_change_type(struct path *
        return err;
  }
  
+ static bool has_locked_children(struct mount *mnt, struct dentry *dentry)
+ {
+       struct mount *child;
+       list_for_each_entry(child, &mnt->mnt_mounts, mnt_child) {
+               if (!is_subdir(child->mnt_mountpoint, dentry))
+                       continue;
+               if (child->mnt.mnt_flags & MNT_LOCKED)
+                       return true;
+       }
+       return false;
+ }
  /*
   * do loopback mount.
   */
@@@ -1713,7 -1751,7 +1751,7 @@@ static int do_loopback(struct path *pat
                return err;
  
        err = -EINVAL;
-       if (mnt_ns_loop(&old_path))
+       if (mnt_ns_loop(old_path.dentry))
                goto out; 
  
        mp = lock_mount(path);
        if (!check_mnt(parent) || !check_mnt(old))
                goto out2;
  
+       if (!recurse && has_locked_children(old, old_path.dentry))
+               goto out2;
        if (recurse)
-               mnt = copy_tree(old, old_path.dentry, 0);
+               mnt = copy_tree(old, old_path.dentry, CL_COPY_MNT_NS_FILE);
        else
                mnt = clone_mnt(old, old_path.dentry, 0);
  
                goto out2;
        }
  
+       mnt->mnt.mnt_flags &= ~MNT_LOCKED;
        err = graft_tree(mnt, parent, mp);
        if (err) {
                br_write_lock(&vfsmount_lock);
@@@ -1853,6 -1896,9 +1896,9 @@@ static int do_move_mount(struct path *p
        if (!check_mnt(p) || !check_mnt(old))
                goto out1;
  
+       if (old->mnt.mnt_flags & MNT_LOCKED)
+               goto out1;
        err = -EINVAL;
        if (old_path.dentry != old_path.mnt->mnt_root)
                goto out1;
@@@ -2389,7 -2435,7 +2435,7 @@@ static struct mnt_namespace *dup_mnt_ns
  
        namespace_lock();
        /* First pass: copy the tree topology */
-       copy_flags = CL_COPY_ALL | CL_EXPIRE;
+       copy_flags = CL_COPY_UNBINDABLE | CL_EXPIRE;
        if (user_ns != mnt_ns->user_ns)
                copy_flags |= CL_SHARED_TO_SLAVE | CL_UNPRIVILEGED;
        new = copy_tree(old, old->mnt.mnt_root, copy_flags);
                }
                p = next_mnt(p, old);
                q = next_mnt(q, new);
+               if (!q)
+                       break;
+               while (p->mnt.mnt_root != q->mnt.mnt_root)
+                       p = next_mnt(p, old);
        }
        namespace_unlock();
  
@@@ -2630,6 -2680,8 +2680,8 @@@ SYSCALL_DEFINE2(pivot_root, const char 
                goto out4;
        if (!check_mnt(root_mnt) || !check_mnt(new_mnt))
                goto out4;
+       if (new_mnt->mnt.mnt_flags & MNT_LOCKED)
+               goto out4;
        error = -ENOENT;
        if (d_unlinked(new.dentry))
                goto out4;
        br_write_lock(&vfsmount_lock);
        detach_mnt(new_mnt, &parent_path);
        detach_mnt(root_mnt, &root_parent);
+       if (root_mnt->mnt.mnt_flags & MNT_LOCKED) {
+               new_mnt->mnt.mnt_flags |= MNT_LOCKED;
+               root_mnt->mnt.mnt_flags &= ~MNT_LOCKED;
+       }
        /* mount old root on put_old */
        attach_mnt(root_mnt, old_mnt, old_mp);
        /* mount new_root on / */
@@@ -2811,25 -2867,38 +2867,38 @@@ bool current_chrooted(void
        return chrooted;
  }
  
void update_mnt_policy(struct user_namespace *userns)
bool fs_fully_visible(struct file_system_type *type)
  {
        struct mnt_namespace *ns = current->nsproxy->mnt_ns;
        struct mount *mnt;
+       bool visible = false;
  
-       down_read(&namespace_sem);
+       if (unlikely(!ns))
+               return false;
+       namespace_lock();
        list_for_each_entry(mnt, &ns->list, mnt_list) {
-               switch (mnt->mnt.mnt_sb->s_magic) {
-               case SYSFS_MAGIC:
-                       userns->may_mount_sysfs = true;
-                       break;
-               case PROC_SUPER_MAGIC:
-                       userns->may_mount_proc = true;
-                       break;
+               struct mount *child;
+               if (mnt->mnt.mnt_sb->s_type != type)
+                       continue;
+               /* This mount is not fully visible if there are any child mounts
+                * that cover anything except for empty directories.
+                */
+               list_for_each_entry(child, &mnt->mnt_mounts, mnt_child) {
+                       struct inode *inode = child->mnt_mountpoint->d_inode;
+                       if (!S_ISDIR(inode->i_mode))
+                               goto next;
+                       if (inode->i_nlink != 2)
+                               goto next;
                }
-               if (userns->may_mount_sysfs && userns->may_mount_proc)
-                       break;
+               visible = true;
+               goto found;
+       next:   ;
        }
-       up_read(&namespace_sem);
+ found:
+       namespace_unlock();
+       return visible;
  }
  
  static void *mntns_get(struct task_struct *task)
@@@ -2860,8 -2929,8 +2929,8 @@@ static int mntns_install(struct nsprox
        struct path root;
  
        if (!ns_capable(mnt_ns->user_ns, CAP_SYS_ADMIN) ||
-           !nsown_capable(CAP_SYS_CHROOT) ||
-           !nsown_capable(CAP_SYS_ADMIN))
+           !ns_capable(current_user_ns(), CAP_SYS_CHROOT) ||
+           !ns_capable(current_user_ns(), CAP_SYS_ADMIN))
                return -EPERM;
  
        if (fs->users != 1)
diff --combined fs/open.c
index 8070825b285b93ae6120622eb315d673858e3ce0,1c9d23f7e683611767cb08d8d6d82150b61727b9..2a731b0d08bc456af047ad58ce68c6823f7b1972
+++ b/fs/open.c
@@@ -443,7 -443,7 +443,7 @@@ retry
                goto dput_and_out;
  
        error = -EPERM;
-       if (!nsown_capable(CAP_SYS_CHROOT))
+       if (!ns_capable(current_user_ns(), CAP_SYS_CHROOT))
                goto dput_and_out;
        error = security_path_chroot(&path);
        if (error)
@@@ -485,13 -485,14 +485,13 @@@ out_unlock
  
  SYSCALL_DEFINE2(fchmod, unsigned int, fd, umode_t, mode)
  {
 -      struct file * file;
 +      struct fd f = fdget(fd);
        int err = -EBADF;
  
 -      file = fget(fd);
 -      if (file) {
 -              audit_inode(NULL, file->f_path.dentry, 0);
 -              err = chmod_common(&file->f_path, mode);
 -              fput(file);
 +      if (f.file) {
 +              audit_inode(NULL, f.file->f_path.dentry, 0);
 +              err = chmod_common(&f.file->f_path, mode);
 +              fdput(f);
        }
        return err;
  }
@@@ -822,7 -823,7 +822,7 @@@ static inline int build_open_flags(int 
        int lookup_flags = 0;
        int acc_mode;
  
 -      if (flags & O_CREAT)
 +      if (flags & (O_CREAT | __O_TMPFILE))
                op->mode = (mode & S_IALLUGO) | S_IFREG;
        else
                op->mode = 0;
                if ((flags & O_TMPFILE_MASK) != O_TMPFILE)
                        return -EINVAL;
                acc_mode = MAY_OPEN | ACC_MODE(flags);
 +              if (!(acc_mode & MAY_WRITE))
 +                      return -EINVAL;
        } else if (flags & O_PATH) {
                /*
                 * If we have O_PATH in the open flag. Then we
diff --combined fs/proc/root.c
index e0a790da726d0f710a7585b0a8b6663e9902a0ac,45e5fb7da09bdadd24e2805812cedb529c52a9e7..87dbcbef7fe4b3535d4bfdcbc7590af24d3d5f0a
@@@ -110,7 -110,11 +110,11 @@@ static struct dentry *proc_mount(struc
                ns = task_active_pid_ns(current);
                options = data;
  
-               if (!current_user_ns()->may_mount_proc)
+               if (!capable(CAP_SYS_ADMIN) && !fs_fully_visible(fs_type))
+                       return ERR_PTR(-EPERM);
+               /* Does the mounter have privilege over the pid namespace? */
+               if (!ns_capable(ns->user_ns, CAP_SYS_ADMIN))
                        return ERR_PTR(-EPERM);
        }
  
@@@ -205,9 -209,7 +209,9 @@@ static struct dentry *proc_root_lookup(
  static int proc_root_readdir(struct file *file, struct dir_context *ctx)
  {
        if (ctx->pos < FIRST_PROCESS_ENTRY) {
 -              proc_readdir(file, ctx);
 +              int error = proc_readdir(file, ctx);
 +              if (unlikely(error <= 0))
 +                      return error;
                ctx->pos = FIRST_PROCESS_ENTRY;
        }
  
diff --combined fs/sysfs/mount.c
index fd7ce7a39f91acfa9304fd80e9ff5a6e7e1139cc,8c69ef49c7f39bc6c3e013d10e9614378075f4bb..834ec2cdb7a37e070b7e5ed04ec684ba46cb2093
@@@ -64,7 -64,7 +64,7 @@@ static int sysfs_fill_super(struct supe
        /* instantiate and link root dentry */
        root = d_make_root(inode);
        if (!root) {
 -              pr_debug("%s: could not get root dentry!\n",__func__);
 +              pr_debug("%s: could not get root dentry!\n", __func__);
                return -ENOMEM;
        }
        root->d_fsdata = &sysfs_root;
@@@ -112,8 -112,15 +112,15 @@@ static struct dentry *sysfs_mount(struc
        struct super_block *sb;
        int error;
  
-       if (!(flags & MS_KERNMOUNT) && !current_user_ns()->may_mount_sysfs)
-               return ERR_PTR(-EPERM);
+       if (!(flags & MS_KERNMOUNT)) {
+               if (!capable(CAP_SYS_ADMIN) && !fs_fully_visible(fs_type))
+                       return ERR_PTR(-EPERM);
+               for (type = KOBJ_NS_TYPE_NONE; type < KOBJ_NS_TYPES; type++) {
+                       if (!kobj_ns_current_may_mount(type))
+                               return ERR_PTR(-EPERM);
+               }
+       }
  
        info = kzalloc(sizeof(*info), GFP_KERNEL);
        if (!info)
diff --combined include/linux/fs.h
index 3b4cd8296e4165625b11584b7f134a5587b0f23b,3050c620f062f92e601a45d146409c8df5294038..529d8711baba0e6ba07c57921694302332d762fd
@@@ -46,7 -46,6 +46,7 @@@ struct vfsmount
  struct cred;
  struct swap_info_struct;
  struct seq_file;
 +struct workqueue_struct;
  
  extern void __init inode_init(void);
  extern void __init inode_init_early(void);
@@@ -64,7 -63,8 +64,7 @@@ struct buffer_head
  typedef int (get_block_t)(struct inode *inode, sector_t iblock,
                        struct buffer_head *bh_result, int create);
  typedef void (dio_iodone_t)(struct kiocb *iocb, loff_t offset,
 -                      ssize_t bytes, void *private, int ret,
 -                      bool is_async);
 +                      ssize_t bytes, void *private);
  
  #define MAY_EXEC              0x00000001
  #define MAY_WRITE             0x00000002
@@@ -1328,9 -1328,6 +1328,9 @@@ struct super_block 
  
        /* Being remounted read-only */
        int s_readonly_remount;
 +
 +      /* AIO completions deferred from interrupt context */
 +      struct workqueue_struct *s_dio_done_wq;
  };
  
  /* superblock cache pruning functions */
@@@ -1807,7 -1804,7 +1807,7 @@@ enum file_time_flags 
        S_VERSION = 8,
  };
  
 -extern void touch_atime(struct path *);
 +extern void touch_atime(const struct path *);
  static inline void file_accessed(struct file *file)
  {
        if (!(file->f_flags & O_NOATIME))
@@@ -1900,6 -1897,7 +1900,7 @@@ extern int vfs_ustat(dev_t, struct ksta
  extern int freeze_super(struct super_block *super);
  extern int thaw_super(struct super_block *super);
  extern bool our_mnt(struct vfsmount *mnt);
+ extern bool fs_fully_visible(struct file_system_type *);
  
  extern int current_umask(void);
  
@@@ -2506,7 -2504,6 +2507,7 @@@ extern void generic_fillattr(struct ino
  extern int vfs_getattr(struct path *, struct kstat *);
  void __inode_add_bytes(struct inode *inode, loff_t bytes);
  void inode_add_bytes(struct inode *inode, loff_t bytes);
 +void __inode_sub_bytes(struct inode *inode, loff_t bytes);
  void inode_sub_bytes(struct inode *inode, loff_t bytes);
  loff_t inode_get_bytes(struct inode *inode);
  void inode_set_bytes(struct inode *inode, loff_t bytes);
index 14105c26a83618da5d91ae09331fb83ba8f8ad6b,4ce009324933ebc2b048ebd5f834b0c1bf44e52d..4db29859464f3af3d78caafa41a1e7525034a589
@@@ -23,12 -23,9 +23,10 @@@ struct user_namespace 
        struct uid_gid_map      projid_map;
        atomic_t                count;
        struct user_namespace   *parent;
 +      int                     level;
        kuid_t                  owner;
        kgid_t                  group;
        unsigned int            proc_inum;
-       bool                    may_mount_sysfs;
-       bool                    may_mount_proc;
  };
  
  extern struct user_namespace init_user_ns;
@@@ -85,6 -82,4 +83,4 @@@ static inline void put_user_ns(struct u
  
  #endif
  
- void update_mnt_policy(struct user_namespace *userns);
  #endif /* _LINUX_USER_H */
diff --combined kernel/fork.c
index bf46287c91a45cc4f52d348d4be503e1ecf94620,eb45f1d72703a542f2b5bc53b62068661820599a..c9eaf20130021fbe3c8b24f4634ff7cd0ef02c3f
@@@ -1177,8 -1177,7 +1177,8 @@@ static struct task_struct *copy_process
         * don't allow the creation of threads.
         */
        if ((clone_flags & (CLONE_VM|CLONE_NEWPID)) &&
 -          (task_active_pid_ns(current) != current->nsproxy->pid_ns))
 +          (task_active_pid_ns(current) !=
 +           current->nsproxy->pid_ns_for_children))
                return ERR_PTR(-EINVAL);
  
        retval = security_task_create(clone_flags);
  
        if (pid != &init_struct_pid) {
                retval = -ENOMEM;
 -              pid = alloc_pid(p->nsproxy->pid_ns);
 +              pid = alloc_pid(p->nsproxy->pid_ns_for_children);
                if (!pid)
                        goto bad_fork_cleanup_io;
        }
@@@ -1547,7 -1546,7 +1547,7 @@@ static inline void init_idle_pids(struc
        }
  }
  
 -struct task_struct * __cpuinit fork_idle(int cpu)
 +struct task_struct *fork_idle(int cpu)
  {
        struct task_struct *task;
        task = copy_process(CLONE_VM, 0, 0, NULL, &init_struct_pid, 0);
@@@ -1680,12 -1679,6 +1680,12 @@@ SYSCALL_DEFINE5(clone, unsigned long, n
                 int __user *, parent_tidptr,
                 int __user *, child_tidptr,
                 int, tls_val)
 +#elif defined(CONFIG_CLONE_BACKWARDS3)
 +SYSCALL_DEFINE6(clone, unsigned long, clone_flags, unsigned long, newsp,
 +              int, stack_size,
 +              int __user *, parent_tidptr,
 +              int __user *, child_tidptr,
 +              int, tls_val)
  #else
  SYSCALL_DEFINE5(clone, unsigned long, clone_flags, unsigned long, newsp,
                 int __user *, parent_tidptr,
@@@ -1824,11 -1817,6 +1824,6 @@@ SYSCALL_DEFINE1(unshare, unsigned long
         */
        if (unshare_flags & CLONE_NEWUSER)
                unshare_flags |= CLONE_THREAD | CLONE_FS;
-       /*
-        * If unsharing a pid namespace must also unshare the thread.
-        */
-       if (unshare_flags & CLONE_NEWPID)
-               unshare_flags |= CLONE_THREAD;
        /*
         * If unsharing a thread from a thread group, must also unshare vm.
         */
diff --combined kernel/nsproxy.c
index 997cbb951a3bd07783e02e5354c3ac369ae4e356,a1ed01139276eb264fc35ce631c4ce255e9b6aa4..8e7811086b826cbc42b6be594d2fc605fee0b84d
  static struct kmem_cache *nsproxy_cachep;
  
  struct nsproxy init_nsproxy = {
 -      .count  = ATOMIC_INIT(1),
 -      .uts_ns = &init_uts_ns,
 +      .count                  = ATOMIC_INIT(1),
 +      .uts_ns                 = &init_uts_ns,
  #if defined(CONFIG_POSIX_MQUEUE) || defined(CONFIG_SYSVIPC)
 -      .ipc_ns = &init_ipc_ns,
 +      .ipc_ns                 = &init_ipc_ns,
  #endif
 -      .mnt_ns = NULL,
 -      .pid_ns = &init_pid_ns,
 +      .mnt_ns                 = NULL,
 +      .pid_ns_for_children    = &init_pid_ns,
  #ifdef CONFIG_NET
 -      .net_ns = &init_net,
 +      .net_ns                 = &init_net,
  #endif
  };
  
@@@ -85,10 -85,9 +85,10 @@@ static struct nsproxy *create_new_names
                goto out_ipc;
        }
  
 -      new_nsp->pid_ns = copy_pid_ns(flags, user_ns, tsk->nsproxy->pid_ns);
 -      if (IS_ERR(new_nsp->pid_ns)) {
 -              err = PTR_ERR(new_nsp->pid_ns);
 +      new_nsp->pid_ns_for_children =
 +              copy_pid_ns(flags, user_ns, tsk->nsproxy->pid_ns_for_children);
 +      if (IS_ERR(new_nsp->pid_ns_for_children)) {
 +              err = PTR_ERR(new_nsp->pid_ns_for_children);
                goto out_pid;
        }
  
        return new_nsp;
  
  out_net:
 -      if (new_nsp->pid_ns)
 -              put_pid_ns(new_nsp->pid_ns);
 +      if (new_nsp->pid_ns_for_children)
 +              put_pid_ns(new_nsp->pid_ns_for_children);
  out_pid:
        if (new_nsp->ipc_ns)
                put_ipc_ns(new_nsp->ipc_ns);
@@@ -126,22 -125,16 +126,16 @@@ int copy_namespaces(unsigned long flags
        struct nsproxy *old_ns = tsk->nsproxy;
        struct user_namespace *user_ns = task_cred_xxx(tsk, user_ns);
        struct nsproxy *new_ns;
-       int err = 0;
  
-       if (!old_ns)
+       if (likely(!(flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC |
+                             CLONE_NEWPID | CLONE_NEWNET)))) {
+               get_nsproxy(old_ns);
                return 0;
-       get_nsproxy(old_ns);
-       if (!(flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC |
-                               CLONE_NEWPID | CLONE_NEWNET)))
-               return 0;
-       if (!ns_capable(user_ns, CAP_SYS_ADMIN)) {
-               err = -EPERM;
-               goto out;
        }
  
+       if (!ns_capable(user_ns, CAP_SYS_ADMIN))
+               return -EPERM;
        /*
         * CLONE_NEWIPC must detach from the undolist: after switching
         * to a new ipc namespace, the semaphore arrays from the old
         * means share undolist with parent, so we must forbid using
         * it along with CLONE_NEWIPC.
         */
-       if ((flags & CLONE_NEWIPC) && (flags & CLONE_SYSVSEM)) {
-               err = -EINVAL;
-               goto out;
-       }
+       if ((flags & (CLONE_NEWIPC | CLONE_SYSVSEM)) ==
+               (CLONE_NEWIPC | CLONE_SYSVSEM)) 
+               return -EINVAL;
  
        new_ns = create_new_namespaces(flags, tsk, user_ns, tsk->fs);
-       if (IS_ERR(new_ns)) {
-               err = PTR_ERR(new_ns);
-               goto out;
-       }
+       if (IS_ERR(new_ns))
+               return  PTR_ERR(new_ns);
  
        tsk->nsproxy = new_ns;
- out:
-       put_nsproxy(old_ns);
-       return err;
+       return 0;
  }
  
  void free_nsproxy(struct nsproxy *ns)
                put_uts_ns(ns->uts_ns);
        if (ns->ipc_ns)
                put_ipc_ns(ns->ipc_ns);
 -      if (ns->pid_ns)
 -              put_pid_ns(ns->pid_ns);
 +      if (ns->pid_ns_for_children)
 +              put_pid_ns(ns->pid_ns_for_children);
        put_net(ns->net_ns);
        kmem_cache_free(nsproxy_cachep, ns);
  }
diff --combined kernel/pid_namespace.c
index 601bb361c235a9ff28327a92b1e1a4387d128d0f,ee1f6bb83d6783bf83d414ea35e9a8ca2faef2cc..42086551a24a19d9563148ee1810e30e68cccbbe
@@@ -329,7 -329,7 +329,7 @@@ static int pidns_install(struct nsprox
        struct pid_namespace *ancestor, *new = ns;
  
        if (!ns_capable(new->user_ns, CAP_SYS_ADMIN) ||
-           !nsown_capable(CAP_SYS_ADMIN))
+           !ns_capable(current_user_ns(), CAP_SYS_ADMIN))
                return -EPERM;
  
        /*
        if (ancestor != active)
                return -EINVAL;
  
 -      put_pid_ns(nsproxy->pid_ns);
 -      nsproxy->pid_ns = get_pid_ns(new);
 +      put_pid_ns(nsproxy->pid_ns_for_children);
 +      nsproxy->pid_ns_for_children = get_pid_ns(new);
        return 0;
  }
  
diff --combined kernel/user_namespace.c
index 9064b919a4066fe9fca676581479e9162de56717,d58ad1e7a79469772c7a0e07611cbe3120b3afa6..13fb1134ba582e49c8aa3643feada72a2b0dae8b
@@@ -62,9 -62,6 +62,9 @@@ int create_user_ns(struct cred *new
        kgid_t group = new->egid;
        int ret;
  
 +      if (parent_ns->level > 32)
 +              return -EUSERS;
 +
        /*
         * Verify that we can not violate the policy of which files
         * may be accessed that is specified by the root directory,
        atomic_set(&ns->count, 1);
        /* Leave the new->user_ns reference with the new user namespace. */
        ns->parent = parent_ns;
 +      ns->level = parent_ns->level + 1;
        ns->owner = owner;
        ns->group = group;
  
        set_cred_user_ns(new, ns);
  
-       update_mnt_policy(ns);
        return 0;
  }
  
  int unshare_userns(unsigned long unshare_flags, struct cred **new_cred)
  {
        struct cred *cred;
 +      int err = -ENOMEM;
  
        if (!(unshare_flags & CLONE_NEWUSER))
                return 0;
  
        cred = prepare_creds();
 -      if (!cred)
 -              return -ENOMEM;
 +      if (cred) {
 +              err = create_user_ns(cred);
 +              if (err)
 +                      put_cred(cred);
 +              else
 +                      *new_cred = cred;
 +      }
  
 -      *new_cred = cred;
 -      return create_user_ns(cred);
 +      return err;
  }
  
  void free_user_ns(struct user_namespace *ns)
diff --combined lib/kobject.c
index 1d46c151a4aea8ca741623931bba5d737ac5bc64,3bbde222c90f743928674eb52a131850b4162905..962175134702dace0078edfd0ec7f092bc646536
@@@ -545,8 -545,8 +545,8 @@@ static void kobject_cleanup(struct kobj
        struct kobj_type *t = get_ktype(kobj);
        const char *name = kobj->name;
  
 -      pr_debug("kobject: '%s' (%p): %s\n",
 -               kobject_name(kobj), kobj, __func__);
 +      pr_debug("kobject: '%s' (%p): %s, parent %p\n",
 +               kobject_name(kobj), kobj, __func__, kobj->parent);
  
        if (t && !t->release)
                pr_debug("kobject: '%s' (%p): does not have a release() "
        }
  }
  
 +#ifdef CONFIG_DEBUG_KOBJECT_RELEASE
 +static void kobject_delayed_cleanup(struct work_struct *work)
 +{
 +      kobject_cleanup(container_of(to_delayed_work(work),
 +                                   struct kobject, release));
 +}
 +#endif
 +
  static void kobject_release(struct kref *kref)
  {
 -      kobject_cleanup(container_of(kref, struct kobject, kref));
 +      struct kobject *kobj = container_of(kref, struct kobject, kref);
 +#ifdef CONFIG_DEBUG_KOBJECT_RELEASE
 +      pr_debug("kobject: '%s' (%p): %s, parent %p (delayed)\n",
 +               kobject_name(kobj), kobj, __func__, kobj->parent);
 +      INIT_DELAYED_WORK(&kobj->release, kobject_delayed_cleanup);
 +      schedule_delayed_work(&kobj->release, HZ);
 +#else
 +      kobject_cleanup(kobj);
 +#endif
  }
  
  /**
@@@ -931,6 -915,21 +931,21 @@@ const struct kobj_ns_type_operations *k
        return kobj_child_ns_ops(kobj->parent);
  }
  
+ bool kobj_ns_current_may_mount(enum kobj_ns_type type)
+ {
+       bool may_mount = false;
+       if (type == KOBJ_NS_TYPE_NONE)
+               return true;
+       spin_lock(&kobj_ns_type_lock);
+       if ((type > KOBJ_NS_TYPE_NONE) && (type < KOBJ_NS_TYPES) &&
+           kobj_ns_ops_tbl[type])
+               may_mount = kobj_ns_ops_tbl[type]->current_may_mount();
+       spin_unlock(&kobj_ns_type_lock);
+       return may_mount;
+ }
  
  void *kobj_ns_grab_current(enum kobj_ns_type type)
  {
diff --combined net/core/net-sysfs.c
index 3f40ea9de8149d9dc56f09b884160ff43a53a3d0,9bd9ae16adf5ae589b545a2fe18e6b2337b32122..d954b56b4e47976b3b003c41cb7b39758d5d044d
@@@ -60,19 -60,12 +60,19 @@@ static ssize_t format_##field(const str
  {                                                                     \
        return sprintf(buf, format_string, net->field);                 \
  }                                                                     \
 -static ssize_t show_##field(struct device *dev,                               \
 +static ssize_t field##_show(struct device *dev,                               \
                            struct device_attribute *attr, char *buf)   \
  {                                                                     \
        return netdev_show(dev, attr, buf, format_##field);             \
 -}
 +}                                                                     \
  
 +#define NETDEVICE_SHOW_RO(field, format_string)                               \
 +NETDEVICE_SHOW(field, format_string);                                 \
 +static DEVICE_ATTR_RO(field)
 +
 +#define NETDEVICE_SHOW_RW(field, format_string)                               \
 +NETDEVICE_SHOW(field, format_string);                                 \
 +static DEVICE_ATTR_RW(field)
  
  /* use same locking and permission rules as SIF* ioctl's */
  static ssize_t netdev_store(struct device *dev, struct device_attribute *attr,
        return ret;
  }
  
 -NETDEVICE_SHOW(dev_id, fmt_hex);
 -NETDEVICE_SHOW(addr_assign_type, fmt_dec);
 -NETDEVICE_SHOW(addr_len, fmt_dec);
 -NETDEVICE_SHOW(iflink, fmt_dec);
 -NETDEVICE_SHOW(ifindex, fmt_dec);
 -NETDEVICE_SHOW(type, fmt_dec);
 -NETDEVICE_SHOW(link_mode, fmt_dec);
 +NETDEVICE_SHOW_RO(dev_id, fmt_hex);
 +NETDEVICE_SHOW_RO(addr_assign_type, fmt_dec);
 +NETDEVICE_SHOW_RO(addr_len, fmt_dec);
 +NETDEVICE_SHOW_RO(iflink, fmt_dec);
 +NETDEVICE_SHOW_RO(ifindex, fmt_dec);
 +NETDEVICE_SHOW_RO(type, fmt_dec);
 +NETDEVICE_SHOW_RO(link_mode, fmt_dec);
  
  /* use same locking rules as GIFHWADDR ioctl's */
 -static ssize_t show_address(struct device *dev, struct device_attribute *attr,
 +static ssize_t address_show(struct device *dev, struct device_attribute *attr,
                            char *buf)
  {
        struct net_device *net = to_net_dev(dev);
        read_unlock(&dev_base_lock);
        return ret;
  }
 +static DEVICE_ATTR_RO(address);
  
 -static ssize_t show_broadcast(struct device *dev,
 -                          struct device_attribute *attr, char *buf)
 +static ssize_t broadcast_show(struct device *dev,
 +                            struct device_attribute *attr, char *buf)
  {
        struct net_device *net = to_net_dev(dev);
        if (dev_isalive(net))
                return sysfs_format_mac(buf, net->broadcast, net->addr_len);
        return -EINVAL;
  }
 +static DEVICE_ATTR_RO(broadcast);
  
  static int change_carrier(struct net_device *net, unsigned long new_carrier)
  {
        return dev_change_carrier(net, (bool) new_carrier);
  }
  
 -static ssize_t store_carrier(struct device *dev, struct device_attribute *attr,
 -                       const char *buf, size_t len)
 +static ssize_t carrier_store(struct device *dev, struct device_attribute *attr,
 +                           const char *buf, size_t len)
  {
        return netdev_store(dev, attr, buf, len, change_carrier);
  }
  
 -static ssize_t show_carrier(struct device *dev,
 +static ssize_t carrier_show(struct device *dev,
                            struct device_attribute *attr, char *buf)
  {
        struct net_device *netdev = to_net_dev(dev);
        }
        return -EINVAL;
  }
 +static DEVICE_ATTR_RW(carrier);
  
 -static ssize_t show_speed(struct device *dev,
 +static ssize_t speed_show(struct device *dev,
                          struct device_attribute *attr, char *buf)
  {
        struct net_device *netdev = to_net_dev(dev);
        rtnl_unlock();
        return ret;
  }
 +static DEVICE_ATTR_RO(speed);
  
 -static ssize_t show_duplex(struct device *dev,
 +static ssize_t duplex_show(struct device *dev,
                           struct device_attribute *attr, char *buf)
  {
        struct net_device *netdev = to_net_dev(dev);
        rtnl_unlock();
        return ret;
  }
 +static DEVICE_ATTR_RO(duplex);
  
 -static ssize_t show_dormant(struct device *dev,
 +static ssize_t dormant_show(struct device *dev,
                            struct device_attribute *attr, char *buf)
  {
        struct net_device *netdev = to_net_dev(dev);
  
        return -EINVAL;
  }
 +static DEVICE_ATTR_RO(dormant);
  
  static const char *const operstates[] = {
        "unknown",
        "up"
  };
  
 -static ssize_t show_operstate(struct device *dev,
 +static ssize_t operstate_show(struct device *dev,
                              struct device_attribute *attr, char *buf)
  {
        const struct net_device *netdev = to_net_dev(dev);
  
        return sprintf(buf, "%s\n", operstates[operstate]);
  }
 +static DEVICE_ATTR_RO(operstate);
  
  /* read-write attributes */
 -NETDEVICE_SHOW(mtu, fmt_dec);
  
  static int change_mtu(struct net_device *net, unsigned long new_mtu)
  {
        return dev_set_mtu(net, (int) new_mtu);
  }
  
 -static ssize_t store_mtu(struct device *dev, struct device_attribute *attr,
 +static ssize_t mtu_store(struct device *dev, struct device_attribute *attr,
                         const char *buf, size_t len)
  {
        return netdev_store(dev, attr, buf, len, change_mtu);
  }
 -
 -NETDEVICE_SHOW(flags, fmt_hex);
 +NETDEVICE_SHOW_RW(mtu, fmt_dec);
  
  static int change_flags(struct net_device *net, unsigned long new_flags)
  {
        return dev_change_flags(net, (unsigned int) new_flags);
  }
  
 -static ssize_t store_flags(struct device *dev, struct device_attribute *attr,
 +static ssize_t flags_store(struct device *dev, struct device_attribute *attr,
                           const char *buf, size_t len)
  {
        return netdev_store(dev, attr, buf, len, change_flags);
  }
 -
 -NETDEVICE_SHOW(tx_queue_len, fmt_ulong);
 +NETDEVICE_SHOW_RW(flags, fmt_hex);
  
  static int change_tx_queue_len(struct net_device *net, unsigned long new_len)
  {
        return 0;
  }
  
 -static ssize_t store_tx_queue_len(struct device *dev,
 +static ssize_t tx_queue_len_store(struct device *dev,
                                  struct device_attribute *attr,
                                  const char *buf, size_t len)
  {
  
        return netdev_store(dev, attr, buf, len, change_tx_queue_len);
  }
 +NETDEVICE_SHOW_RW(tx_queue_len, fmt_ulong);
  
 -static ssize_t store_ifalias(struct device *dev, struct device_attribute *attr,
 +static ssize_t ifalias_store(struct device *dev, struct device_attribute *attr,
                             const char *buf, size_t len)
  {
        struct net_device *netdev = to_net_dev(dev);
        return ret < 0 ? ret : len;
  }
  
 -static ssize_t show_ifalias(struct device *dev,
 +static ssize_t ifalias_show(struct device *dev,
                            struct device_attribute *attr, char *buf)
  {
        const struct net_device *netdev = to_net_dev(dev);
        rtnl_unlock();
        return ret;
  }
 -
 -NETDEVICE_SHOW(group, fmt_dec);
 +static DEVICE_ATTR_RW(ifalias);
  
  static int change_group(struct net_device *net, unsigned long new_group)
  {
        return 0;
  }
  
 -static ssize_t store_group(struct device *dev, struct device_attribute *attr,
 -                       const char *buf, size_t len)
 +static ssize_t group_store(struct device *dev, struct device_attribute *attr,
 +                         const char *buf, size_t len)
  {
        return netdev_store(dev, attr, buf, len, change_group);
  }
 +NETDEVICE_SHOW(group, fmt_dec);
 +static DEVICE_ATTR(netdev_group, S_IRUGO | S_IWUSR, group_show, group_store);
  
 -static struct device_attribute net_class_attributes[] = {
 -      __ATTR(addr_assign_type, S_IRUGO, show_addr_assign_type, NULL),
 -      __ATTR(addr_len, S_IRUGO, show_addr_len, NULL),
 -      __ATTR(dev_id, S_IRUGO, show_dev_id, NULL),
 -      __ATTR(ifalias, S_IRUGO | S_IWUSR, show_ifalias, store_ifalias),
 -      __ATTR(iflink, S_IRUGO, show_iflink, NULL),
 -      __ATTR(ifindex, S_IRUGO, show_ifindex, NULL),
 -      __ATTR(type, S_IRUGO, show_type, NULL),
 -      __ATTR(link_mode, S_IRUGO, show_link_mode, NULL),
 -      __ATTR(address, S_IRUGO, show_address, NULL),
 -      __ATTR(broadcast, S_IRUGO, show_broadcast, NULL),
 -      __ATTR(carrier, S_IRUGO | S_IWUSR, show_carrier, store_carrier),
 -      __ATTR(speed, S_IRUGO, show_speed, NULL),
 -      __ATTR(duplex, S_IRUGO, show_duplex, NULL),
 -      __ATTR(dormant, S_IRUGO, show_dormant, NULL),
 -      __ATTR(operstate, S_IRUGO, show_operstate, NULL),
 -      __ATTR(mtu, S_IRUGO | S_IWUSR, show_mtu, store_mtu),
 -      __ATTR(flags, S_IRUGO | S_IWUSR, show_flags, store_flags),
 -      __ATTR(tx_queue_len, S_IRUGO | S_IWUSR, show_tx_queue_len,
 -             store_tx_queue_len),
 -      __ATTR(netdev_group, S_IRUGO | S_IWUSR, show_group, store_group),
 -      {}
 +static ssize_t phys_port_id_show(struct device *dev,
 +                               struct device_attribute *attr, char *buf)
 +{
 +      struct net_device *netdev = to_net_dev(dev);
 +      ssize_t ret = -EINVAL;
 +
 +      if (!rtnl_trylock())
 +              return restart_syscall();
 +
 +      if (dev_isalive(netdev)) {
 +              struct netdev_phys_port_id ppid;
 +
 +              ret = dev_get_phys_port_id(netdev, &ppid);
 +              if (!ret)
 +                      ret = sprintf(buf, "%*phN\n", ppid.id_len, ppid.id);
 +      }
 +      rtnl_unlock();
 +
 +      return ret;
 +}
 +static DEVICE_ATTR_RO(phys_port_id);
 +
 +static struct attribute *net_class_attrs[] = {
 +      &dev_attr_netdev_group.attr,
 +      &dev_attr_type.attr,
 +      &dev_attr_dev_id.attr,
 +      &dev_attr_iflink.attr,
 +      &dev_attr_ifindex.attr,
 +      &dev_attr_addr_assign_type.attr,
 +      &dev_attr_addr_len.attr,
 +      &dev_attr_link_mode.attr,
 +      &dev_attr_address.attr,
 +      &dev_attr_broadcast.attr,
 +      &dev_attr_speed.attr,
 +      &dev_attr_duplex.attr,
 +      &dev_attr_dormant.attr,
 +      &dev_attr_operstate.attr,
 +      &dev_attr_ifalias.attr,
 +      &dev_attr_carrier.attr,
 +      &dev_attr_mtu.attr,
 +      &dev_attr_flags.attr,
 +      &dev_attr_tx_queue_len.attr,
 +      &dev_attr_phys_port_id.attr,
 +      NULL,
  };
 +ATTRIBUTE_GROUPS(net_class);
  
  /* Show a given an attribute in the statistics group */
  static ssize_t netstat_show(const struct device *d,
  
  /* generate a read-only statistics attribute */
  #define NETSTAT_ENTRY(name)                                           \
 -static ssize_t show_##name(struct device *d,                          \
 +static ssize_t name##_show(struct device *d,                          \
                           struct device_attribute *attr, char *buf)    \
  {                                                                     \
        return netstat_show(d, attr, buf,                               \
                            offsetof(struct rtnl_link_stats64, name));  \
  }                                                                     \
 -static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL)
 +static DEVICE_ATTR_RO(name)
  
  NETSTAT_ENTRY(rx_packets);
  NETSTAT_ENTRY(tx_packets);
@@@ -493,9 -457,6 +493,9 @@@ static struct attribute_group wireless_
        .attrs = wireless_attrs,
  };
  #endif
 +
 +#else /* CONFIG_SYSFS */
 +#define net_class_groups      NULL
  #endif /* CONFIG_SYSFS */
  
  #ifdef CONFIG_RPS
@@@ -1196,6 -1157,13 +1196,13 @@@ static void remove_queue_kobjects(struc
  #endif
  }
  
+ static bool net_current_may_mount(void)
+ {
+       struct net *net = current->nsproxy->net_ns;
+       return ns_capable(net->user_ns, CAP_SYS_ADMIN);
+ }
  static void *net_grab_current_ns(void)
  {
        struct net *ns = current->nsproxy->net_ns;
@@@ -1218,6 -1186,7 +1225,7 @@@ static const void *net_netlink_ns(struc
  
  struct kobj_ns_type_operations net_ns_type_operations = {
        .type = KOBJ_NS_TYPE_NET,
+       .current_may_mount = net_current_may_mount,
        .grab_current_ns = net_grab_current_ns,
        .netlink_ns = net_netlink_ns,
        .initial_ns = net_initial_ns,
@@@ -1268,7 -1237,9 +1276,7 @@@ static const void *net_namespace(struc
  static struct class net_class = {
        .name = "net",
        .dev_release = netdev_release,
 -#ifdef CONFIG_SYSFS
 -      .dev_attrs = net_class_attributes,
 -#endif /* CONFIG_SYSFS */
 +      .dev_groups = net_class_groups,
        .dev_uevent = netdev_uevent,
        .ns_type = &net_ns_type_operations,
        .namespace = net_namespace,
diff --combined net/core/scm.c
index b4da80b1cc07d28eafec50a6185d6d2a2ece61af,c346f58d97c28cc43bda5dbdd836959585f9bee3..b442e7e25e601b19a8237d1a90efbaa1d572fc19
@@@ -54,11 -54,11 +54,11 @@@ static __inline__ int scm_check_creds(s
                return -EINVAL;
  
        if ((creds->pid == task_tgid_vnr(current) ||
 -           ns_capable(current->nsproxy->pid_ns->user_ns, CAP_SYS_ADMIN)) &&
 +           ns_capable(task_active_pid_ns(current)->user_ns, CAP_SYS_ADMIN)) &&
            ((uid_eq(uid, cred->uid)   || uid_eq(uid, cred->euid) ||
-             uid_eq(uid, cred->suid)) || nsown_capable(CAP_SETUID)) &&
+             uid_eq(uid, cred->suid)) || ns_capable(cred->user_ns, CAP_SETUID)) &&
            ((gid_eq(gid, cred->gid)   || gid_eq(gid, cred->egid) ||
-             gid_eq(gid, cred->sgid)) || nsown_capable(CAP_SETGID))) {
+             gid_eq(gid, cred->sgid)) || ns_capable(cred->user_ns, CAP_SETGID))) {
               return 0;
        }
        return -EPERM;