[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