Normally the extended attributes in the inode body would have been checked when the inode is first opened, but if someone is writing to the block device while the file system is mounted, it's possible for the inode table to get corrupted. Add bounds checking to avoid reading beyond the end of allocated memory if this happens.
Unified · Split
| @@ -34,6 +34,7 @@ static int get_max_inline_xattr_value_size(struct inode *inode, · 33 unmodified lines | ||
| 34 | 34 | struct ext4_xattr_ibody_header *header; |
| 35 | 35 | struct ext4_xattr_entry *entry; |
| 36 | 36 | struct ext4_inode *raw_inode; |
| 37 | + void *end; | |
| 37 | 38 | int free, min_offs; |
| 38 | 39 | |
| 39 | 40 | if (!EXT4_INODE_HAS_XATTR_SPACE(inode)) |
| @@ -57,14 +58,23 @@ static int get_max_inline_xattr_value_size(struct inode *inode, · 17 unmodified lines | ||
| 57 | 58 | raw_inode = ext4_raw_inode(iloc); |
| 58 | 59 | header = IHDR(inode, raw_inode); |
| 59 | 60 | entry = IFIRST(header); |
| 61 | + end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size; | |
| 60 | 62 | |
| 61 | 63 | /* Compute min_offs. */ |
| 62 | − for (; !IS_LAST_ENTRY(entry); entry = EXT4_XATTR_NEXT(entry)) { | |
| 64 | + while (!IS_LAST_ENTRY(entry)) { | |
| 65 | + void *next = EXT4_XATTR_NEXT(entry); | |
| 66 | + | |
| 67 | + if (next >= end) { | |
| 68 | + EXT4_ERROR_INODE(inode, | |
| 69 | + "corrupt xattr in inline inode"); | |
| 70 | + return 0; | |
| 71 | + } | |
| 63 | 72 | if (!entry->e_value_inum && entry->e_value_size) { |
| 64 | 73 | size_t offs = le16_to_cpu(entry->e_value_offs); |
| 65 | 74 | if (offs < min_offs) |
| 66 | 75 | min_offs = offs; |
| 67 | 76 | } |
| 77 | + entry = next; | |
| 68 | 78 | } |
| 69 | 79 | free = min_offs - |
| 70 | 80 | ((void *)entry - (void *)IFIRST(header)) - sizeof(__u32); |