diff options
Diffstat (limited to 'trunk/2.6.18/30048_sysfs_readdir-NULL-deref-1.patch')
-rw-r--r-- | trunk/2.6.18/30048_sysfs_readdir-NULL-deref-1.patch | 112 |
1 files changed, 112 insertions, 0 deletions
diff --git a/trunk/2.6.18/30048_sysfs_readdir-NULL-deref-1.patch b/trunk/2.6.18/30048_sysfs_readdir-NULL-deref-1.patch new file mode 100644 index 0000000..c13fd1b --- /dev/null +++ b/trunk/2.6.18/30048_sysfs_readdir-NULL-deref-1.patch @@ -0,0 +1,112 @@ +From: Eric Sandeen <sandeen@sandeen.net> +Date: Mon, 11 Jun 2007 05:02:45 +0000 (+0900) +Subject: sysfs: store sysfs inode nrs in s_ino to avoid readdir oopses +X-Git-Tag: v2.6.22-rc5~47 +X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Fstable%2Flinux-2.6.22.y.git;a=commitdiff_plain;h=dc351252b33f8fede396d6173dba117bcb933607 + +sysfs: store sysfs inode nrs in s_ino to avoid readdir oopses + +Backport of +ftp://ftp.kernel.org/pub/linux/kernel/people/akpm/patches/2.6/2.6.22-rc1/2.6.22-rc1-mm1/broken-out/gregkh-driver-sysfs-allocate-inode-number-using-ida.patch + +For regular files in sysfs, sysfs_readdir wants to traverse +sysfs_dirent->s_dentry->d_inode->i_ino to get to the inode number. +But, the dentry can be reclaimed under memory pressure, and there is +no synchronization with readdir. This patch follows Tejun's scheme of +allocating and storing an inode number in the new s_ino member of a +sysfs_dirent, when dirents are created, and retrieving it from there +for readdir, so that the pointer chain doesn't have to be traversed. + +Tejun's upstream patch uses a new-ish "ida" allocator which brings +along some extra complexity; this -stable patch has a brain-dead +incrementing counter which does not guarantee uniqueness, but because +sysfs doesn't hash inodes as iunique expects, uniqueness wasn't +guaranteed today anyway. + +Signed-off-by: Eric Sandeen <sandeen@redhat.com> +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 2006-09-19 21:42:06.000000000 -0600 ++++ linux-source-2.6.18/fs/sysfs/dir.c 2007-11-07 15:31:11.000000000 -0700 +@@ -29,6 +29,14 @@ static struct dentry_operations sysfs_de + .d_iput = sysfs_d_iput, + }; + ++static unsigned int sysfs_inode_counter; ++ino_t sysfs_get_inum(void) ++{ ++ if (unlikely(sysfs_inode_counter < 3)) ++ sysfs_inode_counter = 3; ++ return sysfs_inode_counter++; ++} ++ + /* + * Allocates a new sysfs_dirent and links it to the parent sysfs_dirent + */ +@@ -42,6 +50,7 @@ static struct sysfs_dirent * sysfs_new_d + return NULL; + + memset(sd, 0, sizeof(*sd)); ++ sd->s_ino = sysfs_get_inum(); + atomic_set(&sd->s_count, 1); + atomic_set(&sd->s_event, 0); + INIT_LIST_HEAD(&sd->s_children); +@@ -416,7 +425,7 @@ static int sysfs_readdir(struct file * f + + switch (i) { + case 0: +- ino = dentry->d_inode->i_ino; ++ ino = parent_sd->s_ino; + if (filldir(dirent, ".", 1, i, ino, DT_DIR) < 0) + break; + filp->f_pos++; +@@ -445,10 +454,7 @@ static int sysfs_readdir(struct file * f + + name = sysfs_get_name(next); + len = strlen(name); +- if (next->s_dentry) +- ino = next->s_dentry->d_inode->i_ino; +- else +- ino = iunique(sysfs_sb, 2); ++ ino = next->s_ino; + + if (filldir(dirent, name, len, filp->f_pos, ino, + dt_type(next)) < 0) +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 2006-09-19 21:42:06.000000000 -0600 ++++ linux-source-2.6.18/fs/sysfs/inode.c 2007-11-07 15:30:13.000000000 -0700 +@@ -129,6 +129,7 @@ struct inode * sysfs_new_inode(mode_t mo + inode->i_mapping->a_ops = &sysfs_aops; + inode->i_mapping->backing_dev_info = &sysfs_backing_dev_info; + inode->i_op = &sysfs_inode_operations; ++ inode->i_ino = sd->s_ino; + lockdep_set_class(&inode->i_mutex, &sysfs_inode_imutex_key); + + if (sd->s_iattr) { +diff -urpN linux-source-2.6.18.orig/fs/sysfs/mount.c linux-source-2.6.18/fs/sysfs/mount.c +--- linux-source-2.6.18.orig/fs/sysfs/mount.c 2006-09-19 21:42:06.000000000 -0600 ++++ linux-source-2.6.18/fs/sysfs/mount.c 2007-11-07 15:30:13.000000000 -0700 +@@ -29,6 +29,7 @@ static struct sysfs_dirent sysfs_root = + .s_element = NULL, + .s_type = SYSFS_ROOT, + .s_iattr = NULL, ++ .s_ino = 1, + }; + + static int sysfs_fill_super(struct super_block *sb, void *data, int silent) +diff -urpN linux-source-2.6.18.orig/include/linux/sysfs.h linux-source-2.6.18/include/linux/sysfs.h +--- linux-source-2.6.18.orig/include/linux/sysfs.h 2006-09-19 21:42:06.000000000 -0600 ++++ linux-source-2.6.18/include/linux/sysfs.h 2007-11-07 15:34:16.000000000 -0700 +@@ -72,6 +72,7 @@ struct sysfs_dirent { + void * s_element; + int s_type; + umode_t s_mode; ++ ino_t s_ino; + struct dentry * s_dentry; + struct iattr * s_iattr; + atomic_t s_event; |