aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2017-01-15 19:09:50 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2017-01-15 19:09:50 -0500
commit99421c1cb27fb837e93b517036fab4500fe39de5 (patch)
treede5fc5bacb671223f389793ad643cebe520bc292
parentc92816275674c1491ce228ee49aa030a5fa1be04 (diff)
parent93362fa47fe98b62e4a34ab408c4a418432e7939 (diff)
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace
Pull namespace fixes from Eric Biederman: "This tree contains 4 fixes. The first is a fix for a race that can causes oopses under the right circumstances, and that someone just recently encountered. Past that are several small trivial correct fixes. A real issue that was blocking development of an out of tree driver, but does not appear to have caused any actual problems for in-tree code. A potential deadlock that was reported by lockdep. And a deadlock people have experienced and took the time to track down caused by a cleanup that removed the code to drop a reference count" * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace: sysctl: Drop reference added by grab_header in proc_sys_readdir pid: fix lockdep deadlock warning due to ucount_lock libfs: Modify mount_pseudo_xattr to be clear it is not a userspace mount mnt: Protect the mountpoint hashtable with mount_lock
-rw-r--r--fs/dcache.c7
-rw-r--r--fs/libfs.c3
-rw-r--r--fs/namespace.c64
-rw-r--r--fs/proc/proc_sysctl.c3
-rw-r--r--kernel/pid_namespace.c10
5 files changed, 60 insertions, 27 deletions
diff --git a/fs/dcache.c b/fs/dcache.c
index 769903dbc19d..95d71eda8142 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -1336,8 +1336,11 @@ int d_set_mounted(struct dentry *dentry)
1336 } 1336 }
1337 spin_lock(&dentry->d_lock); 1337 spin_lock(&dentry->d_lock);
1338 if (!d_unlinked(dentry)) { 1338 if (!d_unlinked(dentry)) {
1339 dentry->d_flags |= DCACHE_MOUNTED; 1339 ret = -EBUSY;
1340 ret = 0; 1340 if (!d_mountpoint(dentry)) {
1341 dentry->d_flags |= DCACHE_MOUNTED;
1342 ret = 0;
1343 }
1341 } 1344 }
1342 spin_unlock(&dentry->d_lock); 1345 spin_unlock(&dentry->d_lock);
1343out: 1346out:
diff --git a/fs/libfs.c b/fs/libfs.c
index e973cd51f126..28d6f35feed6 100644
--- a/fs/libfs.c
+++ b/fs/libfs.c
@@ -245,7 +245,8 @@ struct dentry *mount_pseudo_xattr(struct file_system_type *fs_type, char *name,
245 struct inode *root; 245 struct inode *root;
246 struct qstr d_name = QSTR_INIT(name, strlen(name)); 246 struct qstr d_name = QSTR_INIT(name, strlen(name));
247 247
248 s = sget(fs_type, NULL, set_anon_super, MS_NOUSER, NULL); 248 s = sget_userns(fs_type, NULL, set_anon_super, MS_KERNMOUNT|MS_NOUSER,
249 &init_user_ns, NULL);
249 if (IS_ERR(s)) 250 if (IS_ERR(s))
250 return ERR_CAST(s); 251 return ERR_CAST(s);
251 252
diff --git a/fs/namespace.c b/fs/namespace.c
index b5b1259e064f..487ba30bb5c6 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -742,26 +742,50 @@ static struct mountpoint *lookup_mountpoint(struct dentry *dentry)
742 return NULL; 742 return NULL;
743} 743}
744 744
745static struct mountpoint *new_mountpoint(struct dentry *dentry) 745static struct mountpoint *get_mountpoint(struct dentry *dentry)
746{ 746{
747 struct hlist_head *chain = mp_hash(dentry); 747 struct mountpoint *mp, *new = NULL;
748 struct mountpoint *mp;
749 int ret; 748 int ret;
750 749
751 mp = kmalloc(sizeof(struct mountpoint), GFP_KERNEL); 750 if (d_mountpoint(dentry)) {
752 if (!mp) 751mountpoint:
752 read_seqlock_excl(&mount_lock);
753 mp = lookup_mountpoint(dentry);
754 read_sequnlock_excl(&mount_lock);
755 if (mp)
756 goto done;
757 }
758
759 if (!new)
760 new = kmalloc(sizeof(struct mountpoint), GFP_KERNEL);
761 if (!new)
753 return ERR_PTR(-ENOMEM); 762 return ERR_PTR(-ENOMEM);
754 763
764
765 /* Exactly one processes may set d_mounted */
755 ret = d_set_mounted(dentry); 766 ret = d_set_mounted(dentry);
756 if (ret) {
757 kfree(mp);
758 return ERR_PTR(ret);
759 }
760 767
761 mp->m_dentry = dentry; 768 /* Someone else set d_mounted? */
762 mp->m_count = 1; 769 if (ret == -EBUSY)
763 hlist_add_head(&mp->m_hash, chain); 770 goto mountpoint;
764 INIT_HLIST_HEAD(&mp->m_list); 771
772 /* The dentry is not available as a mountpoint? */
773 mp = ERR_PTR(ret);
774 if (ret)
775 goto done;
776
777 /* Add the new mountpoint to the hash table */
778 read_seqlock_excl(&mount_lock);
779 new->m_dentry = dentry;
780 new->m_count = 1;
781 hlist_add_head(&new->m_hash, mp_hash(dentry));
782 INIT_HLIST_HEAD(&new->m_list);
783 read_sequnlock_excl(&mount_lock);
784
785 mp = new;
786 new = NULL;
787done:
788 kfree(new);
765 return mp; 789 return mp;
766} 790}
767 791
@@ -1595,11 +1619,11 @@ void __detach_mounts(struct dentry *dentry)
1595 struct mount *mnt; 1619 struct mount *mnt;
1596 1620
1597 namespace_lock(); 1621 namespace_lock();
1622 lock_mount_hash();
1598 mp = lookup_mountpoint(dentry); 1623 mp = lookup_mountpoint(dentry);
1599 if (IS_ERR_OR_NULL(mp)) 1624 if (IS_ERR_OR_NULL(mp))
1600 goto out_unlock; 1625 goto out_unlock;
1601 1626
1602 lock_mount_hash();
1603 event++; 1627 event++;
1604 while (!hlist_empty(&mp->m_list)) { 1628 while (!hlist_empty(&mp->m_list)) {
1605 mnt = hlist_entry(mp->m_list.first, struct mount, mnt_mp_list); 1629 mnt = hlist_entry(mp->m_list.first, struct mount, mnt_mp_list);
@@ -1609,9 +1633,9 @@ void __detach_mounts(struct dentry *dentry)
1609 } 1633 }
1610 else umount_tree(mnt, UMOUNT_CONNECTED); 1634 else umount_tree(mnt, UMOUNT_CONNECTED);
1611 } 1635 }
1612 unlock_mount_hash();
1613 put_mountpoint(mp); 1636 put_mountpoint(mp);
1614out_unlock: 1637out_unlock:
1638 unlock_mount_hash();
1615 namespace_unlock(); 1639 namespace_unlock();
1616} 1640}
1617 1641
@@ -2038,9 +2062,7 @@ retry:
2038 namespace_lock(); 2062 namespace_lock();
2039 mnt = lookup_mnt(path); 2063 mnt = lookup_mnt(path);
2040 if (likely(!mnt)) { 2064 if (likely(!mnt)) {
2041 struct mountpoint *mp = lookup_mountpoint(dentry); 2065 struct mountpoint *mp = get_mountpoint(dentry);
2042 if (!mp)
2043 mp = new_mountpoint(dentry);
2044 if (IS_ERR(mp)) { 2066 if (IS_ERR(mp)) {
2045 namespace_unlock(); 2067 namespace_unlock();
2046 inode_unlock(dentry->d_inode); 2068 inode_unlock(dentry->d_inode);
@@ -2059,7 +2081,11 @@ retry:
2059static void unlock_mount(struct mountpoint *where) 2081static void unlock_mount(struct mountpoint *where)
2060{ 2082{
2061 struct dentry *dentry = where->m_dentry; 2083 struct dentry *dentry = where->m_dentry;
2084
2085 read_seqlock_excl(&mount_lock);
2062 put_mountpoint(where); 2086 put_mountpoint(where);
2087 read_sequnlock_excl(&mount_lock);
2088
2063 namespace_unlock(); 2089 namespace_unlock();
2064 inode_unlock(dentry->d_inode); 2090 inode_unlock(dentry->d_inode);
2065} 2091}
@@ -3135,9 +3161,9 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root,
3135 touch_mnt_namespace(current->nsproxy->mnt_ns); 3161 touch_mnt_namespace(current->nsproxy->mnt_ns);
3136 /* A moved mount should not expire automatically */ 3162 /* A moved mount should not expire automatically */
3137 list_del_init(&new_mnt->mnt_expire); 3163 list_del_init(&new_mnt->mnt_expire);
3164 put_mountpoint(root_mp);
3138 unlock_mount_hash(); 3165 unlock_mount_hash();
3139 chroot_fs_refs(&root, &new); 3166 chroot_fs_refs(&root, &new);
3140 put_mountpoint(root_mp);
3141 error = 0; 3167 error = 0;
3142out4: 3168out4:
3143 unlock_mount(old_mp); 3169 unlock_mount(old_mp);
diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c
index 55313d994895..d4e37acd4821 100644
--- a/fs/proc/proc_sysctl.c
+++ b/fs/proc/proc_sysctl.c
@@ -709,7 +709,7 @@ static int proc_sys_readdir(struct file *file, struct dir_context *ctx)
709 ctl_dir = container_of(head, struct ctl_dir, header); 709 ctl_dir = container_of(head, struct ctl_dir, header);
710 710
711 if (!dir_emit_dots(file, ctx)) 711 if (!dir_emit_dots(file, ctx))
712 return 0; 712 goto out;
713 713
714 pos = 2; 714 pos = 2;
715 715
@@ -719,6 +719,7 @@ static int proc_sys_readdir(struct file *file, struct dir_context *ctx)
719 break; 719 break;
720 } 720 }
721 } 721 }
722out:
722 sysctl_head_finish(head); 723 sysctl_head_finish(head);
723 return 0; 724 return 0;
724} 725}
diff --git a/kernel/pid_namespace.c b/kernel/pid_namespace.c
index df9e8e9e0be7..eef2ce968636 100644
--- a/kernel/pid_namespace.c
+++ b/kernel/pid_namespace.c
@@ -151,8 +151,12 @@ out:
151 151
152static void delayed_free_pidns(struct rcu_head *p) 152static void delayed_free_pidns(struct rcu_head *p)
153{ 153{
154 kmem_cache_free(pid_ns_cachep, 154 struct pid_namespace *ns = container_of(p, struct pid_namespace, rcu);
155 container_of(p, struct pid_namespace, rcu)); 155
156 dec_pid_namespaces(ns->ucounts);
157 put_user_ns(ns->user_ns);
158
159 kmem_cache_free(pid_ns_cachep, ns);
156} 160}
157 161
158static void destroy_pid_namespace(struct pid_namespace *ns) 162static void destroy_pid_namespace(struct pid_namespace *ns)
@@ -162,8 +166,6 @@ static void destroy_pid_namespace(struct pid_namespace *ns)
162 ns_free_inum(&ns->ns); 166 ns_free_inum(&ns->ns);
163 for (i = 0; i < PIDMAP_ENTRIES; i++) 167 for (i = 0; i < PIDMAP_ENTRIES; i++)
164 kfree(ns->pidmap[i].page); 168 kfree(ns->pidmap[i].page);
165 dec_pid_namespaces(ns->ucounts);
166 put_user_ns(ns->user_ns);
167 call_rcu(&ns->rcu, delayed_free_pidns); 169 call_rcu(&ns->rcu, delayed_free_pidns);
168} 170}
169 171