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;
 }

