| From: Theodore Ts'o <tytso@mit.edu> |
| Date: Fri, 30 Mar 2018 20:00:56 -0400 |
| Subject: ext4: add bounds checking to ext4_xattr_find_entry() |
| |
| commit 9496005d6ca4cf8f5ee8f828165a8956872dc59d upstream. |
| |
| Add some paranoia checks to make sure we don't stray beyond the end of |
| the valid memory region containing ext4 xattr entries while we are |
| scanning for a match. |
| |
| Also rename the function to xattr_find_entry() since it is static and |
| thus only used in fs/ext4/xattr.c |
| |
| Signed-off-by: Theodore Ts'o <tytso@mit.edu> |
| [bwh: Backported to 3.16: |
| - Keep passing an explicit size to xattr_find_entry() |
| - s/EFSCORRUPTED/EIO/]] |
| Signed-off-by: Ben Hutchings <ben@decadent.org.uk> |
| --- |
| fs/ext4/xattr.c | 28 +++++++++++++++++----------- |
| 1 file changed, 17 insertions(+), 11 deletions(-) |
| |
| --- a/fs/ext4/xattr.c |
| +++ b/fs/ext4/xattr.c |
| @@ -240,18 +240,23 @@ ext4_xattr_check_entry(struct ext4_xattr |
| } |
| |
| static int |
| -ext4_xattr_find_entry(struct ext4_xattr_entry **pentry, int name_index, |
| - const char *name, size_t size, int sorted) |
| +xattr_find_entry(struct inode *inode, struct ext4_xattr_entry **pentry, |
| + void *end, int name_index, const char *name, size_t size, |
| + int sorted) |
| { |
| - struct ext4_xattr_entry *entry; |
| + struct ext4_xattr_entry *entry, *next; |
| size_t name_len; |
| int cmp = 1; |
| |
| if (name == NULL) |
| return -EINVAL; |
| name_len = strlen(name); |
| - entry = *pentry; |
| - for (; !IS_LAST_ENTRY(entry); entry = EXT4_XATTR_NEXT(entry)) { |
| + for (entry = *pentry; !IS_LAST_ENTRY(entry); entry = next) { |
| + next = EXT4_XATTR_NEXT(entry); |
| + if ((void *) next >= end) { |
| + EXT4_ERROR_INODE(inode, "corrupted xattr entries"); |
| + return -EIO; |
| + } |
| cmp = name_index - entry->e_name_index; |
| if (!cmp) |
| cmp = name_len - entry->e_name_len; |
| @@ -273,6 +278,7 @@ ext4_xattr_block_get(struct inode *inode |
| struct buffer_head *bh = NULL; |
| struct ext4_xattr_entry *entry; |
| size_t size; |
| + void *end; |
| int error; |
| struct mb_cache *ext4_mb_cache = EXT4_GET_MB_CACHE(inode); |
| |
| @@ -298,7 +304,9 @@ bad_block: |
| } |
| ext4_xattr_cache_insert(ext4_mb_cache, bh); |
| entry = BFIRST(bh); |
| - error = ext4_xattr_find_entry(&entry, name_index, name, bh->b_size, 1); |
| + end = bh->b_data + bh->b_size; |
| + error = xattr_find_entry(inode, &entry, end, name_index, name, |
| + bh->b_size, 1); |
| if (error == -EIO) |
| goto bad_block; |
| if (error) |
| @@ -342,8 +350,8 @@ ext4_xattr_ibody_get(struct inode *inode |
| error = ext4_xattr_check_names(entry, end, entry); |
| if (error) |
| goto cleanup; |
| - error = ext4_xattr_find_entry(&entry, name_index, name, |
| - end - (void *)entry, 0); |
| + error = xattr_find_entry(inode, &entry, end, name_index, name, |
| + end - (void *)entry, 0); |
| if (error) |
| goto cleanup; |
| size = le32_to_cpu(entry->e_value_size); |
| @@ -761,8 +769,9 @@ ext4_xattr_block_find(struct inode *inod |
| bs->s.first = BFIRST(bs->bh); |
| bs->s.end = bs->bh->b_data + bs->bh->b_size; |
| bs->s.here = bs->s.first; |
| - error = ext4_xattr_find_entry(&bs->s.here, i->name_index, |
| - i->name, bs->bh->b_size, 1); |
| + error = xattr_find_entry(inode, &bs->s.here, bs->s.end, |
| + i->name_index, i->name, |
| + bs->bh->b_size, 1); |
| if (error && error != -ENODATA) |
| goto cleanup; |
| bs->s.not_found = error; |
| @@ -1007,9 +1016,9 @@ int ext4_xattr_ibody_find(struct inode * |
| if (error) |
| return error; |
| /* Find the named attribute. */ |
| - error = ext4_xattr_find_entry(&is->s.here, i->name_index, |
| - i->name, is->s.end - |
| - (void *)is->s.base, 0); |
| + error = xattr_find_entry(inode, &is->s.here, is->s.end, |
| + i->name_index, i->name, |
| + is->s.end - (void *)is->s.base, 0); |
| if (error && error != -ENODATA) |
| return error; |
| is->s.not_found = error; |