From ca99c1da080345e227cfb083c330a184d42e27f3 Mon Sep 17 00:00:00 2001 From: Dipankar Sarma Date: Tue, 18 Apr 2006 22:21:46 -0700 Subject: [PATCH] Fix file lookup without ref There are places in the kernel where we look up files in fd tables and access the file structure without holding refereces to the file. So, we need special care to avoid the race between looking up files in the fd table and tearing down of the file in another CPU. Otherwise, one might see a NULL f_dentry or such torn down version of the file. This patch fixes those special places where such a race may happen. Signed-off-by: Dipankar Sarma Acked-by: "Paul E. McKenney" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/tty_io.c | 8 ++++++-- fs/locks.c | 9 +++++++-- fs/proc/base.c | 21 +++++++++++++++------ 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 841f0bd3eaa..f07637a8f88 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -2723,7 +2723,11 @@ static void __do_SAK(void *arg) } task_lock(p); if (p->files) { - rcu_read_lock(); + /* + * We don't take a ref to the file, so we must + * hold ->file_lock instead. + */ + spin_lock(&p->files->file_lock); fdt = files_fdtable(p->files); for (i=0; i < fdt->max_fds; i++) { filp = fcheck_files(p->files, i); @@ -2738,7 +2742,7 @@ static void __do_SAK(void *arg) break; } } - rcu_read_unlock(); + spin_unlock(&p->files->file_lock); } task_unlock(p); } while_each_thread(g, p); diff --git a/fs/locks.c b/fs/locks.c index dda83d6cd48..efad798824d 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -2230,7 +2230,12 @@ void steal_locks(fl_owner_t from) lock_kernel(); j = 0; - rcu_read_lock(); + + /* + * We are not taking a ref to the file structures, so + * we need to acquire ->file_lock. + */ + spin_lock(&files->file_lock); fdt = files_fdtable(files); for (;;) { unsigned long set; @@ -2248,7 +2253,7 @@ void steal_locks(fl_owner_t from) set >>= 1; } } - rcu_read_unlock(); + spin_unlock(&files->file_lock); unlock_kernel(); } EXPORT_SYMBOL(steal_locks); diff --git a/fs/proc/base.c b/fs/proc/base.c index a3a3eecef68..6cc77dc3f3f 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -297,16 +297,20 @@ static int proc_fd_link(struct inode *inode, struct dentry **dentry, struct vfsm files = get_files_struct(task); if (files) { - rcu_read_lock(); + /* + * We are not taking a ref to the file structure, so we must + * hold ->file_lock. + */ + spin_lock(&files->file_lock); file = fcheck_files(files, fd); if (file) { *mnt = mntget(file->f_vfsmnt); *dentry = dget(file->f_dentry); - rcu_read_unlock(); + spin_unlock(&files->file_lock); put_files_struct(files); return 0; } - rcu_read_unlock(); + spin_unlock(&files->file_lock); put_files_struct(files); } return -ENOENT; @@ -1523,7 +1527,12 @@ static struct dentry *proc_lookupfd(struct inode * dir, struct dentry * dentry, if (!files) goto out_unlock; inode->i_mode = S_IFLNK; - rcu_read_lock(); + + /* + * We are not taking a ref to the file structure, so we must + * hold ->file_lock. + */ + spin_lock(&files->file_lock); file = fcheck_files(files, fd); if (!file) goto out_unlock2; @@ -1531,7 +1540,7 @@ static struct dentry *proc_lookupfd(struct inode * dir, struct dentry * dentry, inode->i_mode |= S_IRUSR | S_IXUSR; if (file->f_mode & 2) inode->i_mode |= S_IWUSR | S_IXUSR; - rcu_read_unlock(); + spin_unlock(&files->file_lock); put_files_struct(files); inode->i_op = &proc_pid_link_inode_operations; inode->i_size = 64; @@ -1541,7 +1550,7 @@ static struct dentry *proc_lookupfd(struct inode * dir, struct dentry * dentry, return NULL; out_unlock2: - rcu_read_unlock(); + spin_unlock(&files->file_lock); put_files_struct(files); out_unlock: iput(inode); -- cgit v1.2.3