Security fixes from upstream. --- fs/ext2/super.c | 39 ++++++++------ fs/ext3/super.c | 25 ++++++--- ipc/shm.c | 2 kernel/sysctl.c | 147 +++++++++++++++++++++++++++++++++++++++++++------------- net/802/tr.c | 4 + net/atm/clip.c | 19 ++++++- 6 files changed, 175 insertions(+), 61 deletions(-) diff --git a/fs/ext2/super.c b/fs/ext2/super.c index d510cc8..4d7c709 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -480,12 +480,8 @@ struct super_block * ext2_read_super (struct super_block * sb, void * data, es = (struct ext2_super_block *) (((char *)bh->b_data) + offset); sb->u.ext2_sb.s_es = es; sb->s_magic = le16_to_cpu(es->s_magic); - if (sb->s_magic != EXT2_SUPER_MAGIC) { - if (!silent) - printk ("VFS: Can't find ext2 filesystem on dev %s.\n", - bdevname(dev)); - goto failed_mount; - } + if (sb->s_magic != EXT2_SUPER_MAGIC) + goto cantfind_ext2; if (le32_to_cpu(es->s_rev_level) == EXT2_GOOD_OLD_REV && (EXT2_HAS_COMPAT_FEATURE(sb, ~0U) || EXT2_HAS_RO_COMPAT_FEATURE(sb, ~0U) || @@ -561,16 +557,19 @@ struct super_block * ext2_read_super (struct super_block * sb, void * data, } sb->u.ext2_sb.s_frag_size = EXT2_MIN_FRAG_SIZE << le32_to_cpu(es->s_log_frag_size); - if (sb->u.ext2_sb.s_frag_size) - sb->u.ext2_sb.s_frags_per_block = sb->s_blocksize / - sb->u.ext2_sb.s_frag_size; - else - sb->s_magic = 0; + if (sb->u.ext2_sb.s_frag_size == 0) + goto cantfind_ext2; + sb->u.ext2_sb.s_frags_per_block = sb->s_blocksize / + sb->u.ext2_sb.s_frag_size; sb->u.ext2_sb.s_blocks_per_group = le32_to_cpu(es->s_blocks_per_group); sb->u.ext2_sb.s_frags_per_group = le32_to_cpu(es->s_frags_per_group); sb->u.ext2_sb.s_inodes_per_group = le32_to_cpu(es->s_inodes_per_group); + if (EXT2_INODE_SIZE(sb) == 0) + goto cantfind_ext2; sb->u.ext2_sb.s_inodes_per_block = sb->s_blocksize / EXT2_INODE_SIZE(sb); + if (sb->u.ext2_sb.s_inodes_per_block == 0) + goto cantfind_ext2; sb->u.ext2_sb.s_itb_per_group = sb->u.ext2_sb.s_inodes_per_group / sb->u.ext2_sb.s_inodes_per_block; sb->u.ext2_sb.s_desc_per_block = sb->s_blocksize / @@ -589,13 +588,10 @@ struct super_block * ext2_read_super (struct super_block * sb, void * data, log2 (EXT2_ADDR_PER_BLOCK(sb)); sb->u.ext2_sb.s_desc_per_block_bits = log2 (EXT2_DESC_PER_BLOCK(sb)); - if (sb->s_magic != EXT2_SUPER_MAGIC) { - if (!silent) - printk ("VFS: Can't find an ext2 filesystem on dev " - "%s.\n", - bdevname(dev)); - goto failed_mount; - } + + if (sb->s_magic != EXT2_SUPER_MAGIC) + goto cantfind_ext2; + if (sb->s_blocksize != bh->b_size) { if (!silent) printk ("VFS: Unsupported blocksize on dev " @@ -625,6 +621,8 @@ struct super_block * ext2_read_super (struct super_block * sb, void * data, goto failed_mount; } + if (EXT2_BLOCKS_PER_GROUP(sb) == 0) + goto cantfind_ext2; sb->u.ext2_sb.s_groups_count = (le32_to_cpu(es->s_blocks_count) - le32_to_cpu(es->s_first_data_block) + EXT2_BLOCKS_PER_GROUP(sb) - 1) / @@ -678,6 +676,11 @@ struct super_block * ext2_read_super (struct super_block * sb, void * data, } ext2_setup_super (sb, es, sb->s_flags & MS_RDONLY); return sb; +cantfind_ext2: + if (!silent) + printk ("VFS: Can't find ext2 filesystem on dev %s.\n", + bdevname(dev)); + goto failed_mount; failed_mount2: for (i = 0; i < db_count; i++) brelse(sb->u.ext2_sb.s_group_desc[i]); diff --git a/fs/ext3/super.c b/fs/ext3/super.c index 33e2f97..c011c50 100644 --- a/fs/ext3/super.c +++ b/fs/ext3/super.c @@ -979,13 +979,9 @@ struct super_block * ext3_read_super (struct super_block * sb, void * data, es = (struct ext3_super_block *) (((char *)bh->b_data) + offset); sbi->s_es = es; sb->s_magic = le16_to_cpu(es->s_magic); - if (sb->s_magic != EXT3_SUPER_MAGIC) { - if (!silent) - printk(KERN_ERR - "VFS: Can't find ext3 filesystem on dev %s.\n", - bdevname(dev)); - goto failed_mount; - } + if (sb->s_magic != EXT3_SUPER_MAGIC) + goto cantfind_ext3; + if (le32_to_cpu(es->s_rev_level) == EXT3_GOOD_OLD_REV && (EXT3_HAS_COMPAT_FEATURE(sb, ~0U) || EXT3_HAS_RO_COMPAT_FEATURE(sb, ~0U) || @@ -1083,8 +1079,13 @@ struct super_block * ext3_read_super (struct super_block * sb, void * data, sbi->s_blocks_per_group = le32_to_cpu(es->s_blocks_per_group); sbi->s_frags_per_group = le32_to_cpu(es->s_frags_per_group); sbi->s_inodes_per_group = le32_to_cpu(es->s_inodes_per_group); + if (EXT3_INODE_SIZE(sb) == 0) + goto cantfind_ext3; sbi->s_inodes_per_block = blocksize / EXT3_INODE_SIZE(sb); - sbi->s_itb_per_group = sbi->s_inodes_per_group /sbi->s_inodes_per_block; + if (sbi->s_inodes_per_block == 0) + goto cantfind_ext3; + sbi->s_itb_per_group = sbi->s_inodes_per_group / + sbi->s_inodes_per_block; sbi->s_desc_per_block = blocksize / sizeof(struct ext3_group_desc); sbi->s_sbh = bh; if (sbi->s_resuid == EXT3_DEF_RESUID) @@ -1114,6 +1115,8 @@ struct super_block * ext3_read_super (struct super_block * sb, void * data, goto failed_mount; } + if (EXT3_BLOCKS_PER_GROUP(sb) == 0) + goto cantfind_ext3; sbi->s_groups_count = (le32_to_cpu(es->s_blocks_count) - le32_to_cpu(es->s_first_data_block) + EXT3_BLOCKS_PER_GROUP(sb) - 1) / @@ -1240,6 +1243,12 @@ struct super_block * ext3_read_super (struct super_block * sb, void * data, return sb; +cantfind_ext3: + if (!silent) + printk(KERN_ERR + "VFS: Can't find ext3 filesystem on dev %s.\n", + bdevname(dev)); + goto failed_mount; failed_mount3: journal_destroy(sbi->s_journal); failed_mount2: diff --git a/ipc/shm.c b/ipc/shm.c index 1df0577..36cb09a 100644 --- a/ipc/shm.c +++ b/ipc/shm.c @@ -161,6 +161,8 @@ static int shm_mmap(struct file * file, struct vm_area_struct * vma) { UPDATE_ATIME(file->f_dentry->d_inode); vma->vm_ops = &shm_vm_ops; + if (!(vma->vm_flags & VM_WRITE)) + vma->vm_flags &= ~VM_MAYWRITE; shm_inc(file->f_dentry->d_inode->i_ino); return 0; } diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 7a2fd02..3d54347 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -147,7 +147,7 @@ static struct inode_operations proc_sys_inode_operations = { 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 *oldval, size_t *oldlenp, 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 *oldval, size_t *oldlenp, 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_table(ctl_table * table, 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_table(ctl_table * table, */ 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_table_header * header) #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_table * table, struct proc_dir_entry *root) 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_table * table, struct proc_dir_entry *root) } 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_table * table, struct proc_dir_entry *root 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, struct file * file, char * buf, 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; - - /* - * FIXME: we need to pass on ppos to the handler. - */ - - error = (*table->proc_handler) (table, write, file, buf, &res); - if (error) - return error; - return res; + ssize_t error = -ENOTDIR; + + 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; + + /* + * 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, diff --git a/net/802/tr.c b/net/802/tr.c index 88fbab8..d68d3d7 100644 --- a/net/802/tr.c +++ b/net/802/tr.c @@ -100,6 +100,8 @@ int tr_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, struct trllc *trllc=(struct trllc *)(trh+1); hdr_len = sizeof(struct trh_hdr) + sizeof(struct trllc); + if ((skb->data - hdr_len) < skb->head) + return -hdr_len; trh = (struct trh_hdr *)skb_push(skb, hdr_len); trllc = (struct trllc *)(trh+1); trllc->dsap = trllc->ssap = EXTENDED_SAP; @@ -110,6 +112,8 @@ int tr_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, else { hdr_len = sizeof(struct trh_hdr); + if ((skb->data - hdr_len) < skb->head) + return -hdr_len; trh = (struct trh_hdr *)skb_push(skb, hdr_len); } diff --git a/net/atm/clip.c b/net/atm/clip.c index a7ded5b..e655580 100644 --- a/net/atm/clip.c +++ b/net/atm/clip.c @@ -377,7 +377,7 @@ static int clip_start_xmit(struct sk_buff *skb,struct net_device *dev) DPRINTK("clip_start_xmit (skb %p)\n",skb); if (!skb->dst) { - printk(KERN_ERR "clip_start_xmit: skb->dst == NULL\n"); + //printk(KERN_ERR "clip_start_xmit: skb->dst == NULL\n"); dev_kfree_skb(skb); clip_priv->stats.tx_dropped++; return 0; @@ -417,6 +417,21 @@ static int clip_start_xmit(struct sk_buff *skb,struct net_device *dev) if (entry->vccs->encap) { void *here; +#if 1 + if (skb_headroom(skb) < RFC1483LLC_LEN || skb_cloned(skb) || + skb_shared(skb)) { + struct sk_buff *skb2 = skb_realloc_headroom(skb, RFC1483LLC_LEN); + if (!skb2) { + clip_priv->stats.tx_dropped++; + dev_kfree_skb(skb); + return 0; + } + if (skb->sk) + skb_set_owner_w(skb2, skb->sk); + dev_kfree_skb(skb); + skb = skb2; + } +#endif here = skb_push(skb,RFC1483LLC_LEN); memcpy(here,llc_oui,sizeof(llc_oui)); ((u16 *) here)[3] = skb->protocol; @@ -489,9 +504,11 @@ static int clip_mkip(struct atm_vcc *vcc,int timeout) else { unsigned int len = skb->len; + skb_get(skb); clip_push(vcc,skb); PRIV(skb->dev)->stats.rx_packets--; PRIV(skb->dev)->stats.rx_bytes -= len; + kfree_skb(skb); } return 0; }