aboutsummaryrefslogtreecommitdiffstats
path: root/fs/namei.c
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2015-05-07 19:24:57 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2015-05-09 00:12:35 -0400
commit766c4cbfacd8634d7580bac6a1b8456e63de3e84 (patch)
tree0bdde729de49ac95858f5429b40419b643fcc1cb /fs/namei.c
parent5ebe6afaf0057ac3eaeb98defd5456894b446d22 (diff)
namei: d_is_negative() should be checked before ->d_seq validation
Fetching ->d_inode, verifying ->d_seq and finding d_is_negative() to be true does *not* mean that inode we'd fetched had been NULL - that holds only while ->d_seq is still unchanged. Shift d_is_negative() checks into lookup_fast() prior to ->d_seq verification. Reported-by: Steven Rostedt <rostedt@goodmis.org> Tested-by: Steven Rostedt <rostedt@goodmis.org> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/namei.c')
-rw-r--r--fs/namei.c19
1 files changed, 13 insertions, 6 deletions
diff --git a/fs/namei.c b/fs/namei.c
index 4a8d998b7274..f67cf6cef986 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -1415,6 +1415,7 @@ static int lookup_fast(struct nameidata *nd,
1415 */ 1415 */
1416 if (nd->flags & LOOKUP_RCU) { 1416 if (nd->flags & LOOKUP_RCU) {
1417 unsigned seq; 1417 unsigned seq;
1418 bool negative;
1418 dentry = __d_lookup_rcu(parent, &nd->last, &seq); 1419 dentry = __d_lookup_rcu(parent, &nd->last, &seq);
1419 if (!dentry) 1420 if (!dentry)
1420 goto unlazy; 1421 goto unlazy;
@@ -1424,8 +1425,11 @@ static int lookup_fast(struct nameidata *nd,
1424 * the dentry name information from lookup. 1425 * the dentry name information from lookup.
1425 */ 1426 */
1426 *inode = dentry->d_inode; 1427 *inode = dentry->d_inode;
1428 negative = d_is_negative(dentry);
1427 if (read_seqcount_retry(&dentry->d_seq, seq)) 1429 if (read_seqcount_retry(&dentry->d_seq, seq))
1428 return -ECHILD; 1430 return -ECHILD;
1431 if (negative)
1432 return -ENOENT;
1429 1433
1430 /* 1434 /*
1431 * This sequence count validates that the parent had no 1435 * This sequence count validates that the parent had no
@@ -1472,6 +1476,10 @@ unlazy:
1472 goto need_lookup; 1476 goto need_lookup;
1473 } 1477 }
1474 1478
1479 if (unlikely(d_is_negative(dentry))) {
1480 dput(dentry);
1481 return -ENOENT;
1482 }
1475 path->mnt = mnt; 1483 path->mnt = mnt;
1476 path->dentry = dentry; 1484 path->dentry = dentry;
1477 err = follow_managed(path, nd->flags); 1485 err = follow_managed(path, nd->flags);
@@ -1583,10 +1591,10 @@ static inline int walk_component(struct nameidata *nd, struct path *path,
1583 goto out_err; 1591 goto out_err;
1584 1592
1585 inode = path->dentry->d_inode; 1593 inode = path->dentry->d_inode;
1594 err = -ENOENT;
1595 if (d_is_negative(path->dentry))
1596 goto out_path_put;
1586 } 1597 }
1587 err = -ENOENT;
1588 if (d_is_negative(path->dentry))
1589 goto out_path_put;
1590 1598
1591 if (should_follow_link(path->dentry, follow)) { 1599 if (should_follow_link(path->dentry, follow)) {
1592 if (nd->flags & LOOKUP_RCU) { 1600 if (nd->flags & LOOKUP_RCU) {
@@ -3036,14 +3044,13 @@ retry_lookup:
3036 3044
3037 BUG_ON(nd->flags & LOOKUP_RCU); 3045 BUG_ON(nd->flags & LOOKUP_RCU);
3038 inode = path->dentry->d_inode; 3046 inode = path->dentry->d_inode;
3039finish_lookup:
3040 /* we _can_ be in RCU mode here */
3041 error = -ENOENT; 3047 error = -ENOENT;
3042 if (d_is_negative(path->dentry)) { 3048 if (d_is_negative(path->dentry)) {
3043 path_to_nameidata(path, nd); 3049 path_to_nameidata(path, nd);
3044 goto out; 3050 goto out;
3045 } 3051 }
3046 3052finish_lookup:
3053 /* we _can_ be in RCU mode here */
3047 if (should_follow_link(path->dentry, !symlink_ok)) { 3054 if (should_follow_link(path->dentry, !symlink_ok)) {
3048 if (nd->flags & LOOKUP_RCU) { 3055 if (nd->flags & LOOKUP_RCU) {
3049 if (unlikely(nd->path.mnt != path->mnt || 3056 if (unlikely(nd->path.mnt != path->mnt ||