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>
This commit is contained in:
Родитель
5ebe6afaf0
Коммит
766c4cbfac
19
fs/namei.c
19
fs/namei.c
|
@ -1415,6 +1415,7 @@ static int lookup_fast(struct nameidata *nd,
|
|||
*/
|
||||
if (nd->flags & LOOKUP_RCU) {
|
||||
unsigned seq;
|
||||
bool negative;
|
||||
dentry = __d_lookup_rcu(parent, &nd->last, &seq);
|
||||
if (!dentry)
|
||||
goto unlazy;
|
||||
|
@ -1424,8 +1425,11 @@ static int lookup_fast(struct nameidata *nd,
|
|||
* the dentry name information from lookup.
|
||||
*/
|
||||
*inode = dentry->d_inode;
|
||||
negative = d_is_negative(dentry);
|
||||
if (read_seqcount_retry(&dentry->d_seq, seq))
|
||||
return -ECHILD;
|
||||
if (negative)
|
||||
return -ENOENT;
|
||||
|
||||
/*
|
||||
* This sequence count validates that the parent had no
|
||||
|
@ -1472,6 +1476,10 @@ unlazy:
|
|||
goto need_lookup;
|
||||
}
|
||||
|
||||
if (unlikely(d_is_negative(dentry))) {
|
||||
dput(dentry);
|
||||
return -ENOENT;
|
||||
}
|
||||
path->mnt = mnt;
|
||||
path->dentry = dentry;
|
||||
err = follow_managed(path, nd->flags);
|
||||
|
@ -1583,10 +1591,10 @@ static inline int walk_component(struct nameidata *nd, struct path *path,
|
|||
goto out_err;
|
||||
|
||||
inode = path->dentry->d_inode;
|
||||
err = -ENOENT;
|
||||
if (d_is_negative(path->dentry))
|
||||
goto out_path_put;
|
||||
}
|
||||
err = -ENOENT;
|
||||
if (d_is_negative(path->dentry))
|
||||
goto out_path_put;
|
||||
|
||||
if (should_follow_link(path->dentry, follow)) {
|
||||
if (nd->flags & LOOKUP_RCU) {
|
||||
|
@ -3036,14 +3044,13 @@ retry_lookup:
|
|||
|
||||
BUG_ON(nd->flags & LOOKUP_RCU);
|
||||
inode = path->dentry->d_inode;
|
||||
finish_lookup:
|
||||
/* we _can_ be in RCU mode here */
|
||||
error = -ENOENT;
|
||||
if (d_is_negative(path->dentry)) {
|
||||
path_to_nameidata(path, nd);
|
||||
goto out;
|
||||
}
|
||||
|
||||
finish_lookup:
|
||||
/* we _can_ be in RCU mode here */
|
||||
if (should_follow_link(path->dentry, !symlink_ok)) {
|
||||
if (nd->flags & LOOKUP_RCU) {
|
||||
if (unlikely(nd->path.mnt != path->mnt ||
|
||||
|
|
Загрузка…
Ссылка в новой задаче