summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/2.6.18/30049_sysfs_readdir-NULL-deref-2.patch')
-rw-r--r--trunk/2.6.18/30049_sysfs_readdir-NULL-deref-2.patch128
1 files changed, 128 insertions, 0 deletions
diff --git a/trunk/2.6.18/30049_sysfs_readdir-NULL-deref-2.patch b/trunk/2.6.18/30049_sysfs_readdir-NULL-deref-2.patch
new file mode 100644
index 0000000..e242c17
--- /dev/null
+++ b/trunk/2.6.18/30049_sysfs_readdir-NULL-deref-2.patch
@@ -0,0 +1,128 @@
+From: Tejun Heo <htejun@gmail.com>
+Date: Mon, 11 Jun 2007 05:04:01 +0000 (+0900)
+Subject: sysfs: fix race condition around sd->s_dentry, take#2
+X-Git-Tag: v2.6.22-rc5~45
+X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Fstable%2Flinux-2.6.22.y.git;a=commitdiff_plain;h=dd14cbc994709a1c5a64ed3621f583c49a27e521
+
+sysfs: fix race condition around sd->s_dentry, take#2
+
+Allowing attribute and symlink dentries to be reclaimed means
+sd->s_dentry can change dynamically. However, updates to the field
+are unsynchronized leading to race conditions. This patch adds
+sysfs_lock and use it to synchronize updates to sd->s_dentry.
+
+Due to the locking around ->d_iput, the check in sysfs_drop_dentry()
+is complex. sysfs_lock only protect sd->s_dentry pointer itself. The
+validity of the dentry is protected by dcache_lock, so whether dentry
+is alive or not can only be tested while holding both locks.
+
+This is minimal backport of sysfs_drop_dentry() rewrite in devel
+branch.
+
+Signed-off-by: Tejun Heo <htejun@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+---
+
+Backported to Debian's 2.6.18 by dann frazier <dannf@hp.com>
+
+diff -urpN linux-source-2.6.18.orig/fs/sysfs/dir.c linux-source-2.6.18/fs/sysfs/dir.c
+--- linux-source-2.6.18.orig/fs/sysfs/dir.c 2007-11-07 15:44:57.000000000 -0700
++++ linux-source-2.6.18/fs/sysfs/dir.c 2007-11-07 15:38:57.000000000 -0700
+@@ -12,14 +12,26 @@
+ #include "sysfs.h"
+
+ DECLARE_RWSEM(sysfs_rename_sem);
++spinlock_t sysfs_lock = SPIN_LOCK_UNLOCKED;
+
+ static void sysfs_d_iput(struct dentry * dentry, struct inode * inode)
+ {
+ struct sysfs_dirent * sd = dentry->d_fsdata;
+
+ if (sd) {
+- BUG_ON(sd->s_dentry != dentry);
+- sd->s_dentry = NULL;
++ /* sd->s_dentry is protected with sysfs_lock. This
++ * allows sysfs_drop_dentry() to dereference it.
++ */
++ spin_lock(&sysfs_lock);
++
++ /* The dentry might have been deleted or another
++ * lookup could have happened updating sd->s_dentry to
++ * point the new dentry. Ignore if it isn't pointing
++ * to this dentry.
++ */
++ if (sd->s_dentry == dentry)
++ sd->s_dentry = NULL;
++ spin_unlock(&sysfs_lock);
+ sysfs_put(sd);
+ }
+ iput(inode);
+@@ -218,7 +230,10 @@ static int sysfs_attach_attr(struct sysf
+ }
+
+ dentry->d_fsdata = sysfs_get(sd);
++ /* protect sd->s_dentry against sysfs_d_iput */
++ spin_lock(&sysfs_lock);
+ sd->s_dentry = dentry;
++ spin_unlock(&sysfs_lock);
+ error = sysfs_create(dentry, (attr->mode & S_IALLUGO) | S_IFREG, init);
+ if (error) {
+ sysfs_put(sd);
+@@ -240,7 +255,10 @@ static int sysfs_attach_link(struct sysf
+ int err = 0;
+
+ dentry->d_fsdata = sysfs_get(sd);
++ /* protect sd->s_dentry against sysfs_d_iput */
++ spin_lock(&sysfs_lock);
+ sd->s_dentry = dentry;
++ spin_unlock(&sysfs_lock);
+ err = sysfs_create(dentry, S_IFLNK|S_IRWXUGO, init_symlink);
+ if (!err) {
+ dentry->d_op = &sysfs_dentry_ops;
+diff -urpN linux-source-2.6.18.orig/fs/sysfs/inode.c linux-source-2.6.18/fs/sysfs/inode.c
+--- linux-source-2.6.18.orig/fs/sysfs/inode.c 2007-11-07 15:44:57.000000000 -0700
++++ linux-source-2.6.18/fs/sysfs/inode.c 2007-11-07 15:40:19.000000000 -0700
+@@ -217,8 +217,22 @@ const unsigned char * sysfs_get_name(str
+ */
+ void sysfs_drop_dentry(struct sysfs_dirent * sd, struct dentry * parent)
+ {
+- struct dentry * dentry = sd->s_dentry;
++ struct dentry *dentry = NULL;
+
++ /* We're not holding a reference to ->s_dentry dentry but the
++ * field will stay valid as long as sysfs_lock is held.
++ */
++ spin_lock(&sysfs_lock);
++ spin_lock(&dcache_lock);
++
++ /* dget dentry if it's still alive */
++ if (sd->s_dentry && sd->s_dentry->d_inode)
++ dentry = dget_locked(sd->s_dentry);
++
++ spin_unlock(&dcache_lock);
++ spin_unlock(&sysfs_lock);
++
++ /* drop dentry */
+ if (dentry) {
+ spin_lock(&dcache_lock);
+ spin_lock(&dentry->d_lock);
+@@ -232,6 +246,8 @@ void sysfs_drop_dentry(struct sysfs_dire
+ spin_unlock(&dentry->d_lock);
+ spin_unlock(&dcache_lock);
+ }
++
++ dput(dentry);
+ }
+ }
+
+diff -urpN linux-source-2.6.18.orig/fs/sysfs/sysfs.h linux-source-2.6.18/fs/sysfs/sysfs.h
+--- linux-source-2.6.18.orig/fs/sysfs/sysfs.h 2006-09-19 21:42:06.000000000 -0600
++++ linux-source-2.6.18/fs/sysfs/sysfs.h 2007-11-07 15:38:57.000000000 -0700
+@@ -20,6 +20,7 @@ extern const unsigned char * sysfs_get_n
+ extern void sysfs_drop_dentry(struct sysfs_dirent *sd, struct dentry *parent);
+ extern int sysfs_setattr(struct dentry *dentry, struct iattr *iattr);
+
++extern spinlock_t sysfs_lock;
+ extern struct rw_semaphore sysfs_rename_sem;
+ extern struct super_block * sysfs_sb;
+ extern const struct file_operations sysfs_dir_operations;