aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorEric W. Biederman <ebiederm@xmission.com>2017-01-02 20:18:43 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-01-19 14:18:03 -0500
commit1a62a0f76556f39d6d67789bb981b28230aaa338 (patch)
tree583149c0b9d7fc7f434a26e1bccbdaf2a12c5e90 /fs
parent52fd0ab07676b295abc54eeb1cef7a06d49c9847 (diff)
mnt: Protect the mountpoint hashtable with mount_lock
commit 3895dbf8985f656675b5bde610723a29cbce3fa7 upstream. Protecting the mountpoint hashtable with namespace_sem was sufficient until a call to umount_mnt was added to mntput_no_expire. At which point it became possible for multiple calls of put_mountpoint on the same hash chain to happen on the same time. Kristen Johansen <kjlx@templeofstupid.com> reported: > This can cause a panic when simultaneous callers of put_mountpoint > attempt to free the same mountpoint. This occurs because some callers > hold the mount_hash_lock, while others hold the namespace lock. Some > even hold both. > > In this submitter's case, the panic manifested itself as a GP fault in > put_mountpoint() when it called hlist_del() and attempted to dereference > a m_hash.pprev that had been poisioned by another thread. Al Viro observed that the simple fix is to switch from using the namespace_sem to the mount_lock to protect the mountpoint hash table. I have taken Al's suggested patch moved put_mountpoint in pivot_root (instead of taking mount_lock an additional time), and have replaced new_mountpoint with get_mountpoint a function that does the hash table lookup and addition under the mount_lock. The introduction of get_mounptoint ensures that only the mount_lock is needed to manipulate the mountpoint hashtable. d_set_mounted is modified to only set DCACHE_MOUNTED if it is not already set. This allows get_mountpoint to use the setting of DCACHE_MOUNTED to ensure adding a struct mountpoint for a dentry happens exactly once. Fixes: ce07d891a089 ("mnt: Honor MNT_LOCKED when detaching mounts") Reported-by: Krister Johansen <kjlx@templeofstupid.com> Suggested-by: Al Viro <viro@ZenIV.linux.org.uk> Acked-by: Al Viro <viro@ZenIV.linux.org.uk> Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'fs')
-rw-r--r--fs/dcache.c7
-rw-r--r--fs/namespace.c64
2 files changed, 50 insertions, 21 deletions
diff --git a/fs/dcache.c b/fs/dcache.c
index 5c7cc953ac81..4485a48f4091 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -1330,8 +1330,11 @@ int d_set_mounted(struct dentry *dentry)
1330 } 1330 }
1331 spin_lock(&dentry->d_lock); 1331 spin_lock(&dentry->d_lock);
1332 if (!d_unlinked(dentry)) { 1332 if (!d_unlinked(dentry)) {
1333 dentry->d_flags |= DCACHE_MOUNTED; 1333 ret = -EBUSY;
1334 ret = 0; 1334 if (!d_mountpoint(dentry)) {
1335 dentry->d_flags |= DCACHE_MOUNTED;
1336 ret = 0;
1337 }
1335 } 1338 }
1336 spin_unlock(&dentry->d_lock); 1339 spin_unlock(&dentry->d_lock);
1337out: 1340out:
diff --git a/fs/namespace.c b/fs/namespace.c
index e6c234b1a645..7cea503ae06d 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -746,26 +746,50 @@ static struct mountpoint *lookup_mountpoint(struct dentry *dentry)
746 return NULL; 746 return NULL;
747} 747}
748 748
749static struct mountpoint *new_mountpoint(struct dentry *dentry) 749static struct mountpoint *get_mountpoint(struct dentry *dentry)
750{ 750{
751 struct hlist_head *chain = mp_hash(dentry); 751 struct mountpoint *mp, *new = NULL;
752 struct mountpoint *mp;
753 int ret; 752 int ret;
754 753
755 mp = kmalloc(sizeof(struct mountpoint), GFP_KERNEL); 754 if (d_mountpoint(dentry)) {
756 if (!mp) 755mountpoint:
756 read_seqlock_excl(&mount_lock);
757 mp = lookup_mountpoint(dentry);
758 read_sequnlock_excl(&mount_lock);
759 if (mp)
760 goto done;
761 }
762
763 if (!new)
764 new = kmalloc(sizeof(struct mountpoint), GFP_KERNEL);
765 if (!new)
757 return ERR_PTR(-ENOMEM); 766 return ERR_PTR(-ENOMEM);
758 767
768
769 /* Exactly one processes may set d_mounted */
759 ret = d_set_mounted(dentry); 770 ret = d_set_mounted(dentry);
760 if (ret) {
761 kfree(mp);
762 return ERR_PTR(ret);
763 }
764 771
765 mp->m_dentry = dentry; 772 /* Someone else set d_mounted? */
766 mp->m_count = 1; 773 if (ret == -EBUSY)
767 hlist_add_head(&mp->m_hash, chain); 774 goto mountpoint;
768 INIT_HLIST_HEAD(&mp->m_list); 775
776 /* The dentry is not available as a mountpoint? */
777 mp = ERR_PTR(ret);
778 if (ret)
779 goto done;
780
781 /* Add the new mountpoint to the hash table */
782 read_seqlock_excl(&mount_lock);
783 new->m_dentry = dentry;
784 new->m_count = 1;
785 hlist_add_head(&new->m_hash, mp_hash(dentry));
786 INIT_HLIST_HEAD(&new->m_list);
787 read_sequnlock_excl(&mount_lock);
788
789 mp = new;
790 new = NULL;
791done:
792 kfree(new);
769 return mp; 793 return mp;
770} 794}
771 795
@@ -1568,11 +1592,11 @@ void __detach_mounts(struct dentry *dentry)
1568 struct mount *mnt; 1592 struct mount *mnt;
1569 1593
1570 namespace_lock(); 1594 namespace_lock();
1595 lock_mount_hash();
1571 mp = lookup_mountpoint(dentry); 1596 mp = lookup_mountpoint(dentry);
1572 if (IS_ERR_OR_NULL(mp)) 1597 if (IS_ERR_OR_NULL(mp))
1573 goto out_unlock; 1598 goto out_unlock;
1574 1599
1575 lock_mount_hash();
1576 event++; 1600 event++;
1577 while (!hlist_empty(&mp->m_list)) { 1601 while (!hlist_empty(&mp->m_list)) {
1578 mnt = hlist_entry(mp->m_list.first, struct mount, mnt_mp_list); 1602 mnt = hlist_entry(mp->m_list.first, struct mount, mnt_mp_list);
@@ -1582,9 +1606,9 @@ void __detach_mounts(struct dentry *dentry)
1582 } 1606 }
1583 else umount_tree(mnt, UMOUNT_CONNECTED); 1607 else umount_tree(mnt, UMOUNT_CONNECTED);
1584 } 1608 }
1585 unlock_mount_hash();
1586 put_mountpoint(mp); 1609 put_mountpoint(mp);
1587out_unlock: 1610out_unlock:
1611 unlock_mount_hash();
1588 namespace_unlock(); 1612 namespace_unlock();
1589} 1613}
1590 1614
@@ -2013,9 +2037,7 @@ retry:
2013 namespace_lock(); 2037 namespace_lock();
2014 mnt = lookup_mnt(path); 2038 mnt = lookup_mnt(path);
2015 if (likely(!mnt)) { 2039 if (likely(!mnt)) {
2016 struct mountpoint *mp = lookup_mountpoint(dentry); 2040 struct mountpoint *mp = get_mountpoint(dentry);
2017 if (!mp)
2018 mp = new_mountpoint(dentry);
2019 if (IS_ERR(mp)) { 2041 if (IS_ERR(mp)) {
2020 namespace_unlock(); 2042 namespace_unlock();
2021 inode_unlock(dentry->d_inode); 2043 inode_unlock(dentry->d_inode);
@@ -2034,7 +2056,11 @@ retry:
2034static void unlock_mount(struct mountpoint *where) 2056static void unlock_mount(struct mountpoint *where)
2035{ 2057{
2036 struct dentry *dentry = where->m_dentry; 2058 struct dentry *dentry = where->m_dentry;
2059
2060 read_seqlock_excl(&mount_lock);
2037 put_mountpoint(where); 2061 put_mountpoint(where);
2062 read_sequnlock_excl(&mount_lock);
2063
2038 namespace_unlock(); 2064 namespace_unlock();
2039 inode_unlock(dentry->d_inode); 2065 inode_unlock(dentry->d_inode);
2040} 2066}
@@ -3110,9 +3136,9 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root,
3110 touch_mnt_namespace(current->nsproxy->mnt_ns); 3136 touch_mnt_namespace(current->nsproxy->mnt_ns);
3111 /* A moved mount should not expire automatically */ 3137 /* A moved mount should not expire automatically */
3112 list_del_init(&new_mnt->mnt_expire); 3138 list_del_init(&new_mnt->mnt_expire);
3139 put_mountpoint(root_mp);
3113 unlock_mount_hash(); 3140 unlock_mount_hash();
3114 chroot_fs_refs(&root, &new); 3141 chroot_fs_refs(&root, &new);
3115 put_mountpoint(root_mp);
3116 error = 0; 3142 error = 0;
3117out4: 3143out4:
3118 unlock_mount(old_mp); 3144 unlock_mount(old_mp);