Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: aa75f4d3daae ("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>

authored by

Eric Biggers and committed by
Theodore Ts'o
64b4a25c 594bc43b

+20 -20
+19 -19
fs/ext4/fast_commit.c
··· 1991 1991 kfree(sbi->s_fc_replay_state.fc_modified_inodes); 1992 1992 } 1993 1993 1994 - static inline bool ext4_fc_tag_len_isvalid(struct ext4_fc_tl *tl, 1995 - u8 *val, u8 *end) 1994 + static bool ext4_fc_value_len_isvalid(struct ext4_sb_info *sbi, 1995 + int tag, int len) 1996 1996 { 1997 - if (val + tl->fc_len > end) 1998 - return false; 1999 - 2000 - /* Here only check ADD_RANGE/TAIL/HEAD which will read data when do 2001 - * journal rescan before do CRC check. Other tags length check will 2002 - * rely on CRC check. 2003 - */ 2004 - switch (tl->fc_tag) { 1997 + switch (tag) { 2005 1998 case EXT4_FC_TAG_ADD_RANGE: 2006 - return (sizeof(struct ext4_fc_add_range) == tl->fc_len); 2007 - case EXT4_FC_TAG_TAIL: 2008 - return (sizeof(struct ext4_fc_tail) <= tl->fc_len); 2009 - case EXT4_FC_TAG_HEAD: 2010 - return (sizeof(struct ext4_fc_head) == tl->fc_len); 1999 + return len == sizeof(struct ext4_fc_add_range); 2011 2000 case EXT4_FC_TAG_DEL_RANGE: 2001 + return len == sizeof(struct ext4_fc_del_range); 2002 + case EXT4_FC_TAG_CREAT: 2012 2003 case EXT4_FC_TAG_LINK: 2013 2004 case EXT4_FC_TAG_UNLINK: 2014 - case EXT4_FC_TAG_CREAT: 2005 + len -= sizeof(struct ext4_fc_dentry_info); 2006 + return len >= 1 && len <= EXT4_NAME_LEN; 2015 2007 case EXT4_FC_TAG_INODE: 2008 + len -= sizeof(struct ext4_fc_inode); 2009 + return len >= EXT4_GOOD_OLD_INODE_SIZE && 2010 + len <= sbi->s_inode_size; 2016 2011 case EXT4_FC_TAG_PAD: 2017 - default: 2018 - return true; 2012 + return true; /* padding can have any length */ 2013 + case EXT4_FC_TAG_TAIL: 2014 + return len >= sizeof(struct ext4_fc_tail); 2015 + case EXT4_FC_TAG_HEAD: 2016 + return len == sizeof(struct ext4_fc_head); 2019 2017 } 2018 + return false; 2020 2019 } 2021 2020 2022 2021 /* ··· 2078 2079 cur = cur + EXT4_FC_TAG_BASE_LEN + tl.fc_len) { 2079 2080 ext4_fc_get_tl(&tl, cur); 2080 2081 val = cur + EXT4_FC_TAG_BASE_LEN; 2081 - if (!ext4_fc_tag_len_isvalid(&tl, val, end)) { 2082 + if (tl.fc_len > end - val || 2083 + !ext4_fc_value_len_isvalid(sbi, tl.fc_tag, tl.fc_len)) { 2082 2084 ret = state->fc_replay_num_tags ? 2083 2085 JBD2_FC_REPLAY_STOP : -ECANCELED; 2084 2086 goto out_err;
+1 -1
fs/ext4/fast_commit.h
··· 58 58 __u8 fc_dname[]; 59 59 }; 60 60 61 - /* Value structure for EXT4_FC_TAG_INODE and EXT4_FC_TAG_INODE_PARTIAL. */ 61 + /* Value structure for EXT4_FC_TAG_INODE. */ 62 62 struct ext4_fc_inode { 63 63 __le32 fc_ino; 64 64 __u8 fc_raw_inode[];