aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeilBrown <neilb@suse.com>2017-11-09 23:45:41 -0500
committerAl Viro <viro@zeniv.linux.org.uk>2017-12-28 14:12:09 -0500
commit61647823aa920e395afcce4b57c32afb51456cab (patch)
treec8bbc4cfa1a8e95441de334be35bdf63d2bf0e52
parentf1ee616214cb22410e939d963bbb2349c2570f02 (diff)
VFS: close race between getcwd() and d_move()
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>
-rw-r--r--fs/dcache.c23
1 files changed, 16 insertions, 7 deletions
diff --git a/fs/dcache.c b/fs/dcache.c
index 17e6b84b9656..cab211e13744 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -467,9 +467,11 @@ static void dentry_lru_add(struct dentry *dentry)
467 * d_drop() is used mainly for stuff that wants to invalidate a dentry for some 467 * d_drop() is used mainly for stuff that wants to invalidate a dentry for some
468 * reason (NFS timeouts or autofs deletes). 468 * reason (NFS timeouts or autofs deletes).
469 * 469 *
470 * __d_drop requires dentry->d_lock. 470 * __d_drop requires dentry->d_lock
471 * ___d_drop doesn't mark dentry as "unhashed"
472 * (dentry->d_hash.pprev will be LIST_POISON2, not NULL).
471 */ 473 */
472void __d_drop(struct dentry *dentry) 474static void ___d_drop(struct dentry *dentry)
473{ 475{
474 if (!d_unhashed(dentry)) { 476 if (!d_unhashed(dentry)) {
475 struct hlist_bl_head *b; 477 struct hlist_bl_head *b;
@@ -485,12 +487,17 @@ void __d_drop(struct dentry *dentry)
485 487
486 hlist_bl_lock(b); 488 hlist_bl_lock(b);
487 __hlist_bl_del(&dentry->d_hash); 489 __hlist_bl_del(&dentry->d_hash);
488 dentry->d_hash.pprev = NULL;
489 hlist_bl_unlock(b); 490 hlist_bl_unlock(b);
490 /* After this call, in-progress rcu-walk path lookup will fail. */ 491 /* After this call, in-progress rcu-walk path lookup will fail. */
491 write_seqcount_invalidate(&dentry->d_seq); 492 write_seqcount_invalidate(&dentry->d_seq);
492 } 493 }
493} 494}
495
496void __d_drop(struct dentry *dentry)
497{
498 ___d_drop(dentry);
499 dentry->d_hash.pprev = NULL;
500}
494EXPORT_SYMBOL(__d_drop); 501EXPORT_SYMBOL(__d_drop);
495 502
496void d_drop(struct dentry *dentry) 503void d_drop(struct dentry *dentry)
@@ -2382,7 +2389,7 @@ EXPORT_SYMBOL(d_delete);
2382static void __d_rehash(struct dentry *entry) 2389static void __d_rehash(struct dentry *entry)
2383{ 2390{
2384 struct hlist_bl_head *b = d_hash(entry->d_name.hash); 2391 struct hlist_bl_head *b = d_hash(entry->d_name.hash);
2385 BUG_ON(!d_unhashed(entry)); 2392
2386 hlist_bl_lock(b); 2393 hlist_bl_lock(b);
2387 hlist_bl_add_head_rcu(&entry->d_hash, b); 2394 hlist_bl_add_head_rcu(&entry->d_hash, b);
2388 hlist_bl_unlock(b); 2395 hlist_bl_unlock(b);
@@ -2817,9 +2824,9 @@ static void __d_move(struct dentry *dentry, struct dentry *target,
2817 write_seqcount_begin_nested(&target->d_seq, DENTRY_D_LOCK_NESTED); 2824 write_seqcount_begin_nested(&target->d_seq, DENTRY_D_LOCK_NESTED);
2818 2825
2819 /* unhash both */ 2826 /* unhash both */
2820 /* __d_drop does write_seqcount_barrier, but they're OK to nest. */ 2827 /* ___d_drop does write_seqcount_barrier, but they're OK to nest. */
2821 __d_drop(dentry); 2828 ___d_drop(dentry);
2822 __d_drop(target); 2829 ___d_drop(target);
2823 2830
2824 /* Switch the names.. */ 2831 /* Switch the names.. */
2825 if (exchange) 2832 if (exchange)
@@ -2831,6 +2838,8 @@ static void __d_move(struct dentry *dentry, struct dentry *target,
2831 __d_rehash(dentry); 2838 __d_rehash(dentry);
2832 if (exchange) 2839 if (exchange)
2833 __d_rehash(target); 2840 __d_rehash(target);
2841 else
2842 target->d_hash.pprev = NULL;
2834 2843
2835 /* ... and switch them in the tree */ 2844 /* ... and switch them in the tree */
2836 if (IS_ROOT(dentry)) { 2845 if (IS_ROOT(dentry)) {