Home Home > GIT Browse
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNeilBrown <neilb@suse.com>2018-11-01 13:59:09 +1100
committerNeilBrown <neilb@suse.com>2018-11-01 13:59:09 +1100
commit76815af2b903dcd25688fd980a52c5384c273d3d (patch)
tree0809b5245748f842324bd173bf677e8740951ae1
parente8660e8f13373ea96d2c00d626c6f1471fba1ecc (diff)
- VFS: close race between getcwd() and d_move() (git-fixes).
- Refresh patches.fixes/d-lookup-fairness.fix.
-rw-r--r--patches.fixes/VFS-close-race-between-getcwd-and-d_move.patch111
-rw-r--r--patches.fixes/d-lookup-fairness.fix5
-rw-r--r--series.conf1
3 files changed, 114 insertions, 3 deletions
diff --git a/patches.fixes/VFS-close-race-between-getcwd-and-d_move.patch b/patches.fixes/VFS-close-race-between-getcwd-and-d_move.patch
new file mode 100644
index 0000000000..ebda5682f4
--- /dev/null
+++ b/patches.fixes/VFS-close-race-between-getcwd-and-d_move.patch
@@ -0,0 +1,111 @@
+From: NeilBrown <neilb@suse.com>
+Date: Fri, 10 Nov 2017 15:45:41 +1100
+Subject: [PATCH] VFS: close race between getcwd() and d_move()
+Git-commit: 61647823aa920e395afcce4b57c32afb51456cab
+Patch-mainline: v4.16
+References: git-fixes
+
+d_move() will call __d_drop() and then __d_rehash()
+on the dentry being moved. This creates a small window
+when the dentry appears to be unhashed. Many tests
+of d_unhashed() are made under ->d_lock and so are safe
+from racing with this window, but some aren't.
+In particular, getcwd() calls d_unlinked() (which calls
+d_unhashed()) without d_lock protection, so it can race.
+
+This races has been seen in practice with lustre, which uses d_move() as
+part of name lookup. See:
+ https://jira.hpdd.intel.com/browse/LU-9735
+It could race with a regular rename(), and result in ENOENT instead
+of either the 'before' or 'after' name.
+
+The race can be demonstrated with a simple program which
+has two threads, one renaming a directory back and forth
+while another calls getcwd() within that directory: it should never
+fail, but does. See:
+ https://patchwork.kernel.org/patch/9455345/
+
+We could fix this race by taking d_lock and rechecking when
+d_unhashed() reports true. Alternately when can remove the window,
+which is the approach this patch takes.
+
+___d_drop() is introduce which does *not* clear d_hash.pprev
+so the dentry still appears to be hashed. __d_drop() calls
+___d_drop(), then clears d_hash.pprev.
+__d_move() now uses ___d_drop() and only clears d_hash.pprev
+when not rehashing.
+
+Signed-off-by: NeilBrown <neilb@suse.com>
+Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
+Acked-by: NeilBrown <neilb@suse.com>
+
+---
+ fs/dcache.c | 23 ++++++++++++++++-------
+ 1 file changed, 16 insertions(+), 7 deletions(-)
+
+--- a/fs/dcache.c
++++ b/fs/dcache.c
+@@ -463,9 +463,11 @@ static void dentry_lru_add(struct dentry
+ * d_drop() is used mainly for stuff that wants to invalidate a dentry for some
+ * reason (NFS timeouts or autofs deletes).
+ *
+- * __d_drop requires dentry->d_lock.
++ * __d_drop requires dentry->d_lock
++ * ___d_drop doesn't mark dentry as "unhashed"
++ * (dentry->d_hash.pprev will be LIST_POISON2, not NULL).
+ */
+-void __d_drop(struct dentry *dentry)
++static void ___d_drop(struct dentry *dentry)
+ {
+ if (!d_unhashed(dentry)) {
+ struct hlist_bl_head *b;
+@@ -481,12 +483,17 @@ void __d_drop(struct dentry *dentry)
+
+ hlist_bl_lock(b);
+ __hlist_bl_del(&dentry->d_hash);
+- dentry->d_hash.pprev = NULL;
+ hlist_bl_unlock(b);
+ /* After this call, in-progress rcu-walk path lookup will fail. */
+ write_seqcount_invalidate(&dentry->d_seq);
+ }
+ }
++
++void __d_drop(struct dentry *dentry)
++{
++ ___d_drop(dentry);
++ dentry->d_hash.pprev = NULL;
++}
+ EXPORT_SYMBOL(__d_drop);
+
+ void d_drop(struct dentry *dentry)
+@@ -2377,7 +2384,7 @@ EXPORT_SYMBOL(d_delete);
+ static void __d_rehash(struct dentry *entry)
+ {
+ struct hlist_bl_head *b = d_hash(entry->d_name.hash);
+- BUG_ON(!d_unhashed(entry));
++
+ hlist_bl_lock(b);
+ hlist_bl_add_head_rcu(&entry->d_hash, b);
+ hlist_bl_unlock(b);
+@@ -2814,9 +2821,9 @@ static void __d_move(struct dentry *dent
+ write_seqcount_begin_nested(&target->d_seq, DENTRY_D_LOCK_NESTED);
+
+ /* unhash both */
+- /* __d_drop does write_seqcount_barrier, but they're OK to nest. */
+- __d_drop(dentry);
+- __d_drop(target);
++ /* ___d_drop does write_seqcount_barrier, but they're OK to nest. */
++ ___d_drop(dentry);
++ ___d_drop(target);
+
+ /* Switch the names.. */
+ if (exchange)
+@@ -2828,6 +2835,8 @@ static void __d_move(struct dentry *dent
+ __d_rehash(dentry);
+ if (exchange)
+ __d_rehash(target);
++ else
++ target->d_hash.pprev = NULL;
+
+ /* ... and switch them in the tree */
+ if (IS_ROOT(dentry)) {
diff --git a/patches.fixes/d-lookup-fairness.fix b/patches.fixes/d-lookup-fairness.fix
index 7324a19541..72d4f15408 100644
--- a/patches.fixes/d-lookup-fairness.fix
+++ b/patches.fixes/d-lookup-fairness.fix
@@ -33,7 +33,7 @@ Signed-off-by: NeilBrown <neilb@suse.com>
static inline struct hlist_bl_head *in_lookup_hash(const struct dentry *parent,
unsigned int hash)
{
-@@ -479,10 +481,14 @@ void __d_drop(struct dentry *dentry)
+@@ -479,9 +481,13 @@ static void ___d_drop(struct dentry *den
else
b = d_hash(dentry->d_name.hash);
@@ -41,14 +41,13 @@ Signed-off-by: NeilBrown <neilb@suse.com>
+ spin_lock(&s_anon_lock);
hlist_bl_lock(b);
__hlist_bl_del(&dentry->d_hash);
- dentry->d_hash.pprev = NULL;
hlist_bl_unlock(b);
+ if (b == &dentry->d_sb->s_anon)
+ spin_unlock(&s_anon_lock);
/* After this call, in-progress rcu-walk path lookup will fail. */
write_seqcount_invalidate(&dentry->d_seq);
}
-@@ -1961,9 +1967,11 @@ static struct dentry *__d_obtain_alias(s
+@@ -2000,9 +2006,11 @@ static struct dentry *__d_obtain_alias(s
spin_lock(&tmp->d_lock);
__d_set_inode_and_type(tmp, inode, add_flags);
hlist_add_head(&tmp->d_u.d_alias, &inode->i_dentry);
diff --git a/series.conf b/series.conf
index 2ba743110e..f5ab31bf6c 100644
--- a/series.conf
+++ b/series.conf
@@ -12260,6 +12260,7 @@
patches.suse/mm-pin-address_space-before-dereferencing-it-while-isolating-an-LRU-page.patch
patches.fixes/mm-fadvise-discard-partial-page-if-endbyte-is-also-E.patch
patches.suse/mm-numa-do-not-trap-faults-on-shared-data-section-pages.patch
+ patches.fixes/VFS-close-race-between-getcwd-and-d_move.patch
patches.fixes/errseq-Add-to-documentation-tree.patch
patches.arch/s390-fix-handling-of-1-in-set-fs-id16-syscalls.patch
patches.fixes/0001-typec-tcpm-fusb302-Resolve-out-of-order-messaging-ev.patch