diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c index 1f6bc05593df..f807598e96c9 100644 --- a/fs/ext4/inline.c +++ b/fs/ext4/inline.c @@ -188,16 +188,44 @@ static int ext4_read_inline_data(struct inode *inode, void *buffer, struct ext4_xattr_ibody_header *header; int cp_len = 0; struct ext4_inode *raw_inode; + void *xattr_start, *xattr_end; + u32 value_offs; + void *source; if (!len) return 0; + printk(KERN_ERR "ext4_read_inline_data: START - inode=%lu, len=%u\n", + inode->i_ino, len); + + /* VALIDATION 1: Check iloc and buffer validity */ + if (!iloc) { + printk(KERN_ERR "ext4_read_inline_data: ERROR - iloc is NULL\n"); + return -EFSCORRUPTED; + } + + if (!iloc->bh) { + printk(KERN_ERR "ext4_read_inline_data: ERROR - iloc->bh is NULL\n"); + return -EFSCORRUPTED; + } + + printk(KERN_ERR "ext4_read_inline_data: iloc->bh=%px, refcount=%d\n", + iloc->bh, atomic_read(&iloc->bh->b_count)); + + if (!buffer_uptodate(iloc->bh)) { + printk(KERN_ERR "ext4_read_inline_data: ERROR - buffer not uptodate\n"); + return -EIO; + } + BUG_ON(len > EXT4_I(inode)->i_inline_size); cp_len = min_t(unsigned int, len, EXT4_MIN_INLINE_DATA_SIZE); raw_inode = ext4_raw_inode(iloc); + printk(KERN_ERR "ext4_read_inline_data: raw_inode=%px\n", raw_inode); + memcpy(buffer, (void *)(raw_inode->i_block), cp_len); + printk(KERN_ERR "ext4_read_inline_data: copied %d bytes from i_block\n", cp_len); len -= cp_len; buffer += cp_len; @@ -208,17 +236,77 @@ static int ext4_read_inline_data(struct inode *inode, void *buffer, header = IHDR(inode, raw_inode); entry = (struct ext4_xattr_entry *)((void *)raw_inode + EXT4_I(inode)->i_inline_off); + + printk(KERN_ERR "ext4_read_inline_data: header=%px, entry=%px, i_inline_off=%u\n", + header, entry, EXT4_I(inode)->i_inline_off); + + /* VALIDATION 2: Calculate and check bounds */ + xattr_start = (void *)IFIRST(header); + xattr_end = (void *)raw_inode + EXT4_INODE_SIZE(inode->i_sb); + + printk(KERN_ERR "ext4_read_inline_data: BOUNDS - xattr_start=%px, xattr_end=%px, size=%ld\n", + xattr_start, xattr_end, (long)(xattr_end - xattr_start)); + + /* VALIDATION 3: Check entry pointer is within inode */ + if ((void *)entry < (void *)raw_inode || (void *)entry >= xattr_end) { + printk(KERN_ERR "ext4_read_inline_data: ERROR - entry %px outside inode bounds [%px-%px]\n", + entry, raw_inode, xattr_end); + ext4_error_inode(inode, __func__, __LINE__, 0, + "xattr entry outside inode bounds"); + return -EFSCORRUPTED; + } + + /* VALIDATION 4: Check e_value_offs */ + value_offs = le16_to_cpu(entry->e_value_offs); + printk(KERN_ERR "ext4_read_inline_data: e_value_offs=%u, e_value_size=%u\n", + value_offs, le32_to_cpu(entry->e_value_size)); + + if (value_offs > (xattr_end - xattr_start)) { + printk(KERN_ERR "ext4_read_inline_data: ERROR - value_offs %u exceeds xattr bounds %ld\n", + value_offs, (long)(xattr_end - xattr_start)); + ext4_error_inode(inode, __func__, __LINE__, 0, + "corrupt xattr offset: %u", value_offs); + return -EFSCORRUPTED; + } + len = min_t(unsigned int, len, (unsigned int)le32_to_cpu(entry->e_value_size)); - memcpy(buffer, - (void *)IFIRST(header) + le16_to_cpu(entry->e_value_offs), len); + /* VALIDATION 5: Check final read bounds */ + source = xattr_start + value_offs; + + printk(KERN_ERR "ext4_read_inline_data: FINAL CHECK - source=%px, len=%u, end=%px\n", + source, len, source + len); + + if (source < (void *)raw_inode) { + printk(KERN_ERR "ext4_read_inline_data: ERROR - source %px before inode start %px\n", + source, raw_inode); + return -EFSCORRUPTED; + } + + if ((source + len) > xattr_end) { + printk(KERN_ERR "ext4_read_inline_data: ERROR - read [%px-%px] exceeds inode end %px\n", + source, source + len, xattr_end); + ext4_error_inode(inode, __func__, __LINE__, 0, + "xattr read exceeds inode bounds"); + return -EFSCORRUPTED; + } + + printk(KERN_ERR "ext4_read_inline_data: ABOUT TO MEMCPY - source=%px, dest=%px, len=%u\n", + source, buffer, len); + + memcpy(buffer, source, len); + + printk(KERN_ERR "ext4_read_inline_data: memcpy SUCCESS\n"); + cp_len += len; out: + printk(KERN_ERR "ext4_read_inline_data: DONE - copied %d bytes total\n", cp_len); return cp_len; } + /* * write the buffer to the inline inode. * If 'create' is set, we don't need to do the extra copy in the xattr