Skip to content

Commit

Permalink
Fix dnotify/close race
Browse files Browse the repository at this point in the history
We have a race between fcntl() and close() that can lead to
dnotify_struct inserted into inode's list *after* the last descriptor
had been gone from current->files.

Since that's the only point where dnotify_struct gets evicted, we are
screwed - it will stick around indefinitely.  Even after struct file in
question is gone and freed.  Worse, we can trigger send_sigio() on it at
any later point, which allows to send an arbitrary signal to arbitrary
process if we manage to apply enough memory pressure to get the page
that used to host that struct file and fill it with the right pattern...

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
  • Loading branch information
Al Viro authored and torvalds committed May 1, 2008
1 parent 6d98ca7 commit 214b704
Showing 1 changed file with 11 additions and 0 deletions.
11 changes: 11 additions & 0 deletions fs/dnotify.c
Expand Up @@ -20,6 +20,7 @@
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <linux/file.h>

int dir_notify_enable __read_mostly = 1;

Expand Down Expand Up @@ -66,6 +67,7 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg)
struct dnotify_struct **prev;
struct inode *inode;
fl_owner_t id = current->files;
struct file *f;
int error = 0;

if ((arg & ~DN_MULTISHOT) == 0) {
Expand All @@ -92,6 +94,15 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg)
prev = &odn->dn_next;
}

rcu_read_lock();
f = fcheck(fd);
rcu_read_unlock();
/* we'd lost the race with close(), sod off silently */
/* note that inode->i_lock prevents reordering problems
* between accesses to descriptor table and ->i_dnotify */
if (f != filp)
goto out_free;

error = __f_setown(filp, task_pid(current), PIDTYPE_PID, 0);
if (error)
goto out_free;
Expand Down

0 comments on commit 214b704

Please sign in to comment.