[kernel] r22739 - in dists/squeeze-security/linux-2.6/debian: . patches/bugfix/all patches/series

Ben Hutchings benh at moszumanska.debian.org
Sun Jun 14 17:27:26 UTC 2015


Author: benh
Date: Sun Jun 14 17:27:25 2015
New Revision: 22739

Log:
Add udf security fixes

Added:
   dists/squeeze-security/linux-2.6/debian/patches/bugfix/all/udf-check-component-length-before-reading-it.patch
   dists/squeeze-security/linux-2.6/debian/patches/bugfix/all/udf-check-path-length-when-reading-symlink.patch
   dists/squeeze-security/linux-2.6/debian/patches/bugfix/all/udf-treat-symlink-component-of-type-2-as.patch
   dists/squeeze-security/linux-2.6/debian/patches/bugfix/all/udf-verify-i_size-when-loading-inode.patch
   dists/squeeze-security/linux-2.6/debian/patches/bugfix/all/udf-verify-symlink-size-before-loading-it.patch
Modified:
   dists/squeeze-security/linux-2.6/debian/changelog
   dists/squeeze-security/linux-2.6/debian/patches/series/48squeeze12

Modified: dists/squeeze-security/linux-2.6/debian/changelog
==============================================================================
--- dists/squeeze-security/linux-2.6/debian/changelog	Thu Jun 11 21:47:07 2015	(r22738)
+++ dists/squeeze-security/linux-2.6/debian/changelog	Sun Jun 14 17:27:25 2015	(r22739)
@@ -46,6 +46,12 @@
   * eCryptfs: Remove buggy and unnecessary write in file name decode routine
     (CVE-2014-9683)
   * HID: fix a couple of off-by-ones (CVE-2014-3184)
+  * udf: Verify i_size when loading inode (CVE-2014-9728, CVE-2014-9729)
+  * udf: Verify symlink size before loading it (CVE-2014-9728)
+  * udf: Treat symlink component of type 2 as /
+  * udf: Check path length when reading symlink (CVE-2014-9731)
+  * udf: Check component length before reading it
+    (CVE-2014-9728, CVE-2014-9730)
 
  -- Ben Hutchings <ben at decadent.org.uk>  Sun, 12 Apr 2015 17:12:31 +0100
 

Added: dists/squeeze-security/linux-2.6/debian/patches/bugfix/all/udf-check-component-length-before-reading-it.patch
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ dists/squeeze-security/linux-2.6/debian/patches/bugfix/all/udf-check-component-length-before-reading-it.patch	Sun Jun 14 17:27:25 2015	(r22739)
@@ -0,0 +1,58 @@
+From: Jan Kara <jack at suse.cz>
+Date: Fri, 19 Dec 2014 14:27:55 +0100
+Subject: [4/4] udf: Check component length before reading it
+Origin: https://git.kernel.org/linus/e237ec37ec154564f8690c5bd1795339955eeef9
+
+Check that length specified in a component of a symlink fits in the
+input buffer we are reading. Also properly ignore component length for
+component types that do not use it. Otherwise we read memory after end
+of buffer for corrupted udf image.
+
+Reported-by: Carl Henrik Lunde <chlunde at ping.uio.no>
+CC: stable at vger.kernel.org
+Signed-off-by: Jan Kara <jack at suse.cz>
+---
+ fs/udf/symlink.c | 9 +++++++--
+ 1 file changed, 7 insertions(+), 2 deletions(-)
+
+diff --git a/fs/udf/symlink.c b/fs/udf/symlink.c
+index 0f1b3a2..ac10ca9 100644
+--- a/fs/udf/symlink.c
++++ b/fs/udf/symlink.c
+@@ -44,14 +44,17 @@ static int udf_pc_to_char(struct super_block *sb, unsigned char *from,
+ 	tolen--;
+ 	while (elen < fromlen) {
+ 		pc = (struct pathComponent *)(from + elen);
++		elen += sizeof(struct pathComponent);
+ 		switch (pc->componentType) {
+ 		case 1:
+ 			/*
+ 			 * Symlink points to some place which should be agreed
+  			 * upon between originator and receiver of the media. Ignore.
+ 			 */
+-			if (pc->lengthComponentIdent > 0)
++			if (pc->lengthComponentIdent > 0) {
++				elen += pc->lengthComponentIdent;
+ 				break;
++			}
+ 			/* Fall through */
+ 		case 2:
+ 			if (tolen == 0)
+@@ -76,6 +79,9 @@ static int udf_pc_to_char(struct super_block *sb, unsigned char *from,
+ 			/* that would be . - just ignore */
+ 			break;
+ 		case 5:
++			elen += pc->lengthComponentIdent;
++			if (elen > fromlen)
++				return -EIO;
+ 			comp_len = udf_get_filename(sb, pc->componentIdent,
+ 						    pc->lengthComponentIdent,
+ 						    p, tolen);
+@@ -87,7 +93,6 @@ static int udf_pc_to_char(struct super_block *sb, unsigned char *from,
+ 			tolen--;
+ 			break;
+ 		}
+-		elen += sizeof(struct pathComponent) + pc->lengthComponentIdent;
+ 	}
+ 	if (p > to + 1)
+ 		p[-1] = '\0';

Added: dists/squeeze-security/linux-2.6/debian/patches/bugfix/all/udf-check-path-length-when-reading-symlink.patch
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ dists/squeeze-security/linux-2.6/debian/patches/bugfix/all/udf-check-path-length-when-reading-symlink.patch	Sun Jun 14 17:27:25 2015	(r22739)
@@ -0,0 +1,234 @@
+From: Jan Kara <jack at suse.cz>
+Date: Thu, 18 Dec 2014 22:37:50 +0100
+Subject: [3/4] udf: Check path length when reading symlink
+Origin: https://git.kernel.org/linus/0e5cc9a40ada6046e6bc3bdfcd0c0d7e4b706b14
+
+Symlink reading code does not check whether the resulting path fits into
+the page provided by the generic code. This isn't as easy as just
+checking the symlink size because of various encoding conversions we
+perform on path. So we have to check whether there is still enough space
+in the buffer on the fly.
+
+CC: stable at vger.kernel.org
+Reported-by: Carl Henrik Lunde <chlunde at ping.uio.no>
+Signed-off-by: Jan Kara <jack at suse.cz>
+[bwh: Backported to 2.6.32: adjust context, indentation]
+---
+ fs/udf/dir.c     |  3 ++-
+ fs/udf/namei.c   |  3 ++-
+ fs/udf/symlink.c | 31 ++++++++++++++++++++++++++-----
+ fs/udf/udfdecl.h |  3 ++-
+ fs/udf/unicode.c | 28 ++++++++++++++++------------
+ 5 files changed, 48 insertions(+), 20 deletions(-)
+
+diff --git a/fs/udf/dir.c b/fs/udf/dir.c
+index a012c51..a7690b4 100644
+--- a/fs/udf/dir.c
++++ b/fs/udf/dir.c
+@@ -164,7 +164,8 @@ static int udf_readdir(struct file *file, struct dir_context *ctx)
+ 			struct kernel_lb_addr tloc = lelb_to_cpu(cfi.icb.extLocation);
+ 
+ 			iblock = udf_get_lb_pblock(dir->i_sb, &tloc, 0);
+-			flen = udf_get_filename(dir->i_sb, nameptr, fname, lfi);
++			flen = udf_get_filename(dir->i_sb, nameptr, lfi, fname,
++						UDF_NAME_LEN);
+ 			dt_type = DT_UNKNOWN;
+ 		}
+ 
+diff --git a/fs/udf/namei.c b/fs/udf/namei.c
+index c12e260..6ff19b5 100644
+--- a/fs/udf/namei.c
++++ b/fs/udf/namei.c
+@@ -237,7 +237,8 @@ static struct fileIdentDesc *udf_find_entry(struct inode *dir,
+ 		if (!lfi)
+ 			continue;
+ 
+-		flen = udf_get_filename(dir->i_sb, nameptr, fname, lfi);
++		flen = udf_get_filename(dir->i_sb, nameptr, lfi, fname,
++					UDF_NAME_LEN);
+ 		if (flen && udf_match(flen, fname, child->len, child->name))
+ 			goto out_ok;
+ 	}
+diff --git a/fs/udf/symlink.c b/fs/udf/symlink.c
+index c3aa6fa..0f1b3a2 100644
+--- a/fs/udf/symlink.c
++++ b/fs/udf/symlink.c
+@@ -32,13 +32,16 @@
+ #include <linux/buffer_head.h>
+ #include "udf_i.h"
+ 
+-static void udf_pc_to_char(struct super_block *sb, char *from, int fromlen,
+-			   char *to)
++static int udf_pc_to_char(struct super_block *sb, char *from,
++			  int fromlen, char *to, int tolen)
+ {
+ 	struct pathComponent *pc;
+ 	int elen = 0;
++	int comp_len;
+ 	char *p = to;
+ 
++	/* Reserve one byte for terminating \0 */
++	tolen--;
+ 	while (elen < fromlen) {
+ 		pc = (struct pathComponent *)(from + elen);
+ 		switch (pc->componentType) {
+@@ -51,22 +54,37 @@ static void udf_pc_to_char(struct super_block *sb, unsigned char *from,
+ 				break;
+ 			/* Fall through */
+ 		case 2:
++			if (tolen == 0)
++				return -ENAMETOOLONG;
+ 			p = to;
+ 			*p++ = '/';
++			tolen--;
+ 			break;
+ 		case 3:
++			if (tolen < 3)
++				return -ENAMETOOLONG;
+ 			memcpy(p, "../", 3);
+ 			p += 3;
++			tolen -= 3;
+ 			break;
+ 		case 4:
++			if (tolen < 2)
++				return -ENAMETOOLONG;
+ 			memcpy(p, "./", 2);
+ 			p += 2;
++			tolen -= 2;
+ 			/* that would be . - just ignore */
+ 			break;
+ 		case 5:
+-			p += udf_get_filename(sb, pc->componentIdent, p,
+-					      pc->lengthComponentIdent);
++			comp_len = udf_get_filename(sb, pc->componentIdent,
++						    pc->lengthComponentIdent,
++						    p, tolen);
++			p += comp_len;
++			tolen -= comp_len;
++			if (tolen == 0)
++				return -ENAMETOOLONG;
+ 			*p++ = '/';
++			tolen--;
+ 			break;
+ 		}
+ 		elen += sizeof(struct pathComponent) + pc->lengthComponentIdent;
+@@ -75,6 +93,7 @@ static void udf_pc_to_char(struct super_block *sb, unsigned char *from,
+ 		p[-1] = '\0';
+ 	else
+ 		p[0] = '\0';
++	return 0;
+ }
+ 
+ static int udf_symlink_filler(struct file *file, struct page *page)
+@@ -107,8 +126,10 @@ static int udf_symlink_filler(struct file *file, struct page *page)
+ 		symlink = bh->b_data;
+ 	}
+ 
+-	udf_pc_to_char(inode->i_sb, symlink, inode->i_size, p);
++	err = udf_pc_to_char(inode->i_sb, symlink, inode->i_size, p, PAGE_SIZE);
+ 	brelse(bh);
++	if (err)
++		goto out_unlock_inode;
+ 
+ 	unlock_kernel();
+ 	SetPageUptodate(page);
+diff --git a/fs/udf/udfdecl.h b/fs/udf/udfdecl.h
+index 1cc3c99..47bb3f5 100644
+--- a/fs/udf/udfdecl.h
++++ b/fs/udf/udfdecl.h
+@@ -200,7 +200,8 @@ udf_get_lb_pblock(struct super_block *sb, struct kernel_lb_addr *loc,
+ }
+ 
+ /* unicode.c */
+-extern int udf_get_filename(struct super_block *, uint8_t *, uint8_t *, int);
++extern int udf_get_filename(struct super_block *, uint8_t *, int, uint8_t *,
++			    int);
+ extern int udf_put_filename(struct super_block *, const uint8_t *, uint8_t *,
+ 			    int);
+ extern int udf_build_ustr(struct ustr *, dstring *, int);
+diff --git a/fs/udf/unicode.c b/fs/udf/unicode.c
+index afd470e..b84fee3 100644
+--- a/fs/udf/unicode.c
++++ b/fs/udf/unicode.c
+@@ -27,7 +27,8 @@
+ 
+ #include "udf_sb.h"
+ 
+-static int udf_translate_to_linux(uint8_t *, uint8_t *, int, uint8_t *, int);
++static int udf_translate_to_linux(uint8_t *, int, uint8_t *, int, uint8_t *,
++				  int);
+ 
+ static int udf_char_to_ustr(struct ustr *dest, const uint8_t *src, int strlen)
+ {
+@@ -332,8 +333,8 @@ try_again:
+ 	return u_len + 1;
+ }
+ 
+-int udf_get_filename(struct super_block *sb, uint8_t *sname, uint8_t *dname,
+-		     int flen)
++int udf_get_filename(struct super_block *sb, uint8_t *sname, int slen,
++		     uint8_t *dname, int dlen)
+ {
+ 	struct ustr *filename, *unifilename;
+ 	int len = 0;
+@@ -346,7 +347,7 @@ int udf_get_filename(struct super_block *sb, uint8_t *sname, uint8_t *dname,
+ 	if (!unifilename)
+ 		goto out1;
+ 
+-	if (udf_build_ustr_exact(unifilename, sname, flen))
++	if (udf_build_ustr_exact(unifilename, sname, slen))
+ 		goto out2;
+ 
+ 	if (UDF_QUERY_FLAG(sb, UDF_FLAG_UTF8)) {
+@@ -365,7 +366,8 @@ int udf_get_filename(struct super_block *sb, uint8_t *sname, uint8_t *dname,
+ 	} else
+ 		goto out2;
+ 
+-	len = udf_translate_to_linux(dname, filename->u_name, filename->u_len,
++	len = udf_translate_to_linux(dname, dlen,
++				     filename->u_name, filename->u_len,
+ 				     unifilename->u_name, unifilename->u_len);
+ out2:
+ 	kfree(unifilename);
+@@ -402,10 +404,12 @@ int udf_put_filename(struct super_block *sb, const uint8_t *sname,
+ #define EXT_MARK		'.'
+ #define CRC_MARK		'#'
+ #define EXT_SIZE 		5
++/* Number of chars we need to store generated CRC to make filename unique */
++#define CRC_LEN			5
+ 
+-static int udf_translate_to_linux(uint8_t *newName, uint8_t *udfName,
+-				  int udfLen, uint8_t *fidName,
+-				  int fidNameLen)
++static int udf_translate_to_linux(uint8_t *newName, int newLen,
++				  uint8_t *udfName, int udfLen,
++				  uint8_t *fidName, int fidNameLen)
+ {
+ 	int index, newIndex = 0, needsCRC = 0;
+ 	int extIndex = 0, newExtIndex = 0, hasExt = 0;
+@@ -439,7 +443,7 @@ static int udf_translate_to_linux(uint8_t *newName, uint8_t *udfName,
+ 					newExtIndex = newIndex;
+ 				}
+ 			}
+-			if (newIndex < 256)
++			if (newIndex < newLen)
+ 				newName[newIndex++] = curr;
+ 			else
+ 				needsCRC = 1;
+@@ -467,13 +471,13 @@ static int udf_translate_to_linux(uint8_t *newName, uint8_t *udfName,
+ 				}
+ 				ext[localExtIndex++] = curr;
+ 			}
+-			maxFilenameLen = 250 - localExtIndex;
++			maxFilenameLen = newLen - CRC_LEN - localExtIndex;
+ 			if (newIndex > maxFilenameLen)
+ 				newIndex = maxFilenameLen;
+ 			else
+ 				newIndex = newExtIndex;
+-		} else if (newIndex > 250)
+-			newIndex = 250;
++		} else if (newIndex > newLen - CRC_LEN)
++			newIndex = newLen - CRC_LEN;
+ 		newName[newIndex++] = CRC_MARK;
+ 		valueCRC = crc_itu_t(0, fidName, fidNameLen);
+ 		newName[newIndex++] = hexChar[(valueCRC & 0xf000) >> 12];

Added: dists/squeeze-security/linux-2.6/debian/patches/bugfix/all/udf-treat-symlink-component-of-type-2-as.patch
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ dists/squeeze-security/linux-2.6/debian/patches/bugfix/all/udf-treat-symlink-component-of-type-2-as.patch	Sun Jun 14 17:27:25 2015	(r22739)
@@ -0,0 +1,42 @@
+From: Jan Kara <jack at suse.cz>
+Date: Mon, 12 Dec 2011 15:13:50 +0100
+Subject: udf: Treat symlink component of type 2 as /
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+Origin: https://git.kernel.org/linus/fef2e9f3301934773e4f1b3cc5c7bffb119346b8
+
+Currently, we ignore symlink component of type 2. But mkisofs and other OS'
+seem to treat it as / so do the same for compatibility.
+
+Reported-by: "Gábor S." <otnaccess at hotmail.com>
+Signed-off-by: Jan Kara <jack at suse.cz>
+---
+ fs/udf/symlink.c | 14 ++++++++++----
+ 1 file changed, 10 insertions(+), 4 deletions(-)
+
+diff --git a/fs/udf/symlink.c b/fs/udf/symlink.c
+index b1d4488..d7c6dbe 100644
+--- a/fs/udf/symlink.c
++++ b/fs/udf/symlink.c
+@@ -43,10 +43,16 @@ static void udf_pc_to_char(struct super_block *sb, unsigned char *from,
+ 		pc = (struct pathComponent *)(from + elen);
+ 		switch (pc->componentType) {
+ 		case 1:
+-			if (pc->lengthComponentIdent == 0) {
+-				p = to;
+-				*p++ = '/';
+-			}
++			/*
++			 * Symlink points to some place which should be agreed
++ 			 * upon between originator and receiver of the media. Ignore.
++			 */
++			if (pc->lengthComponentIdent > 0)
++				break;
++			/* Fall through */
++		case 2:
++			p = to;
++			*p++ = '/';
+ 			break;
+ 		case 3:
+ 			memcpy(p, "../", 3);

Added: dists/squeeze-security/linux-2.6/debian/patches/bugfix/all/udf-verify-i_size-when-loading-inode.patch
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ dists/squeeze-security/linux-2.6/debian/patches/bugfix/all/udf-verify-i_size-when-loading-inode.patch	Sun Jun 14 17:27:25 2015	(r22739)
@@ -0,0 +1,45 @@
+From: Jan Kara <jack at suse.cz>
+Date: Fri, 19 Dec 2014 12:03:53 +0100
+Subject: [1/4] udf: Verify i_size when loading inode
+Origin: https://git.kernel.org/linus/e159332b9af4b04d882dbcfe1bb0117f0a6d4b58
+
+Verify that inode size is sane when loading inode with data stored in
+ICB. Otherwise we may get confused later when working with the inode and
+inode size is too big.
+
+CC: stable at vger.kernel.org
+Reported-by: Carl Henrik Lunde <chlunde at ping.uio.no>
+Signed-off-by: Jan Kara <jack at suse.cz>
+[bwh: Backported to 3.2: on error, call make_bad_inode() then return]
+Signed-off-by: Ben Hutchings <ben at decadent.org.uk>
+---
+ fs/udf/inode.c | 18 ++++++++++++++++++
+ 1 file changed, 18 insertions(+)
+
+--- a/fs/udf/inode.c
++++ b/fs/udf/inode.c
+@@ -1286,6 +1286,24 @@ static void udf_fill_inode(struct inode *inode, struct buffer_head *bh)
+ 							iinfo->i_lenEAttr;
+ 	}
+ 
++	/* Sanity checks for files in ICB so that we don't get confused later */
++	if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
++		/*
++		 * For file in ICB data is stored in allocation descriptor
++		 * so sizes should match
++		 */
++		if (iinfo->i_lenAlloc != inode->i_size) {
++			make_bad_inode(inode);
++			return;
++		}
++		/* File in ICB has to fit in there... */
++		if (inode->i_size > inode->i_sb->s_blocksize -
++					udf_file_entry_alloc_offset(inode)) {
++			make_bad_inode(inode);
++			return;
++		}
++	}
++
+ 	switch (fe->icbTag.fileType) {
+ 	case ICBTAG_FILE_TYPE_DIRECTORY:
+ 		inode->i_op = &udf_dir_inode_operations;

Added: dists/squeeze-security/linux-2.6/debian/patches/bugfix/all/udf-verify-symlink-size-before-loading-it.patch
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ dists/squeeze-security/linux-2.6/debian/patches/bugfix/all/udf-verify-symlink-size-before-loading-it.patch	Sun Jun 14 17:27:25 2015	(r22739)
@@ -0,0 +1,64 @@
+From: Jan Kara <jack at suse.cz>
+Date: Fri, 19 Dec 2014 12:21:47 +0100
+Subject: [2/4] udf: Verify symlink size before loading it
+Origin: https://git.kernel.org/linus/a1d47b262952a45aae62bd49cfaf33dd76c11a2c
+
+UDF specification allows arbitrarily large symlinks. However we support
+only symlinks at most one block large. Check the length of the symlink
+so that we don't access memory beyond end of the symlink block.
+
+CC: stable at vger.kernel.org
+Reported-by: Carl Henrik Lunde <chlunde at gmail.com>
+Signed-off-by: Jan Kara <jack at suse.cz>
+bwh: Backported to 2.6.32: adjust context]
+---
+ fs/udf/symlink.c | 17 +++++++++++++----
+ 1 file changed, 13 insertions(+), 4 deletions(-)
+
+diff --git a/fs/udf/symlink.c b/fs/udf/symlink.c
+index 6fb7945..c3aa6fa 100644
+--- a/fs/udf/symlink.c
++++ b/fs/udf/symlink.c
+@@ -76,10 +76,16 @@ static int udf_symlink_filler(struct file *file, struct page *page)
+ 	struct inode *inode = page->mapping->host;
+ 	struct buffer_head *bh = NULL;
+ 	char *symlink;
+-	int err = -EIO;
++	int err;
+ 	char *p = kmap(page);
+ 	struct udf_inode_info *iinfo;
+ 
++	/* We don't support symlinks longer than one block */
++	if (inode->i_size > inode->i_sb->s_blocksize) {
++		err = -ENAMETOOLONG;
++		goto out_unmap;
++	}
++
+ 	lock_kernel();
+ 	iinfo = UDF_I(inode);
+ 	if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
+@@ -87,8 +93,10 @@ static int udf_symlink_filler(struct file *file, struct page *page)
+ 	} else {
+ 		bh = sb_bread(inode->i_sb, udf_block_map(inode, 0));
+ 
+-		if (!bh)
+-			goto out;
++		if (!bh) {
++			err = -EIO;
++			goto out_unlock_inode;
++		}
+ 
+ 		symlink = bh->b_data;
+ 	}
+@@ -102,9 +110,10 @@ static int udf_symlink_filler(struct file *file, struct page *page)
+ 	unlock_page(page);
+ 	return 0;
+ 
+-out:
++out_unlock_inode:
+ 	unlock_kernel();
+ 	SetPageError(page);
++out_unmap:
+ 	kunmap(page);
+ 	unlock_page(page);
+ 	return err;

Modified: dists/squeeze-security/linux-2.6/debian/patches/series/48squeeze12
==============================================================================
--- dists/squeeze-security/linux-2.6/debian/patches/series/48squeeze12	Thu Jun 11 21:47:07 2015	(r22738)
+++ dists/squeeze-security/linux-2.6/debian/patches/series/48squeeze12	Sun Jun 14 17:27:25 2015	(r22739)
@@ -20,3 +20,9 @@
 
 # Add upstream patches
 + bugfix/all/stable/2.6.32.66.patch
+
++ bugfix/all/udf-verify-i_size-when-loading-inode.patch
++ bugfix/all/udf-verify-symlink-size-before-loading-it.patch
++ bugfix/all/udf-treat-symlink-component-of-type-2-as.patch
++ bugfix/all/udf-check-path-length-when-reading-symlink.patch
++ bugfix/all/udf-check-component-length-before-reading-it.patch



More information about the Kernel-svn-changes mailing list