ext4: add missing validation of fast-commit record lengths
Validate the inode and filename lengths in fast-commit journal records
so that a malicious fast-commit journal cannot cause a crash by having
invalid values for these. Also validate EXT4_FC_TAG_DEL_RANGE.
Fixes: aa75f4d3da
("ext4: main fast-commit commit path")
Cc: <stable@vger.kernel.org> # v5.10+
Signed-off-by: Eric Biggers <ebiggers@google.com>
Link: https://lore.kernel.org/r/20221106224841.279231-5-ebiggers@kernel.org
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
This commit is contained in:
Родитель
594bc43b41
Коммит
64b4a25c3d
|
@ -1991,32 +1991,31 @@ void ext4_fc_replay_cleanup(struct super_block *sb)
|
|||
kfree(sbi->s_fc_replay_state.fc_modified_inodes);
|
||||
}
|
||||
|
||||
static inline bool ext4_fc_tag_len_isvalid(struct ext4_fc_tl *tl,
|
||||
u8 *val, u8 *end)
|
||||
static bool ext4_fc_value_len_isvalid(struct ext4_sb_info *sbi,
|
||||
int tag, int len)
|
||||
{
|
||||
if (val + tl->fc_len > end)
|
||||
return false;
|
||||
|
||||
/* Here only check ADD_RANGE/TAIL/HEAD which will read data when do
|
||||
* journal rescan before do CRC check. Other tags length check will
|
||||
* rely on CRC check.
|
||||
*/
|
||||
switch (tl->fc_tag) {
|
||||
switch (tag) {
|
||||
case EXT4_FC_TAG_ADD_RANGE:
|
||||
return (sizeof(struct ext4_fc_add_range) == tl->fc_len);
|
||||
case EXT4_FC_TAG_TAIL:
|
||||
return (sizeof(struct ext4_fc_tail) <= tl->fc_len);
|
||||
case EXT4_FC_TAG_HEAD:
|
||||
return (sizeof(struct ext4_fc_head) == tl->fc_len);
|
||||
return len == sizeof(struct ext4_fc_add_range);
|
||||
case EXT4_FC_TAG_DEL_RANGE:
|
||||
return len == sizeof(struct ext4_fc_del_range);
|
||||
case EXT4_FC_TAG_CREAT:
|
||||
case EXT4_FC_TAG_LINK:
|
||||
case EXT4_FC_TAG_UNLINK:
|
||||
case EXT4_FC_TAG_CREAT:
|
||||
len -= sizeof(struct ext4_fc_dentry_info);
|
||||
return len >= 1 && len <= EXT4_NAME_LEN;
|
||||
case EXT4_FC_TAG_INODE:
|
||||
len -= sizeof(struct ext4_fc_inode);
|
||||
return len >= EXT4_GOOD_OLD_INODE_SIZE &&
|
||||
len <= sbi->s_inode_size;
|
||||
case EXT4_FC_TAG_PAD:
|
||||
default:
|
||||
return true;
|
||||
return true; /* padding can have any length */
|
||||
case EXT4_FC_TAG_TAIL:
|
||||
return len >= sizeof(struct ext4_fc_tail);
|
||||
case EXT4_FC_TAG_HEAD:
|
||||
return len == sizeof(struct ext4_fc_head);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2079,7 +2078,8 @@ static int ext4_fc_replay_scan(journal_t *journal,
|
|||
cur = cur + EXT4_FC_TAG_BASE_LEN + tl.fc_len) {
|
||||
ext4_fc_get_tl(&tl, cur);
|
||||
val = cur + EXT4_FC_TAG_BASE_LEN;
|
||||
if (!ext4_fc_tag_len_isvalid(&tl, val, end)) {
|
||||
if (tl.fc_len > end - val ||
|
||||
!ext4_fc_value_len_isvalid(sbi, tl.fc_tag, tl.fc_len)) {
|
||||
ret = state->fc_replay_num_tags ?
|
||||
JBD2_FC_REPLAY_STOP : -ECANCELED;
|
||||
goto out_err;
|
||||
|
|
|
@ -58,7 +58,7 @@ struct ext4_fc_dentry_info {
|
|||
__u8 fc_dname[];
|
||||
};
|
||||
|
||||
/* Value structure for EXT4_FC_TAG_INODE and EXT4_FC_TAG_INODE_PARTIAL. */
|
||||
/* Value structure for EXT4_FC_TAG_INODE. */
|
||||
struct ext4_fc_inode {
|
||||
__le32 fc_ino;
|
||||
__u8 fc_raw_inode[];
|
||||
|
|
Загрузка…
Ссылка в новой задаче