summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAl Viro <viro@ZenIV.linux.org.uk>2008-05-01 03:52:22 +0100
committerGreg Kroah-Hartman <gregkh@suse.de>2008-05-01 14:44:40 -0700
commit9a6ec895392ed38549a94c855f045f3a83cb89af (patch)
treeb4961a5840748f4444d36a32dea201e5501fb958
parentb0cc38c177802742d847469e0a06f4ad31d011cc (diff)
Fix dnotify/close race (CVE-2008-1375)
commit 214b7049a7929f03bbd2786aaef04b8b79db34e2 upstream. 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> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r--fs/dnotify.c11
1 files changed, 11 insertions, 0 deletions
diff --git a/fs/dnotify.c b/fs/dnotify.c
index 28d01ed66de0..eaecc4cfe540 100644
--- a/fs/dnotify.c
+++ b/fs/dnotify.c
@@ -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;
@@ -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) {
@@ -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;