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

Reply via email to