Skip to content

Commit 54dd0e0

Browse files
committed
ext4: add extra checks to ext4_xattr_block_get()
Add explicit checks in ext4_xattr_block_get() just in case the e_value_offs and e_value_size fields in the the xattr block are corrupted in memory after the buffer_verified bit is set on the xattr block. Signed-off-by: Theodore Ts'o <[email protected]> Cc: [email protected]
1 parent 9496005 commit 54dd0e0

File tree

2 files changed

+30
-7
lines changed

2 files changed

+30
-7
lines changed

fs/ext4/xattr.c

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ ext4_xattr_check_entries(struct ext4_xattr_entry *entry, void *end,
197197
while (!IS_LAST_ENTRY(entry)) {
198198
u32 size = le32_to_cpu(entry->e_value_size);
199199

200-
if (size > INT_MAX)
200+
if (size > EXT4_XATTR_SIZE_MAX)
201201
return -EFSCORRUPTED;
202202

203203
if (size != 0 && entry->e_value_inum == 0) {
@@ -540,8 +540,10 @@ ext4_xattr_block_get(struct inode *inode, int name_index, const char *name,
540540
if (error)
541541
goto cleanup;
542542
size = le32_to_cpu(entry->e_value_size);
543+
error = -ERANGE;
544+
if (unlikely(size > EXT4_XATTR_SIZE_MAX))
545+
goto cleanup;
543546
if (buffer) {
544-
error = -ERANGE;
545547
if (size > buffer_size)
546548
goto cleanup;
547549
if (entry->e_value_inum) {
@@ -550,8 +552,12 @@ ext4_xattr_block_get(struct inode *inode, int name_index, const char *name,
550552
if (error)
551553
goto cleanup;
552554
} else {
553-
memcpy(buffer, bh->b_data +
554-
le16_to_cpu(entry->e_value_offs), size);
555+
u16 offset = le16_to_cpu(entry->e_value_offs);
556+
void *p = bh->b_data + offset;
557+
558+
if (unlikely(p + size > end))
559+
goto cleanup;
560+
memcpy(buffer, p, size);
555561
}
556562
}
557563
error = size;
@@ -589,8 +595,10 @@ ext4_xattr_ibody_get(struct inode *inode, int name_index, const char *name,
589595
if (error)
590596
goto cleanup;
591597
size = le32_to_cpu(entry->e_value_size);
598+
error = -ERANGE;
599+
if (unlikely(size > EXT4_XATTR_SIZE_MAX))
600+
goto cleanup;
592601
if (buffer) {
593-
error = -ERANGE;
594602
if (size > buffer_size)
595603
goto cleanup;
596604
if (entry->e_value_inum) {
@@ -599,8 +607,12 @@ ext4_xattr_ibody_get(struct inode *inode, int name_index, const char *name,
599607
if (error)
600608
goto cleanup;
601609
} else {
602-
memcpy(buffer, (void *)IFIRST(header) +
603-
le16_to_cpu(entry->e_value_offs), size);
610+
u16 offset = le16_to_cpu(entry->e_value_offs);
611+
void *p = (void *)IFIRST(header) + offset;
612+
613+
if (unlikely(p + size > end))
614+
goto cleanup;
615+
memcpy(buffer, p, size);
604616
}
605617
}
606618
error = size;

fs/ext4/xattr.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,17 @@ struct ext4_xattr_entry {
7070
EXT4_I(inode)->i_extra_isize))
7171
#define IFIRST(hdr) ((struct ext4_xattr_entry *)((hdr)+1))
7272

73+
/*
74+
* XATTR_SIZE_MAX is currently 64k, but for the purposes of checking
75+
* for file system consistency errors, we use a somewhat bigger value.
76+
* This allows XATTR_SIZE_MAX to grow in the future, but by using this
77+
* instead of INT_MAX for certain consistency checks, we don't need to
78+
* worry about arithmetic overflows. (Actually XATTR_SIZE_MAX is
79+
* defined in include/uapi/linux/limits.h, so changing it is going
80+
* not going to be trivial....)
81+
*/
82+
#define EXT4_XATTR_SIZE_MAX (1 << 24)
83+
7384
/*
7485
* The minimum size of EA value when you start storing it in an external inode
7586
* size of block - size of header - size of 1 entry - 4 null bytes

0 commit comments

Comments
 (0)