Commit 6d09bb62 authored by Trond Myklebust's avatar Trond Myklebust Committed by Linus Torvalds

[PATCH] fs/namei.c: Call to file_permission() under a spinlock in do_lookup_path()

From: Trond Myklebust <Trond.Myklebust@netapp.com>

We're presently running lock_kernel() under fs_lock via nfs's ->permission
handler.  That's a ranking bug and sometimes a sleep-in-spinlock bug.  This
problem was introduced in the openat() patchset.

We should not need to hold the current->fs->lock for a codepath that doesn't
use current->fs.

[vsu@altlinux.ru: fix error path]
Signed-off-by: default avatarTrond Myklebust <Trond.Myklebust@netapp.com>
Cc: Al Viro <viro@ftp.linux.org.uk>
Signed-off-by: default avatarSergey Vlasov <vsu@altlinux.ru>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent c7d2d28b
...@@ -1080,8 +1080,8 @@ static int fastcall do_path_lookup(int dfd, const char *name, ...@@ -1080,8 +1080,8 @@ static int fastcall do_path_lookup(int dfd, const char *name,
nd->flags = flags; nd->flags = flags;
nd->depth = 0; nd->depth = 0;
read_lock(&current->fs->lock);
if (*name=='/') { if (*name=='/') {
read_lock(&current->fs->lock);
if (current->fs->altroot && !(nd->flags & LOOKUP_NOALT)) { if (current->fs->altroot && !(nd->flags & LOOKUP_NOALT)) {
nd->mnt = mntget(current->fs->altrootmnt); nd->mnt = mntget(current->fs->altrootmnt);
nd->dentry = dget(current->fs->altroot); nd->dentry = dget(current->fs->altroot);
...@@ -1092,33 +1092,35 @@ static int fastcall do_path_lookup(int dfd, const char *name, ...@@ -1092,33 +1092,35 @@ static int fastcall do_path_lookup(int dfd, const char *name,
} }
nd->mnt = mntget(current->fs->rootmnt); nd->mnt = mntget(current->fs->rootmnt);
nd->dentry = dget(current->fs->root); nd->dentry = dget(current->fs->root);
read_unlock(&current->fs->lock);
} else if (dfd == AT_FDCWD) { } else if (dfd == AT_FDCWD) {
read_lock(&current->fs->lock);
nd->mnt = mntget(current->fs->pwdmnt); nd->mnt = mntget(current->fs->pwdmnt);
nd->dentry = dget(current->fs->pwd); nd->dentry = dget(current->fs->pwd);
read_unlock(&current->fs->lock);
} else { } else {
struct dentry *dentry; struct dentry *dentry;
file = fget_light(dfd, &fput_needed); file = fget_light(dfd, &fput_needed);
retval = -EBADF; retval = -EBADF;
if (!file) if (!file)
goto unlock_fail; goto out_fail;
dentry = file->f_dentry; dentry = file->f_dentry;
retval = -ENOTDIR; retval = -ENOTDIR;
if (!S_ISDIR(dentry->d_inode->i_mode)) if (!S_ISDIR(dentry->d_inode->i_mode))
goto fput_unlock_fail; goto fput_fail;
retval = file_permission(file, MAY_EXEC); retval = file_permission(file, MAY_EXEC);
if (retval) if (retval)
goto fput_unlock_fail; goto fput_fail;
nd->mnt = mntget(file->f_vfsmnt); nd->mnt = mntget(file->f_vfsmnt);
nd->dentry = dget(dentry); nd->dentry = dget(dentry);
fput_light(file, fput_needed); fput_light(file, fput_needed);
} }
read_unlock(&current->fs->lock);
current->total_link_count = 0; current->total_link_count = 0;
retval = link_path_walk(name, nd); retval = link_path_walk(name, nd);
out: out:
...@@ -1127,13 +1129,12 @@ static int fastcall do_path_lookup(int dfd, const char *name, ...@@ -1127,13 +1129,12 @@ static int fastcall do_path_lookup(int dfd, const char *name,
nd->dentry->d_inode)) nd->dentry->d_inode))
audit_inode(name, nd->dentry->d_inode, flags); audit_inode(name, nd->dentry->d_inode, flags);
} }
out_fail:
return retval; return retval;
fput_unlock_fail: fput_fail:
fput_light(file, fput_needed); fput_light(file, fput_needed);
unlock_fail: goto out_fail;
read_unlock(&current->fs->lock);
return retval;
} }
int fastcall path_lookup(const char *name, unsigned int flags, int fastcall path_lookup(const char *name, unsigned int flags,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment