From 927fdf89a9febcdfa797a65733a0ae3ebf3aeeb6 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Tue, 16 Sep 2014 14:41:03 -0400 Subject: [PATCH] add patch check-EA-value-offset-when-loading --- check-EA-value-offset-when-loading | 101 +++++++++++++++++++++++++++++++++++++ series | 2 + timestamps | 9 ++-- 3 files changed, 108 insertions(+), 4 deletions(-) create mode 100644 check-EA-value-offset-when-loading diff --git a/check-EA-value-offset-when-loading b/check-EA-value-offset-when-loading new file mode 100644 index 00000000..7b8f2734 --- /dev/null +++ b/check-EA-value-offset-when-loading @@ -0,0 +1,101 @@ +ext4: check EA value offset when loading + +From: "Darrick J. Wong" + +When loading extended attributes, check each entry's value offset to +make sure it doesn't collide with the entries. + +Without this check it is easy to crash the kernel by mounting a +malicious FS containing a file with an EA wherein e_value_offs = 0 and +e_value_size > 0 and then deleting the EA, which corrupts the name +list. + +(See the f_ea_value_crash test's FS image in e2fsprogs for an example.) + +Signed-off-by: Darrick J. Wong +Signed-off-by: Theodore Ts'o +Cc: stable@vger.kernel.org +--- + fs/ext4/xattr.c | 34 +++++++++++++++++++++++++--------- + 1 file changed, 25 insertions(+), 9 deletions(-) + + +diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c +index e738733..c11738e 100644 +--- a/fs/ext4/xattr.c ++++ b/fs/ext4/xattr.c +@@ -189,15 +189,29 @@ ext4_listxattr(struct dentry *dentry, char *buffer, size_t size) + return ext4_xattr_list(dentry, buffer, size); + } + +-static int +-ext4_xattr_check_names(struct ext4_xattr_entry *entry, void *end) ++int ++ext4_xattr_check_names(struct ext4_xattr_entry *entry, void *end, ++ void *value_start) + { +- while (!IS_LAST_ENTRY(entry)) { +- struct ext4_xattr_entry *next = EXT4_XATTR_NEXT(entry); ++ struct ext4_xattr_entry *e = entry; ++ ++ while (!IS_LAST_ENTRY(e)) { ++ struct ext4_xattr_entry *next = EXT4_XATTR_NEXT(e); + if ((void *)next >= end) + return -EIO; +- entry = next; ++ e = next; + } ++ ++ while (!IS_LAST_ENTRY(entry)) { ++ if (entry->e_value_size != 0 && ++ (value_start + le16_to_cpu(entry->e_value_offs) < ++ (void *)e + sizeof(__u32) || ++ value_start + le16_to_cpu(entry->e_value_offs) + ++ le32_to_cpu(entry->e_value_size) > end)) ++ return -EIO; ++ entry = EXT4_XATTR_NEXT(entry); ++ } ++ + return 0; + } + +@@ -214,7 +228,8 @@ ext4_xattr_check_block(struct inode *inode, struct buffer_head *bh) + return -EIO; + if (!ext4_xattr_block_csum_verify(inode, bh->b_blocknr, BHDR(bh))) + return -EIO; +- error = ext4_xattr_check_names(BFIRST(bh), bh->b_data + bh->b_size); ++ error = ext4_xattr_check_names(BFIRST(bh), bh->b_data + bh->b_size, ++ bh->b_data); + if (!error) + set_buffer_verified(bh); + return error; +@@ -331,7 +346,7 @@ ext4_xattr_ibody_get(struct inode *inode, int name_index, const char *name, + header = IHDR(inode, raw_inode); + entry = IFIRST(header); + end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size; +- error = ext4_xattr_check_names(entry, end); ++ error = ext4_xattr_check_names(entry, end, entry); + if (error) + goto cleanup; + error = ext4_xattr_find_entry(&entry, name_index, name, +@@ -463,7 +478,7 @@ ext4_xattr_ibody_list(struct dentry *dentry, char *buffer, size_t buffer_size) + raw_inode = ext4_raw_inode(&iloc); + header = IHDR(inode, raw_inode); + end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size; +- error = ext4_xattr_check_names(IFIRST(header), end); ++ error = ext4_xattr_check_names(IFIRST(header), end, IFIRST(header)); + if (error) + goto cleanup; + error = ext4_xattr_list_entries(dentry, IFIRST(header), +@@ -986,7 +1001,8 @@ int ext4_xattr_ibody_find(struct inode *inode, struct ext4_xattr_info *i, + is->s.here = is->s.first; + is->s.end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size; + if (ext4_test_inode_state(inode, EXT4_STATE_XATTR)) { +- error = ext4_xattr_check_names(IFIRST(header), is->s.end); ++ error = ext4_xattr_check_names(IFIRST(header), is->s.end, ++ IFIRST(header)); + if (error) + return error; + /* Find the named attribute. */ + + diff --git a/series b/series index a5ef07ce..3d770569 100644 --- a/series +++ b/series @@ -50,6 +50,8 @@ fix-journal-checksum-feature-flag-handling validate-external-journal-superblock-checksum dont-keep-using-page-if-inline-conversion-fails +check-EA-value-offset-when-loading + ########################################## # unstable patches #################################################### diff --git a/timestamps b/timestamps index ba77c3a4..f4240adf 100755 --- a/timestamps +++ b/timestamps @@ -67,7 +67,6 @@ touch -d @1410449278 provide-separate-oeprations-for-sysfs-feature-files touch -d @1410449901 fix-journal-checksum-feature-flag-handling touch -d @1410450276 validate-external-journal-superblock-checksum touch -d @1410450312 dont-keep-using-page-if-inline-conversion-fails -touch -d @1410450372 stable-boundary touch -d @1410450432 stable-boundary-undo.patch touch -d @1410450492 add-callback-support-for-bio-read-completion touch -d @1410450552 add-ext4-encryption-facilities @@ -80,7 +79,9 @@ touch -d @1410450762 remove-extra-unlock-page touch -d @1410450763 fix-crypto-warnings touch -d @1410450764 add-dummy-key-mount-option touch -d @1410450767 cache-extent-hole-in-extent-status-tree-for-ext4_da_map_blocks -touch -d @1410450771 status touch -d @1410450990 enable-delayed-allocation -touch -d @1410451007 series -touch -d @1410451010 timestamps +touch -d @1410892476 series +touch -d @1410892499 check-EA-value-offset-when-loading +touch -d @1410892559 stable-boundary +touch -d @1410892796 status +touch -d @1410892852 timestamps -- 2.11.4.GIT