hey Marcelo,
  I've backported the fix for CVE-2005-2709 to 2.4 for Debian's 2.4
sarge kernel.  Below is a patch against 2.4.32, in case one hasn't been
submitted to you yet.  Please apply.

Signed-off-by: dann frazier <[EMAIL PROTECTED]>

diff -urNp linux-2.4.32.orig/include/linux/proc_fs.h 
linux-2.4.32/include/linux/proc_fs.h
--- linux-2.4.32.orig/include/linux/proc_fs.h   2005-01-19 07:10:12.000000000 
-0700
+++ linux-2.4.32/include/linux/proc_fs.h        2005-11-18 10:21:16.000000000 
-0700
@@ -70,6 +70,7 @@ struct proc_dir_entry {
        atomic_t count;         /* use count */
        int deleted;            /* delete flag */
        kdev_t  rdev;
+       void *set;
 };
 
 #define PROC_INODE_PROPER(inode) ((inode)->i_ino & ~0xffff)
diff -urNp linux-2.4.32.orig/include/linux/sysctl.h 
linux-2.4.32/include/linux/sysctl.h
--- linux-2.4.32.orig/include/linux/sysctl.h    2005-04-03 19:42:20.000000000 
-0600
+++ linux-2.4.32/include/linux/sysctl.h 2005-11-18 10:21:20.000000000 -0700
@@ -29,6 +29,7 @@
 #include <linux/list.h>
 
 struct file;
+struct completion;
 
 #define CTL_MAXNAME 10
 
@@ -829,6 +830,8 @@ struct ctl_table_header
 {
        ctl_table *ctl_table;
        struct list_head ctl_entry;
+       int used;
+       struct completion *unregistering;
 };
 
 struct ctl_table_header * register_sysctl_table(ctl_table * table, 
diff -urNp linux-2.4.32.orig/kernel/sysctl.c linux-2.4.32/kernel/sysctl.c
--- linux-2.4.32.orig/kernel/sysctl.c   2005-01-19 07:10:13.000000000 -0700
+++ linux-2.4.32/kernel/sysctl.c        2005-11-18 10:41:30.000000000 -0700
@@ -147,7 +147,7 @@ static struct inode_operations proc_sys_
 
 extern struct proc_dir_entry *proc_sys_root;
 
-static void register_proc_table(ctl_table *, struct proc_dir_entry *);
+static void register_proc_table(ctl_table *, struct proc_dir_entry *, void *);
 static void unregister_proc_table(ctl_table *, struct proc_dir_entry *);
 #endif
 
@@ -360,10 +360,51 @@ static ctl_table dev_table[] = {
 
 extern void init_irq_proc (void);
 
+static spinlock_t sysctl_lock = SPIN_LOCK_UNLOCKED;
+
+/* called under sysctl_lock */
+static int use_table(struct ctl_table_header *p)
+{
+       if (unlikely(p->unregistering != NULL))
+               return 0;
+       p->used++;
+       return 1;
+}
+
+/* called under sysctl_lock */
+static void unuse_table(struct ctl_table_header *p)
+{
+       if (!--p->used)
+               if (unlikely(p->unregistering != NULL))
+                       complete(p->unregistering);
+}
+
+/* called under sysctl_lock, will reacquire if has to wait */
+static void start_unregistering(struct ctl_table_header *p)
+{
+       /*
+        * if p->used is 0, nobody will ever touch that entry again;
+        * we'll eliminate all paths to it before dropping sysctl_lock
+        */
+       if (unlikely(p->used)) {
+               struct completion wait;
+               init_completion(&wait);
+               p->unregistering = &wait;
+               spin_unlock(&sysctl_lock);
+               wait_for_completion(&wait);
+               spin_lock(&sysctl_lock);
+       }
+       /*
+        * do not remove from the list until nobody holds it; walking the
+        * list in do_sysctl() relies on that.
+        */
+       list_del_init(&p->ctl_entry);
+}
+
 void __init sysctl_init(void)
 {
 #ifdef CONFIG_PROC_FS
-       register_proc_table(root_table, proc_sys_root);
+       register_proc_table(root_table, proc_sys_root, &root_table_header);
        init_irq_proc();
 #endif
 }
@@ -372,6 +413,7 @@ int do_sysctl(int *name, int nlen, void 
               void *newval, size_t newlen)
 {
        struct list_head *tmp;
+       int error = -ENOTDIR;
 
        if (nlen <= 0 || nlen >= CTL_MAXNAME)
                return -ENOTDIR;
@@ -383,21 +425,31 @@ int do_sysctl(int *name, int nlen, void 
                if ((ssize_t)old_len < 0)
                        return -EINVAL;
        }
+       spin_lock(&sysctl_lock);
        tmp = &root_table_header.ctl_entry;
        do {
                struct ctl_table_header *head =
                        list_entry(tmp, struct ctl_table_header, ctl_entry);
                void *context = NULL;
-               int error = parse_table(name, nlen, oldval, oldlenp, 
+
+               if (!use_table(head))
+                       continue;
+
+               spin_unlock(&sysctl_lock);
+
+               error = parse_table(name, nlen, oldval, oldlenp, 
                                        newval, newlen, head->ctl_table,
                                        &context);
                if (context)
                        kfree(context);
+
+               spin_lock(&sysctl_lock);
+               unuse_table(head);
                if (error != -ENOTDIR)
-                       return error;
-               tmp = tmp->next;
-       } while (tmp != &root_table_header.ctl_entry);
-       return -ENOTDIR;
+                       break;
+       } while ((tmp = tmp->next) != &root_table_header.ctl_entry);
+       spin_unlock(&sysctl_lock);
+       return error;
 }
 
 extern asmlinkage long sys_sysctl(struct __sysctl_args *args)
@@ -604,12 +656,16 @@ struct ctl_table_header *register_sysctl
                return NULL;
        tmp->ctl_table = table;
        INIT_LIST_HEAD(&tmp->ctl_entry);
+       tmp->used = 0;
+       tmp->unregistering = NULL;
+       spin_lock(&sysctl_lock);
        if (insert_at_head)
                list_add(&tmp->ctl_entry, &root_table_header.ctl_entry);
        else
                list_add_tail(&tmp->ctl_entry, &root_table_header.ctl_entry);
+       spin_unlock(&sysctl_lock);
 #ifdef CONFIG_PROC_FS
-       register_proc_table(table, proc_sys_root);
+       register_proc_table(table, proc_sys_root, tmp);
 #endif
        return tmp;
 }
@@ -623,10 +679,12 @@ struct ctl_table_header *register_sysctl
  */
 void unregister_sysctl_table(struct ctl_table_header * header)
 {
-       list_del(&header->ctl_entry);
+       spin_lock(&sysctl_lock);
+       start_unregistering(header);
 #ifdef CONFIG_PROC_FS
        unregister_proc_table(header->ctl_table, proc_sys_root);
 #endif
+       spin_unlock(&sysctl_lock);
        kfree(header);
 }
 
@@ -637,7 +695,7 @@ void unregister_sysctl_table(struct ctl_
 #ifdef CONFIG_PROC_FS
 
 /* Scan the sysctl entries in table and add them all into /proc */
-static void register_proc_table(ctl_table * table, struct proc_dir_entry *root)
+static void register_proc_table(ctl_table * table, struct proc_dir_entry 
*root, void *set)
 {
        struct proc_dir_entry *de;
        int len;
@@ -673,6 +731,7 @@ static void register_proc_table(ctl_tabl
                        de = create_proc_entry(table->procname, mode, root);
                        if (!de)
                                continue;
+                       de->set = set;
                        de->data = (void *) table;
                        if (table->proc_handler) {
                                de->proc_fops = &proc_sys_file_operations;
@@ -681,7 +740,7 @@ static void register_proc_table(ctl_tabl
                }
                table->de = de;
                if (de->mode & S_IFDIR)
-                       register_proc_table(table->child, de);
+                       register_proc_table(table->child, de, set);
        }
 }
 
@@ -706,6 +765,13 @@ static void unregister_proc_table(ctl_ta
                                continue;
                }
 
+               /*
+                * In any case, mark the entry as goner; we'll keep it
+                * around if it's busy, but we'll know to do nothing with
+                * its fields.  We are under sysctl_lock here.
+                */
+               de->data = NULL;
+
                /* Don't unregister proc entries that are still being used.. */
                if (atomic_read(&de->count))
                        continue;
@@ -719,31 +785,44 @@ static ssize_t do_rw_proc(int write, str
                          size_t count, loff_t *ppos)
 {
        int op;
-       struct proc_dir_entry *de;
+       struct proc_dir_entry *de =
+         (struct proc_dir_entry*) file->f_dentry->d_inode->u.generic_ip;
        struct ctl_table *table;
        size_t res;
-       ssize_t error;
-       
-       de = (struct proc_dir_entry*) file->f_dentry->d_inode->u.generic_ip;
-       if (!de || !de->data)
-               return -ENOTDIR;
-       table = (struct ctl_table *) de->data;
-       if (!table || !table->proc_handler)
-               return -ENOTDIR;
-       op = (write ? 002 : 004);
-       if (ctl_perm(table, op))
-               return -EPERM;
-       
-       res = count;
+       ssize_t error = -ENOTDIR;
 
-       /*
-        * FIXME: we need to pass on ppos to the handler.
-        */
+       spin_lock(&sysctl_lock);
+       if (de && de->data && use_table(de->set)) {
+               /*
+                * at that point we know that sysctl was not unregistered
+                * and won't be until we finish
+                */
+               spin_unlock(&sysctl_lock);
+               table = (struct ctl_table *) de->data;
+               if (!table || !table->proc_handler)
+                       goto out;
+               error = -EPERM;
+               op = (write ? 002 : 004);
+               if (ctl_perm(table, op))
+                       goto out;
+               
+               /* careful: calling conventions are nasty here */
+               res = count;
 
-       error = (*table->proc_handler) (table, write, file, buf, &res);
-       if (error)
-               return error;
-       return res;
+               /*
+                * FIXME: we need to pass on ppos to the handler.
+                */
+
+               error = (*table->proc_handler)(table, write, file,
+                                               buf, &res);
+               if (!error)
+                       error = res;
+       out:
+               spin_lock(&sysctl_lock);
+               unuse_table(de->set);
+       }
+       spin_unlock(&sysctl_lock);
+       return error;
 }
 
 static ssize_t proc_readsys(struct file * file, char * buf,



-- 
To UNSUBSCRIBE, email to [EMAIL PROTECTED]
with a subject of "unsubscribe". Trouble? Contact [EMAIL PROTECTED]

Reply via email to