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]