diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2015-05-07 19:24:57 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2015-05-09 00:12:35 -0400 |
commit | 766c4cbfacd8634d7580bac6a1b8456e63de3e84 (patch) | |
tree | 0bdde729de49ac95858f5429b40419b643fcc1cb /fs/namei.c | |
parent | 5ebe6afaf0057ac3eaeb98defd5456894b446d22 (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.c | 19 |
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; |
3039 | finish_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 | 3052 | finish_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 || |