[kernel] r9020 - in dists/etch-security/linux-2.6/debian: . patches/bugfix patches/series

Dann Frazier dannf at alioth.debian.org
Sat Jun 23 17:43:28 UTC 2007


Author: dannf
Date: Sat Jun 23 17:43:28 2007
New Revision: 9020

Log:
* bugfix/fat-move-ioctl-compat-code.patch, bugfix/fat-fix-compat-ioctls.patch
  [SECURITY] Fix kernel_dirent corruption in the compat layer for fat ioctls
  See CVE-2007-2878

Added:
   dists/etch-security/linux-2.6/debian/patches/bugfix/fat-fix-compat-ioctls.patch
   dists/etch-security/linux-2.6/debian/patches/bugfix/fat-move-ioctl-compat-code.patch
Modified:
   dists/etch-security/linux-2.6/debian/changelog
   dists/etch-security/linux-2.6/debian/patches/series/12etch3

Modified: dists/etch-security/linux-2.6/debian/changelog
==============================================================================
--- dists/etch-security/linux-2.6/debian/changelog	(original)
+++ dists/etch-security/linux-2.6/debian/changelog	Sat Jun 23 17:43:28 2007
@@ -3,8 +3,11 @@
   * bugfix/bluetooth-l2cap-hci-info-leaks.patch
     [SECURITY] Fix information leaks in setsockopt() implementations
     See CVE-2007-1353
+  * bugfix/fat-move-ioctl-compat-code.patch, bugfix/fat-fix-compat-ioctls.patch
+    [SECURITY] Fix kernel_dirent corruption in the compat layer for fat ioctls
+    See CVE-2007-2878
 
- -- dann frazier <dannf at debian.org>  Thu, 17 May 2007 13:58:07 -0600
+ -- dann frazier <dannf at debian.org>  Sat, 23 Jun 2007 18:38:19 +0100
 
 linux-2.6 (2.6.18.dfsg.1-12etch2) stable-security; urgency=high
 

Added: dists/etch-security/linux-2.6/debian/patches/bugfix/fat-fix-compat-ioctls.patch
==============================================================================
--- (empty file)
+++ dists/etch-security/linux-2.6/debian/patches/bugfix/fat-fix-compat-ioctls.patch	Sat Jun 23 17:43:28 2007
@@ -0,0 +1,311 @@
+From: OGAWA Hirofumi <hirofumi at mail.parknet.co.jp>
+Date: Tue, 8 May 2007 07:31:28 +0000 (-0700)
+Subject: fat: fix VFAT compat ioctls on 64-bit systems
+X-Git-Tag: v2.6.22-rc1~614
+X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux-2.6.git;a=commitdiff_plain;h=c483bab099cb89e92b7cad94a52fcdaf37e56657
+
+fat: fix VFAT compat ioctls on 64-bit systems
+
+If you compile and run the below test case in an msdos or vfat directory on
+an x86-64 system with -m32 you'll get garbage in the kernel_dirent struct
+followed by a SIGSEGV.
+
+The patch fixes this.
+
+Reported and initial fix by Bart Oldeman
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <dirent.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+struct kernel_dirent {
+         long            d_ino;
+         long		d_off;
+         unsigned short  d_reclen;
+         char            d_name[256]; /* We must not include limits.h! */
+};
+#define VFAT_IOCTL_READDIR_BOTH  _IOR('r', 1, struct kernel_dirent [2])
+#define VFAT_IOCTL_READDIR_SHORT  _IOR('r', 2, struct kernel_dirent [2])
+
+int main(void)
+{
+         int fd = open(".", O_RDONLY);
+         struct kernel_dirent de[2];
+
+         while (1) {
+                 int i = ioctl(fd, VFAT_IOCTL_READDIR_BOTH, (long)de);
+                 if (i == -1) break;
+                 if (de[0].d_reclen == 0) break;
+                 printf("SFN: reclen=%2d off=%d ino=%d, %-12s",
+ 		       de[0].d_reclen, de[0].d_off, de[0].d_ino, de[0].d_name);
+ 		if (de[1].d_reclen)
+ 		  printf("\tLFN: reclen=%2d off=%d ino=%d, %s",
+ 		    de[1].d_reclen, de[1].d_off, de[1].d_ino, de[1].d_name);
+ 		printf("\n");
+         }
+         return 0;
+}
+
+Signed-off-by: Bart Oldeman <bartoldeman at users.sourceforge.net>
+Signed-off-by: OGAWA Hirofumi <hirofumi at mail.parknet.co.jp>
+Cc: <stable at kernel.org>
+Signed-off-by: Andrew Morton <akpm at linux-foundation.org>
+Signed-off-by: Linus Torvalds <torvalds at linux-foundation.org>
+---
+
+Backported to Debian's 2.6.18 by dann frazier <dannf at debian.org>
+
+diff -urpN linux-source-2.6.18.orig/fs/fat/dir.c linux-source-2.6.18/fs/fat/dir.c
+--- linux-source-2.6.18.orig/fs/fat/dir.c	2007-06-22 21:48:00.000000000 -0600
++++ linux-source-2.6.18/fs/fat/dir.c	2007-06-22 21:48:42.000000000 -0600
+@@ -422,7 +422,7 @@ EODir:
+ EXPORT_SYMBOL_GPL(fat_search_long);
+ 
+ struct fat_ioctl_filldir_callback {
+-	struct dirent __user *dirent;
++	void __user *dirent;
+ 	int result;
+ 	/* for dir ioctl */
+ 	const char *longname;
+@@ -647,62 +647,85 @@ static int fat_readdir(struct file *filp
+ 	return __fat_readdir(inode, filp, dirent, filldir, 0, 0);
+ }
+ 
+-static int fat_ioctl_filldir(void *__buf, const char *name, int name_len,
+-			     loff_t offset, ino_t ino, unsigned int d_type)
++#define FAT_IOCTL_FILLDIR_FUNC(func, dirent_type)			   \
++static int func(void *__buf, const char *name, int name_len,		   \
++			     loff_t offset, ino_t ino, unsigned int d_type)  \
++{									   \
++	struct fat_ioctl_filldir_callback *buf = __buf;			   \
++	struct dirent_type __user *d1 = buf->dirent;			   \
++	struct dirent_type __user *d2 = d1 + 1;				   \
++									   \
++	if (buf->result)						   \
++		return -EINVAL;						   \
++	buf->result++;							   \
++									   \
++	if (name != NULL) {						   \
++		/* dirent has only short name */			   \
++		if (name_len >= sizeof(d1->d_name))			   \
++			name_len = sizeof(d1->d_name) - 1;		   \
++									   \
++		if (put_user(0, d2->d_name)			||	   \
++		    put_user(0, &d2->d_reclen)			||	   \
++		    copy_to_user(d1->d_name, name, name_len)	||	   \
++		    put_user(0, d1->d_name + name_len)		||	   \
++		    put_user(name_len, &d1->d_reclen))			   \
++			goto efault;					   \
++	} else {							   \
++		/* dirent has short and long name */			   \
++		const char *longname = buf->longname;			   \
++		int long_len = buf->long_len;				   \
++		const char *shortname = buf->shortname;			   \
++		int short_len = buf->short_len;				   \
++									   \
++		if (long_len >= sizeof(d1->d_name))			   \
++			long_len = sizeof(d1->d_name) - 1;		   \
++		if (short_len >= sizeof(d1->d_name))			   \
++			short_len = sizeof(d1->d_name) - 1;		   \
++									   \
++		if (copy_to_user(d2->d_name, longname, long_len)	|| \
++		    put_user(0, d2->d_name + long_len)			|| \
++		    put_user(long_len, &d2->d_reclen)			|| \
++		    put_user(ino, &d2->d_ino)				|| \
++		    put_user(offset, &d2->d_off)			|| \
++		    copy_to_user(d1->d_name, shortname, short_len)	|| \
++		    put_user(0, d1->d_name + short_len)			|| \
++		    put_user(short_len, &d1->d_reclen))			   \
++			goto efault;					   \
++	}								   \
++	return 0;							   \
++efault:									   \
++	buf->result = -EFAULT;						   \
++	return -EFAULT;							   \
++}
++
++FAT_IOCTL_FILLDIR_FUNC(fat_ioctl_filldir, dirent)
++
++static int fat_ioctl_readdir(struct inode *inode, struct file *filp,
++			     void __user *dirent, filldir_t filldir,
++			     int short_only, int both)
+ {
+-	struct fat_ioctl_filldir_callback *buf = __buf;
+-	struct dirent __user *d1 = buf->dirent;
+-	struct dirent __user *d2 = d1 + 1;
+-
+-	if (buf->result)
+-		return -EINVAL;
+-	buf->result++;
+-
+-	if (name != NULL) {
+-		/* dirent has only short name */
+-		if (name_len >= sizeof(d1->d_name))
+-			name_len = sizeof(d1->d_name) - 1;
+-
+-		if (put_user(0, d2->d_name)			||
+-		    put_user(0, &d2->d_reclen)			||
+-		    copy_to_user(d1->d_name, name, name_len)	||
+-		    put_user(0, d1->d_name + name_len)		||
+-		    put_user(name_len, &d1->d_reclen))
+-			goto efault;
+-	} else {
+-		/* dirent has short and long name */
+-		const char *longname = buf->longname;
+-		int long_len = buf->long_len;
+-		const char *shortname = buf->shortname;
+-		int short_len = buf->short_len;
+-
+-		if (long_len >= sizeof(d1->d_name))
+-			long_len = sizeof(d1->d_name) - 1;
+-		if (short_len >= sizeof(d1->d_name))
+-			short_len = sizeof(d1->d_name) - 1;
+-
+-		if (copy_to_user(d2->d_name, longname, long_len)	||
+-		    put_user(0, d2->d_name + long_len)			||
+-		    put_user(long_len, &d2->d_reclen)			||
+-		    put_user(ino, &d2->d_ino)				||
+-		    put_user(offset, &d2->d_off)			||
+-		    copy_to_user(d1->d_name, shortname, short_len)	||
+-		    put_user(0, d1->d_name + short_len)			||
+-		    put_user(short_len, &d1->d_reclen))
+-			goto efault;
++	struct fat_ioctl_filldir_callback buf;
++	int ret;
++
++	buf.dirent = dirent;
++	buf.result = 0;
++	mutex_lock(&inode->i_mutex);
++	ret = -ENOENT;
++	if (!IS_DEADDIR(inode)) {
++		ret = __fat_readdir(inode, filp, &buf, filldir,
++				    short_only, both);
+ 	}
+-	return 0;
+-efault:
+-	buf->result = -EFAULT;
+-	return -EFAULT;
++	mutex_unlock(&inode->i_mutex);
++	if (ret >= 0)
++		ret = buf.result;
++	return ret;
+ }
+ 
+-static int fat_dir_ioctl(struct inode * inode, struct file * filp,
+-		  unsigned int cmd, unsigned long arg)
++static int fat_dir_ioctl(struct inode *inode, struct file *filp,
++			 unsigned int cmd, unsigned long arg)
+ {
+-	struct fat_ioctl_filldir_callback buf;
+-	struct dirent __user *d1;
+-	int ret, short_only, both;
++	struct dirent __user *d1 = (struct dirent __user *)arg;
++	int short_only, both;
+ 
+ 	switch (cmd) {
+ 	case VFAT_IOCTL_READDIR_SHORT:
+@@ -717,7 +740,6 @@ static int fat_dir_ioctl(struct inode * 
+ 		return fat_generic_ioctl(inode, filp, cmd, arg);
+ 	}
+ 
+-	d1 = (struct dirent __user *)arg;
+ 	if (!access_ok(VERIFY_WRITE, d1, sizeof(struct dirent[2])))
+ 		return -EFAULT;
+ 	/*
+@@ -728,69 +750,48 @@ static int fat_dir_ioctl(struct inode * 
+ 	if (put_user(0, &d1->d_reclen))
+ 		return -EFAULT;
+ 
+-	buf.dirent = d1;
+-	buf.result = 0;
+-	mutex_lock(&inode->i_mutex);
+-	ret = -ENOENT;
+-	if (!IS_DEADDIR(inode)) {
+-		ret = __fat_readdir(inode, filp, &buf, fat_ioctl_filldir,
+-				    short_only, both);
+-	}
+-	mutex_unlock(&inode->i_mutex);
+-	if (ret >= 0)
+-		ret = buf.result;
+-	return ret;
++	return fat_ioctl_readdir(inode, filp, d1, fat_ioctl_filldir,
++				 short_only, both);
+ }
+ 
+ #ifdef CONFIG_COMPAT
+ #define	VFAT_IOCTL_READDIR_BOTH32	_IOR('r', 1, struct compat_dirent[2])
+ #define	VFAT_IOCTL_READDIR_SHORT32	_IOR('r', 2, struct compat_dirent[2])
+ 
+-static long fat_compat_put_dirent32(struct dirent *d,
+-				    struct compat_dirent __user *d32)
+-{
+-        if (!access_ok(VERIFY_WRITE, d32, sizeof(struct compat_dirent)))
+-                return -EFAULT;
++FAT_IOCTL_FILLDIR_FUNC(fat_compat_ioctl_filldir, compat_dirent)
+ 
+-        __put_user(d->d_ino, &d32->d_ino);
+-        __put_user(d->d_off, &d32->d_off);
+-        __put_user(d->d_reclen, &d32->d_reclen);
+-        if (__copy_to_user(d32->d_name, d->d_name, d->d_reclen))
+-		return -EFAULT;
+-
+-        return 0;
+-}
+-
+-static long fat_compat_dir_ioctl(struct file *file, unsigned cmd,
++static long fat_compat_dir_ioctl(struct file *filp, unsigned cmd,
+ 				 unsigned long arg)
+ {
+-	struct compat_dirent __user *p = compat_ptr(arg);
+-	int ret;
+-	mm_segment_t oldfs = get_fs();
+-	struct dirent d[2];
++	struct inode *inode = filp->f_dentry->d_inode;
++	struct compat_dirent __user *d1 = compat_ptr(arg);
++	int short_only, both;
+ 
+ 	switch (cmd) {
+-	case VFAT_IOCTL_READDIR_BOTH32:
+-		cmd = VFAT_IOCTL_READDIR_BOTH;
+-		break;
+ 	case VFAT_IOCTL_READDIR_SHORT32:
+-		cmd = VFAT_IOCTL_READDIR_SHORT;
++		short_only = 1;
++		both = 0;
++		break;
++	case VFAT_IOCTL_READDIR_BOTH32:
++		short_only = 0;
++		both = 1;
+ 		break;
+ 	default:
+ 		return -ENOIOCTLCMD;
+ 	}
+ 
+-	set_fs(KERNEL_DS);
+-	lock_kernel();
+-	ret = fat_dir_ioctl(file->f_dentry->d_inode, file,
+-			    cmd, (unsigned long) &d);
+-	unlock_kernel();
+-	set_fs(oldfs);
+-	if (ret >= 0) {
+-		ret |= fat_compat_put_dirent32(&d[0], p);
+-		ret |= fat_compat_put_dirent32(&d[1], p + 1);
+-	}
+-	return ret;
++	if (!access_ok(VERIFY_WRITE, d1, sizeof(struct compat_dirent[2])))
++		return -EFAULT;
++	/*
++	 * Yes, we don't need this put_user() absolutely. However old
++	 * code didn't return the right value. So, app use this value,
++	 * in order to check whether it is EOF.
++	 */
++	if (put_user(0, &d1->d_reclen))
++		return -EFAULT;
++
++	return fat_ioctl_readdir(inode, filp, d1, fat_compat_ioctl_filldir,
++				 short_only, both);
+ }
+ #endif /* CONFIG_COMPAT */
+ 

Added: dists/etch-security/linux-2.6/debian/patches/bugfix/fat-move-ioctl-compat-code.patch
==============================================================================
--- (empty file)
+++ dists/etch-security/linux-2.6/debian/patches/bugfix/fat-move-ioctl-compat-code.patch	Sat Jun 23 17:43:28 2007
@@ -0,0 +1,167 @@
+From: David Howells <dhowells at redhat.com>
+Date: Thu, 31 Aug 2006 10:50:04 +0000 (+0200)
+Subject: [PATCH] BLOCK: Move the msdos device ioctl compat stuff to the msdos driver [try #6]
+X-Git-Tag: v2.6.19~1581^2~9
+X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux-2.6.git;a=commitdiff_plain;h=188f83dfe0eeecd1427d0d255cc97dbf7ef6b4b7
+
+[PATCH] BLOCK: Move the msdos device ioctl compat stuff to the msdos driver [try #6]
+
+Move the msdos device ioctl compat stuff from fs/compat_ioctl.c to the msdos
+driver so that the msdos header file doesn't need to be included.
+
+Signed-Off-By: David Howells <dhowells at redhat.com>
+Signed-off-by: Jens Axboe <axboe at kernel.dk>
+---
+
+Backported to Debian's 2.6.18 by dann frazier <dannf at debian.org>
+
+diff -urpN linux-source-2.6.18.orig/fs/compat_ioctl.c linux-source-2.6.18/fs/compat_ioctl.c
+--- linux-source-2.6.18.orig/fs/compat_ioctl.c	2006-09-20 04:42:06.000000000 +0100
++++ linux-source-2.6.18/fs/compat_ioctl.c	2007-06-22 15:57:42.000000000 +0100
+@@ -113,7 +113,6 @@
+ #include <linux/nbd.h>
+ #include <linux/random.h>
+ #include <linux/filter.h>
+-#include <linux/msdos_fs.h>
+ #include <linux/pktcdvd.h>
+ 
+ #include <linux/hiddev.h>
+@@ -2052,51 +2051,6 @@ static int mtd_rw_oob(unsigned int fd, u
+ 	return err;
+ }	
+ 
+-#define	VFAT_IOCTL_READDIR_BOTH32	_IOR('r', 1, struct compat_dirent[2])
+-#define	VFAT_IOCTL_READDIR_SHORT32	_IOR('r', 2, struct compat_dirent[2])
+-
+-static long
+-put_dirent32 (struct dirent *d, struct compat_dirent __user *d32)
+-{
+-        if (!access_ok(VERIFY_WRITE, d32, sizeof(struct compat_dirent)))
+-                return -EFAULT;
+-
+-        __put_user(d->d_ino, &d32->d_ino);
+-        __put_user(d->d_off, &d32->d_off);
+-        __put_user(d->d_reclen, &d32->d_reclen);
+-        if (__copy_to_user(d32->d_name, d->d_name, d->d_reclen))
+-		return -EFAULT;
+-
+-        return 0;
+-}
+-
+-static int vfat_ioctl32(unsigned fd, unsigned cmd, unsigned long arg)
+-{
+-	struct compat_dirent __user *p = compat_ptr(arg);
+-	int ret;
+-	mm_segment_t oldfs = get_fs();
+-	struct dirent d[2];
+-
+-	switch(cmd)
+-	{
+-        	case VFAT_IOCTL_READDIR_BOTH32:
+-                	cmd = VFAT_IOCTL_READDIR_BOTH;
+-                	break;
+-        	case VFAT_IOCTL_READDIR_SHORT32:
+-                	cmd = VFAT_IOCTL_READDIR_SHORT;
+-                	break;
+-	}
+-
+-	set_fs(KERNEL_DS);
+-	ret = sys_ioctl(fd,cmd,(unsigned long)&d);
+-	set_fs(oldfs);
+-	if (ret >= 0) {
+-		ret |= put_dirent32(&d[0], p);
+-		ret |= put_dirent32(&d[1], p + 1);
+-	}
+-	return ret;
+-}
+-
+ #define REISERFS_IOC_UNPACK32               _IOW(0xCD,1,int)
+ 
+ static int reiserfs_ioctl32(unsigned fd, unsigned cmd, unsigned long ptr)
+@@ -2866,9 +2820,6 @@ HANDLE_IOCTL(SONET_GETFRSENSE, do_atm_io
+ HANDLE_IOCTL(BLKBSZGET_32, do_blkbszget)
+ HANDLE_IOCTL(BLKBSZSET_32, do_blkbszset)
+ HANDLE_IOCTL(BLKGETSIZE64_32, do_blkgetsize64)
+-/* vfat */
+-HANDLE_IOCTL(VFAT_IOCTL_READDIR_BOTH32, vfat_ioctl32)
+-HANDLE_IOCTL(VFAT_IOCTL_READDIR_SHORT32, vfat_ioctl32)
+ HANDLE_IOCTL(REISERFS_IOC_UNPACK32, reiserfs_ioctl32)
+ /* Raw devices */
+ HANDLE_IOCTL(RAW_SETBIND, raw_ioctl)
+diff -urpN linux-source-2.6.18.orig/fs/fat/dir.c linux-source-2.6.18/fs/fat/dir.c
+--- linux-source-2.6.18.orig/fs/fat/dir.c	2006-09-20 04:42:06.000000000 +0100
++++ linux-source-2.6.18/fs/fat/dir.c	2007-06-22 15:55:53.000000000 +0100
+@@ -20,6 +20,7 @@
+ #include <linux/dirent.h>
+ #include <linux/smp_lock.h>
+ #include <linux/buffer_head.h>
++#include <linux/compat.h>
+ #include <asm/uaccess.h>
+ 
+ static inline loff_t fat_make_i_pos(struct super_block *sb,
+@@ -741,10 +742,65 @@ static int fat_dir_ioctl(struct inode * 
+ 	return ret;
+ }
+ 
++#ifdef CONFIG_COMPAT
++#define	VFAT_IOCTL_READDIR_BOTH32	_IOR('r', 1, struct compat_dirent[2])
++#define	VFAT_IOCTL_READDIR_SHORT32	_IOR('r', 2, struct compat_dirent[2])
++
++static long fat_compat_put_dirent32(struct dirent *d,
++				    struct compat_dirent __user *d32)
++{
++        if (!access_ok(VERIFY_WRITE, d32, sizeof(struct compat_dirent)))
++                return -EFAULT;
++
++        __put_user(d->d_ino, &d32->d_ino);
++        __put_user(d->d_off, &d32->d_off);
++        __put_user(d->d_reclen, &d32->d_reclen);
++        if (__copy_to_user(d32->d_name, d->d_name, d->d_reclen))
++		return -EFAULT;
++
++        return 0;
++}
++
++static long fat_compat_dir_ioctl(struct file *file, unsigned cmd,
++				 unsigned long arg)
++{
++	struct compat_dirent __user *p = compat_ptr(arg);
++	int ret;
++	mm_segment_t oldfs = get_fs();
++	struct dirent d[2];
++
++	switch (cmd) {
++	case VFAT_IOCTL_READDIR_BOTH32:
++		cmd = VFAT_IOCTL_READDIR_BOTH;
++		break;
++	case VFAT_IOCTL_READDIR_SHORT32:
++		cmd = VFAT_IOCTL_READDIR_SHORT;
++		break;
++	default:
++		return -ENOIOCTLCMD;
++	}
++
++	set_fs(KERNEL_DS);
++	lock_kernel();
++	ret = fat_dir_ioctl(file->f_dentry->d_inode, file,
++			    cmd, (unsigned long) &d);
++	unlock_kernel();
++	set_fs(oldfs);
++	if (ret >= 0) {
++		ret |= fat_compat_put_dirent32(&d[0], p);
++		ret |= fat_compat_put_dirent32(&d[1], p + 1);
++	}
++	return ret;
++}
++#endif /* CONFIG_COMPAT */
++
+ const struct file_operations fat_dir_operations = {
+ 	.read		= generic_read_dir,
+ 	.readdir	= fat_readdir,
+ 	.ioctl		= fat_dir_ioctl,
++#ifdef CONFIG_COMPAT
++	.compat_ioctl	= fat_compat_dir_ioctl,
++#endif
+ 	.fsync		= file_fsync,
+ };
+ 

Modified: dists/etch-security/linux-2.6/debian/patches/series/12etch3
==============================================================================
--- dists/etch-security/linux-2.6/debian/patches/series/12etch3	(original)
+++ dists/etch-security/linux-2.6/debian/patches/series/12etch3	Sat Jun 23 17:43:28 2007
@@ -1 +1,3 @@
 + bugfix/bluetooth-l2cap-hci-info-leaks.patch
++ bugfix/fat-move-ioctl-compat-code.patch
++ bugfix/fat-fix-compat-ioctls.patch



More information about the Kernel-svn-changes mailing list