Commit 18f5a33a authored by Dann Frazier's avatar Dann Frazier

* bugfix/sysfs_readdir-NULL-deref-1.patch,

  bugfix/sysfs_readdir-NULL-deref-2.patch,
  bugfix/sysfs-fix-condition-check.patch
  [SECURITY] Fix potential NULL pointer dereference which can lead to
  a local DoS (kernel oops)
  See CVE-2007-3104

svn path=/dists/etch-security/linux-2.6/; revision=9702
parent 87377b07
linux-2.6 (2.6.18.dfsg.1-13etch5) UNRELEASED-stable-security; urgency=high
* bugfix/sysfs_readdir-NULL-deref-1.patch,
bugfix/sysfs_readdir-NULL-deref-2.patch,
bugfix/sysfs-fix-condition-check.patch
[SECURITY] Fix potential NULL pointer dereference which can lead to
a local DoS (kernel oops)
See CVE-2007-3104
-- dann frazier <dannf@debian.org> Wed, 07 Nov 2007 17:18:15 -0700
linux-2.6 (2.6.18.dfsg.1-13etch4) stable-security; urgency=high
[ Bastian Blank ]
......
From: Tejun Heo <htejun@gmail.com>
Date: Mon, 11 Jun 2007 05:03:27 +0000 (+0900)
Subject: sysfs: fix condition check in sysfs_drop_dentry()
X-Git-Tag: v2.6.22-rc5~46
X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux-2.6.git;a=commitdiff_plain;h=6aa054aadfea613a437ad0b15d38eca2b963fc0a
sysfs: fix condition check in sysfs_drop_dentry()
The condition check doesn't make much sense as it basically always
succeeds. This causes NULL dereferencing on certain cases. It seems
that parentheses are put in the wrong place. Fix it.
Signed-off-by: Tejun Heo <htejun@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
Adjusted to apply to Debian's 2.6.18 by dann frazier <dannf@hp.com>
--- linux-source-2.6.18+2.6.22.y/fs/sysfs/inode.c.orig 2007-11-07 15:40:19.000000000 -0700
+++ linux-source-2.6.18+2.6.22.y/fs/sysfs/inode.c 2007-11-07 17:09:33.000000000 -0700
@@ -236,7 +236,7 @@ void sysfs_drop_dentry(struct sysfs_dire
if (dentry) {
spin_lock(&dcache_lock);
spin_lock(&dentry->d_lock);
- if (!(d_unhashed(dentry) && dentry->d_inode)) {
+ if (!d_unhashed(dentry) && dentry->d_inode) {
dget_locked(dentry);
__d_drop(dentry);
spin_unlock(&dentry->d_lock);
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;
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;
+ bugfix/sysfs_readdir-NULL-deref-1.patch
+ bugfix/sysfs_readdir-NULL-deref-2.patch
+ bugfix/sysfs-fix-condition-check.patch
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment