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:
Eric Biggers 2022-11-06 14:48:38 -08:00 committed by Theodore Ts'o
parent 594bc43b41
commit 64b4a25c3d
2 changed files with 20 additions and 20 deletions

View File

@ -1991,32 +1991,31 @@ void ext4_fc_replay_cleanup(struct super_block *sb)
kfree(sbi->s_fc_replay_state.fc_modified_inodes); kfree(sbi->s_fc_replay_state.fc_modified_inodes);
} }
static inline bool ext4_fc_tag_len_isvalid(struct ext4_fc_tl *tl, static bool ext4_fc_value_len_isvalid(struct ext4_sb_info *sbi,
u8 *val, u8 *end) int tag, int len)
{ {
if (val + tl->fc_len > end) switch (tag) {
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) {
case EXT4_FC_TAG_ADD_RANGE: case EXT4_FC_TAG_ADD_RANGE:
return (sizeof(struct ext4_fc_add_range) == tl->fc_len); return len == sizeof(struct ext4_fc_add_range);
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);
case EXT4_FC_TAG_DEL_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_LINK:
case EXT4_FC_TAG_UNLINK: 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: 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: case EXT4_FC_TAG_PAD:
default: return true; /* padding can have any length */
return true; 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) { cur = cur + EXT4_FC_TAG_BASE_LEN + tl.fc_len) {
ext4_fc_get_tl(&tl, cur); ext4_fc_get_tl(&tl, cur);
val = cur + EXT4_FC_TAG_BASE_LEN; 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 ? ret = state->fc_replay_num_tags ?
JBD2_FC_REPLAY_STOP : -ECANCELED; JBD2_FC_REPLAY_STOP : -ECANCELED;
goto out_err; goto out_err;

View File

@ -58,7 +58,7 @@ struct ext4_fc_dentry_info {
__u8 fc_dname[]; __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 { struct ext4_fc_inode {
__le32 fc_ino; __le32 fc_ino;
__u8 fc_raw_inode[]; __u8 fc_raw_inode[];