Author: dannf Date: Wed Jan 16 06:38:09 2008 New Revision: 10120 Log: * bugfix/fat-move-ioctl-compat-code.patch, bugfix/fat-fix-compat-ioctls.patch [SECURITY][ABI Changer] 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/17etch1 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 Wed Jan 16 06:38:09 2008 @@ -7,6 +7,10 @@ [SECURITY] Use the access mode flag instead of the open flag when testing access mode for a directory. See CVE-2008-0001 + * bugfix/fat-move-ioctl-compat-code.patch, bugfix/fat-fix-compat-ioctls.patch + [SECURITY][ABI Changer] Fix kernel_dirent corruption in the compat layer + for fat ioctls + See CVE-2007-2878 -- dann frazier <[EMAIL PROTECTED]> Tue, 15 Jan 2008 16:44:15 -0700 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 Wed Jan 16 06:38:09 2008 @@ -0,0 +1,311 @@ +From: OGAWA Hirofumi <[EMAIL PROTECTED]> +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 <[EMAIL PROTECTED]> +Signed-off-by: OGAWA Hirofumi <[EMAIL PROTECTED]> +Cc: <[EMAIL PROTECTED]> +Signed-off-by: Andrew Morton <[EMAIL PROTECTED]> +Signed-off-by: Linus Torvalds <[EMAIL PROTECTED]> +--- + +Backported to Debian's 2.6.18 by dann frazier <[EMAIL PROTECTED]> + +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 Wed Jan 16 06:38:09 2008 @@ -0,0 +1,167 @@ +From: David Howells <[EMAIL PROTECTED]> +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 <[EMAIL PROTECTED]> +Signed-off-by: Jens Axboe <[EMAIL PROTECTED]> +--- + +Backported to Debian's 2.6.18 by dann frazier <[EMAIL PROTECTED]> + +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/17etch1 ============================================================================== --- dists/etch-security/linux-2.6/debian/patches/series/17etch1 (original) +++ dists/etch-security/linux-2.6/debian/patches/series/17etch1 Wed Jan 16 06:38:09 2008 @@ -1,2 +1,4 @@ + bugfix/i4l-isdn_ioctl-mem-overrun.patch + bugfix/vfs-use-access-mode-flag.patch ++ bugfix/fat-move-ioctl-compat-code.patch ++ bugfix/fat-fix-compat-ioctls.patch _______________________________________________ Kernel-svn-changes mailing list Kernel-svn-changes@lists.alioth.debian.org http://lists.alioth.debian.org/mailman/listinfo/kernel-svn-changes