diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c index 1f6bc0559..06ccec9a6 100644 --- a/fs/ext4/inline.c +++ b/fs/ext4/inline.c @@ -188,6 +188,8 @@ 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 *end; + u32 size; if (!len) return 0; @@ -208,9 +210,29 @@ 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); - len = min_t(unsigned int, len, - (unsigned int)le32_to_cpu(entry->e_value_size)); + end = ITAIL(inode, raw_inode); + + if (unlikely((void *)entry + sizeof(*entry) > end)) { + EXT4_ERROR_INODE(inode, "corrupted inline data offset %u", + EXT4_I(inode)->i_inline_off); + return -EFSCORRUPTED; + } + + size = le32_to_cpu(entry->e_value_size); + if (unlikely(size > EXT4_XATTR_SIZE_MAX)) { + EXT4_ERROR_INODE(inode, "inline data value too large: %u", + size); + return -EFSCORRUPTED; + } + if (unlikely((void *)IFIRST(header) + + le16_to_cpu(entry->e_value_offs) + size > end)) { + EXT4_ERROR_INODE(inode, + "corrupted inline data: offset %u size %u", + le16_to_cpu(entry->e_value_offs), size); + return -EFSCORRUPTED; + } + len = min_t(unsigned int, len, size); memcpy(buffer, (void *)IFIRST(header) + le16_to_cpu(entry->e_value_offs), len); cp_len += len; @@ -527,7 +549,8 @@ static int ext4_read_inline_folio(struct inode *inode, struct folio *folio) ret = ext4_read_inline_data(inode, kaddr, len, &iloc); kaddr = folio_zero_tail(folio, len, kaddr + len); kunmap_local(kaddr); - folio_mark_uptodate(folio); + if (ret >= 0) + folio_mark_uptodate(folio); brelse(iloc.bh); out: