From: Al Viro <viro@ZenIV.linux.org.uk>
To: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: Krister Johansen <kjlx@templeofstupid.com>,
linux-fsdevel@vger.kernel.org
Subject: Re: [PATCH] Fix a race in put_mountpoint.
Date: Tue, 3 Jan 2017 04:00:52 +0000 [thread overview]
Message-ID: <20170103040052.GB1555@ZenIV.linux.org.uk> (raw)
In-Reply-To: <87ful07ryd.fsf@xmission.com>
On Tue, Jan 03, 2017 at 04:17:14PM +1300, Eric W. Biederman wrote:
> Al Viro <viro@ZenIV.linux.org.uk> writes:
>
> > On Tue, Jan 03, 2017 at 01:51:36PM +1300, Eric W. Biederman wrote:
> >
> >> The only significant thing I see is that you have not taken the
> >> mount_lock on the path where new_mountpoint adds the new struct
> >> mountpoint into the mountpoint hash table.
> >
> > Umm... Point, but I really don't like that bouncing mount_lock up
> > and down there. It's not going to cause any serious overhead,
> > but it just looks ugly... ;-/
> >
> > Let me think for a while...
>
> The other possibility is to grab namespace_sem in mntput_no_expire
> around the call of umount_mnt. That is the only path where
> put_mountpoint can be called where we are not holding namespace_sem.
> That works in the small but I haven't traced the callers of mntput and
> mntput_no_expire yet to see if it works in practice.
No, that's a really bad idea. Final mntput should _not_ happen under
namespace_lock, but I don't want grabbing it in that place.
How about this instead:
diff --git a/fs/namespace.c b/fs/namespace.c
index b5b1259e064f..20fc797277f8 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -742,29 +742,6 @@ static struct mountpoint *lookup_mountpoint(struct dentry *dentry)
return NULL;
}
-static struct mountpoint *new_mountpoint(struct dentry *dentry)
-{
- struct hlist_head *chain = mp_hash(dentry);
- struct mountpoint *mp;
- int ret;
-
- mp = kmalloc(sizeof(struct mountpoint), GFP_KERNEL);
- if (!mp)
- return ERR_PTR(-ENOMEM);
-
- ret = d_set_mounted(dentry);
- if (ret) {
- kfree(mp);
- return ERR_PTR(ret);
- }
-
- mp->m_dentry = dentry;
- mp->m_count = 1;
- hlist_add_head(&mp->m_hash, chain);
- INIT_HLIST_HEAD(&mp->m_list);
- return mp;
-}
-
static void put_mountpoint(struct mountpoint *mp)
{
if (!--mp->m_count) {
@@ -1595,11 +1572,11 @@ void __detach_mounts(struct dentry *dentry)
struct mount *mnt;
namespace_lock();
+ lock_mount_hash();
mp = lookup_mountpoint(dentry);
if (IS_ERR_OR_NULL(mp))
goto out_unlock;
- lock_mount_hash();
event++;
while (!hlist_empty(&mp->m_list)) {
mnt = hlist_entry(mp->m_list.first, struct mount, mnt_mp_list);
@@ -1609,9 +1586,9 @@ void __detach_mounts(struct dentry *dentry)
}
else umount_tree(mnt, UMOUNT_CONNECTED);
}
- unlock_mount_hash();
put_mountpoint(mp);
out_unlock:
+ unlock_mount_hash();
namespace_unlock();
}
@@ -2027,8 +2004,11 @@ static int attach_recursive_mnt(struct mount *source_mnt,
static struct mountpoint *lock_mount(struct path *path)
{
+ struct mountpoint *mp = NULL;
struct vfsmount *mnt;
struct dentry *dentry = path->dentry;
+ int err;
+
retry:
inode_lock(dentry->d_inode);
if (unlikely(cant_mount(dentry))) {
@@ -2037,29 +2017,60 @@ static struct mountpoint *lock_mount(struct path *path)
}
namespace_lock();
mnt = lookup_mnt(path);
- if (likely(!mnt)) {
- struct mountpoint *mp = lookup_mountpoint(dentry);
- if (!mp)
- mp = new_mountpoint(dentry);
- if (IS_ERR(mp)) {
+ if (unlikely(mnt)) {
+ namespace_unlock();
+ inode_unlock(path->dentry->d_inode);
+ path_put(path);
+ path->mnt = mnt;
+ dentry = path->dentry = dget(mnt->mnt_root);
+ goto retry;
+ }
+
+ /*
+ * OK, we have namespace_lock held, nothing is overmounting
+ * *path and inode of mountpoint to be is locked.
+ */
+ if (likely(!d_mountpoint(dentry)))
+ mp = kmalloc(sizeof(struct mountpoint), GFP_KERNEL);
+ read_seqlock_excl(&mount_lock);
+ if (!mp && !d_mountpoint(dentry)) {
+ read_sequnlock_excl(&mount_lock);
+ mp = kmalloc(sizeof(struct mountpoint), GFP_KERNEL);
+ read_seqlock_excl(&mount_lock);
+ }
+ if (d_mountpoint(dentry)) {
+ kfree(mp);
+ mp = lookup_mountpoint(dentry);
+ } else {
+ if (unlikely(!mp)) {
+ read_sequnlock_excl(&mount_lock);
namespace_unlock();
inode_unlock(dentry->d_inode);
- return mp;
+ return ERR_PTR(-ENOMEM);
}
- return mp;
+ err = d_set_mounted(dentry);
+ if (unlikely(err)) {
+ kfree(mp);
+ read_sequnlock_excl(&mount_lock);
+ namespace_unlock();
+ inode_unlock(dentry->d_inode);
+ return ERR_PTR(err);
+ }
+ mp->m_dentry = dentry;
+ mp->m_count = 1;
+ hlist_add_head(&mp->m_hash, mp_hash(dentry));
+ INIT_HLIST_HEAD(&mp->m_list);
}
- namespace_unlock();
- inode_unlock(path->dentry->d_inode);
- path_put(path);
- path->mnt = mnt;
- dentry = path->dentry = dget(mnt->mnt_root);
- goto retry;
+ read_sequnlock_excl(&mount_lock);
+ return mp;
}
static void unlock_mount(struct mountpoint *where)
{
struct dentry *dentry = where->m_dentry;
+ read_seqlock_excl(&mount_lock);
put_mountpoint(where);
+ read_sequnlock_excl(&mount_lock);
namespace_unlock();
inode_unlock(dentry->d_inode);
}
@@ -3137,7 +3148,9 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root,
list_del_init(&new_mnt->mnt_expire);
unlock_mount_hash();
chroot_fs_refs(&root, &new);
+ read_seqlock_excl(&mount_lock);
put_mountpoint(root_mp);
+ read_sequnlock_excl(&mount_lock);
error = 0;
out4:
unlock_mount(old_mp);
next prev parent reply other threads:[~2017-01-03 4:00 UTC|newest]
Thread overview: 63+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-12-31 4:10 [PATCH] Fix a race in put_mountpoint Krister Johansen
2016-12-31 6:17 ` Al Viro
2017-01-03 0:51 ` Eric W. Biederman
2017-01-03 1:48 ` Al Viro
2017-01-03 3:17 ` Eric W. Biederman
2017-01-03 4:00 ` Al Viro [this message]
2017-01-04 3:52 ` Eric W. Biederman
2017-01-04 3:53 ` [PATCH] mnt: Protect the mountpoint hashtable with mount_lock Eric W. Biederman
2017-01-04 21:04 ` [REVIEW][PATCH] mnt: Tuck mounts under others instead of creating shadow/side mounts Eric W. Biederman
2017-01-07 5:06 ` Al Viro
2017-01-11 0:10 ` Eric W. Biederman
2017-01-11 4:11 ` Al Viro
2017-01-11 16:03 ` Eric W. Biederman
2017-01-11 16:18 ` [REVIEW][PATCH 1/2] mnt: Fix propagate_mount_busy to notice all cases of busy mounts Eric W. Biederman
2017-01-11 16:19 ` [REVIEW][PATCH 2/2] mnt: Tuck mounts under others instead of creating shadow/side mounts Eric W. Biederman
2017-01-12 5:45 ` Al Viro
2017-01-20 7:20 ` Eric W. Biederman
2017-01-20 7:26 ` [PATCH v5] " Eric W. Biederman
2017-01-21 3:58 ` Ram Pai
2017-01-21 4:15 ` Eric W. Biederman
2017-01-23 19:02 ` Ram Pai
2017-01-24 0:16 ` Eric W. Biederman
2017-02-03 10:54 ` Eric W. Biederman
2017-02-03 17:10 ` Ram Pai
2017-02-03 18:26 ` Eric W. Biederman
2017-02-03 20:28 ` Ram Pai
2017-02-03 20:58 ` Eric W. Biederman
2017-02-06 3:25 ` Andrei Vagin
2017-02-06 21:40 ` Ram Pai
2017-02-07 6:35 ` Andrei Vagin
2017-01-12 5:30 ` [REVIEW][PATCH 1/2] mnt: Fix propagate_mount_busy to notice all cases of busy mounts Al Viro
2017-01-20 7:18 ` Eric W. Biederman
2017-01-13 20:32 ` Andrei Vagin
2017-01-18 19:20 ` Andrei Vagin
2017-01-20 23:18 ` Ram Pai
2017-01-23 8:15 ` Eric W. Biederman
2017-01-23 17:04 ` Ram Pai
2017-01-12 5:03 ` [REVIEW][PATCH] mnt: Tuck mounts under others instead of creating shadow/side mounts Al Viro
2017-05-14 2:15 ` Andrei Vagin
2017-05-14 4:05 ` Eric W. Biederman
2017-05-14 9:26 ` Eric W. Biederman
2017-05-15 18:27 ` Andrei Vagin
2017-05-15 19:42 ` Eric W. Biederman
2017-05-15 20:10 ` [REVIEW][PATCH] mnt: In umount propagation reparent in a separate pass Eric W. Biederman
2017-05-15 23:12 ` Andrei Vagin
2017-05-16 5:42 ` [PATCH] test: check a case when a mount is propagated between exiting mounts Andrei Vagin
2017-05-17 5:54 ` [REVIEW][PATCH 1/2] mnt: In propgate_umount handle visiting mounts in any order Eric W. Biederman
2017-05-17 5:55 ` [REVIEW][PATCH 2/2] mnt: Make propagate_umount less slow for overlapping mount propagation trees Eric W. Biederman
2017-05-17 22:48 ` Andrei Vagin
2017-05-17 23:26 ` Eric W. Biederman
2017-05-18 0:51 ` Andrei Vagin
2017-05-24 20:42 ` [REVIEW][PATCH 1/2] mnt: In propgate_umount handle visiting mounts in any order Ram Pai
2017-05-24 21:54 ` Eric W. Biederman
2017-05-24 22:35 ` Ram Pai
2017-05-30 6:07 ` Ram Pai
2017-05-30 15:07 ` Eric W. Biederman
2017-06-07 9:54 ` Ram Pai
2017-06-07 13:09 ` Eric W. Biederman
2017-05-22 8:15 ` [REVIEW][PATCH] mnt: In umount propagation reparent in a separate pass Ram Pai
2017-05-22 18:33 ` Eric W. Biederman
2017-05-22 22:34 ` Ram Pai
2017-05-23 13:58 ` Eric W. Biederman
2017-01-06 7:00 ` [PATCH] mnt: Protect the mountpoint hashtable with mount_lock Krister Johansen
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20170103040052.GB1555@ZenIV.linux.org.uk \
--to=viro@zeniv.linux.org.uk \
--cc=ebiederm@xmission.com \
--cc=kjlx@templeofstupid.com \
--cc=linux-fsdevel@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.