fix potential out of bound read in ext4_fc_replay_scan()
cover fix potential out of bound read in ext4_fc_replay_scan()
Ye Bin (3):
ext4: introduce EXT4_FC_TAG_BASE_LEN helper
ext4: factor out ext4_fc_get_tl()
ext4: fix potential out of bound read in ext4_fc_replay_scan()
fs/ext4/fast_commit.c | 122 ++++++++++++++++++++++++++++-------------- fs/ext4/fast_commit.h | 3 ++ 2 files changed, 84 insertions(+), 41 deletions(-)
Signature · 1 line
2.31.1
PATCH 1/3 ext4: introduce EXT4_FC_TAG_BASE_LEN helper
Introduce EXT4_FC_TAG_BASE_LEN helper for calculate length of struct ext4_fc_tl.
--- fs/ext4/fast_commit.c | 54 ++++++++++++++++++++++--------------------- fs/ext4/fast_commit.h | 3 +++ 2 files changed, 31 insertions(+), 26 deletions(-)
fs/ext4/fast_commit.c +28 -26
@@ -702,10 +702,10 @@ static u8 *ext4_fc_reserve_space(struct super_block *sb, int len, u32 *crc)* After allocating len, we should have space at least for a 0 byte * padding. */ -if (len +sizeof (struct ext4_fc_tl ) > bsize) +if (len +EXT4_FC_TAG_BASE_LEN > bsize)return NULL ; -if (bsize - off -1 > len +sizeof (struct ext4_fc_tl )) { +if (bsize - off -1 > len +EXT4_FC_TAG_BASE_LEN ) {/* * Only allocate from current buffer if we have enough space for * this request AND we have space to add a zero byte padding.
@@ -722,10 +722,10 @@ static u8 *ext4_fc_reserve_space(struct super_block *sb, int len, u32 *crc)/* Need to add PAD tag */ tl = (struct ext4_fc_tl *)(sbi->s_fc_bh->b_data + off); tl->fc_tag =cpu_to_le16 (EXT4_FC_TAG_PAD ); - pad_len = bsize - off -1 -sizeof (struct ext4_fc_tl ); + pad_len = bsize - off -1 -EXT4_FC_TAG_BASE_LEN ; tl->fc_len =cpu_to_le16 (pad_len);if (crc) - *crc =ext4_chksum (sbi, *crc, tl,sizeof (*tl)); + *crc =ext4_chksum (sbi, *crc, tl,EXT4_FC_TAG_BASE_LEN );if (pad_len >0 )ext4_fc_memzero (sb, tl +1 , pad_len, crc);ext4_fc_submit_bh (sb, false);
@@ -767,7 +767,7 @@ static int ext4_fc_write_tail(struct super_block *sb, u32 crc)* ext4_fc_reserve_space takes care of allocating an extra block if * there's no enough space on this block for accommodating this tail. */ - dst =ext4_fc_reserve_space (sb,sizeof (tl) +sizeof (tail), &crc); + dst =ext4_fc_reserve_space (sb,EXT4_FC_TAG_BASE_LEN +sizeof (tail), &crc);if (!dst)return -ENOSPC ;
@@ -777,8 +777,8 @@ static int ext4_fc_write_tail(struct super_block *sb, u32 crc) tl.fc_len =cpu_to_le16 (bsize - off -1 +sizeof (struct ext4_fc_tail )); sbi->s_fc_bytes =round_up (sbi->s_fc_bytes, bsize); -ext4_fc_memcpy (sb, dst, &tl,sizeof (tl), &crc); - dst +=sizeof (tl); +ext4_fc_memcpy (sb, dst, &tl,EXT4_FC_TAG_BASE_LEN , &crc); + dst +=EXT4_FC_TAG_BASE_LEN ; tail.fc_tid =cpu_to_le32 (sbi->s_journal->j_running_transaction->t_tid);ext4_fc_memcpy (sb, dst, &tail.fc_tid,sizeof (tail.fc_tid), &crc); dst +=sizeof (tail.fc_tid);
@@ -800,15 +800,15 @@ static bool ext4_fc_add_tlv(struct super_block *sb, u16 tag, u16 len, u8 *val,struct ext4_fc_tl tl;u8 *dst; - dst =ext4_fc_reserve_space (sb,sizeof (tl) + len, crc); + dst =ext4_fc_reserve_space (sb,EXT4_FC_TAG_BASE_LEN + len, crc);if (!dst)return false; tl.fc_tag =cpu_to_le16 (tag); tl.fc_len =cpu_to_le16 (len); -ext4_fc_memcpy (sb, dst, &tl,sizeof (tl), crc); -ext4_fc_memcpy (sb, dst +sizeof (tl), val, len, crc); +ext4_fc_memcpy (sb, dst, &tl,EXT4_FC_TAG_BASE_LEN , crc); +ext4_fc_memcpy (sb, dst +EXT4_FC_TAG_BASE_LEN , val, len, crc);return true; }
@@ -820,8 +820,8 @@ static bool ext4_fc_add_dentry_tlv(struct super_block *sb, u32 *crc,struct ext4_fc_dentry_info fcd;struct ext4_fc_tl tl;int dlen = fc_dentry->fcd_name.len; -u8 *dst =ext4_fc_reserve_space (sb,sizeof (tl) +sizeof (fcd) + dlen, - crc); +u8 *dst =ext4_fc_reserve_space (sb, +EXT4_FC_TAG_BASE_LEN +sizeof (fcd) + dlen, crc);if (!dst)return false;
@@ -830,8 +830,8 @@ static bool ext4_fc_add_dentry_tlv(struct super_block *sb, u32 *crc, fcd.fc_ino =cpu_to_le32 (fc_dentry->fcd_ino); tl.fc_tag =cpu_to_le16 (fc_dentry->fcd_op); tl.fc_len =cpu_to_le16 (sizeof (fcd) + dlen); -ext4_fc_memcpy (sb, dst, &tl,sizeof (tl), crc); - dst +=sizeof (tl); +ext4_fc_memcpy (sb, dst, &tl,EXT4_FC_TAG_BASE_LEN , crc); + dst +=EXT4_FC_TAG_BASE_LEN ;ext4_fc_memcpy (sb, dst, &fcd,sizeof (fcd), crc); dst +=sizeof (fcd);ext4_fc_memcpy (sb, dst, fc_dentry->fcd_name.name, dlen, crc);
@@ -868,13 +868,13 @@ static int ext4_fc_write_inode(struct inode *inode, u32 *crc) ret = -ECANCELED ; dst =ext4_fc_reserve_space (inode->i_sb, -sizeof (tl) + inode_len +sizeof (fc_inode.fc_ino), crc); +EXT4_FC_TAG_BASE_LEN + inode_len +sizeof (fc_inode.fc_ino), crc);if (!dst) goto err; -if (!ext4_fc_memcpy (inode->i_sb, dst, &tl,sizeof (tl), crc)) +if (!ext4_fc_memcpy (inode->i_sb, dst, &tl,EXT4_FC_TAG_BASE_LEN , crc)) goto err; - dst +=sizeof (tl); + dst +=EXT4_FC_TAG_BASE_LEN ;if (!ext4_fc_memcpy (inode->i_sb, dst, &fc_inode,sizeof (fc_inode), crc)) goto err; dst +=sizeof (fc_inode);
@@ -2026,9 +2026,10 @@ static int ext4_fc_replay_scan(journal_t *journal, } state->fc_replay_expected_off++; -for (cur = start; cur < end; cur = cur +sizeof (tl) +le16_to_cpu (tl.fc_len)) { -memcpy (&tl, cur,sizeof (tl)); - val = cur +sizeof (tl); +for (cur = start; cur < end; + cur = cur +EXT4_FC_TAG_BASE_LEN +le16_to_cpu (tl.fc_len)) { +memcpy (&tl, cur,EXT4_FC_TAG_BASE_LEN ); + val = cur +EXT4_FC_TAG_BASE_LEN ;ext4_debug ("Scan phase, tag:%s, blk %lld\n" ,tag2str (le16_to_cpu (tl.fc_tag)), bh->b_blocknr);switch (le16_to_cpu (tl.fc_tag)) {
@@ -2051,13 +2052,13 @@ static int ext4_fc_replay_scan(journal_t *journal,case EXT4_FC_TAG_PAD : state->fc_cur_tag++; state->fc_crc =ext4_chksum (sbi, state->fc_crc, cur, -sizeof (tl) +le16_to_cpu (tl.fc_len)); +EXT4_FC_TAG_BASE_LEN +le16_to_cpu (tl.fc_len));break ;case EXT4_FC_TAG_TAIL : state->fc_cur_tag++;memcpy (&tail, val,sizeof (tail)); state->fc_crc =ext4_chksum (sbi, state->fc_crc, cur, -sizeof (tl) + +EXT4_FC_TAG_BASE_LEN + offsetof(struct ext4_fc_tail , fc_crc)); if (le32_to_cpu(tail.fc_tid) == expected_tid &&
@@ -2084,7 +2085,7 @@ static int ext4_fc_replay_scan(journal_t *journal, } state->fc_cur_tag++; state->fc_crc =ext4_chksum (sbi, state->fc_crc, cur, -sizeof (tl) +le16_to_cpu (tl.fc_len)); +EXT4_FC_TAG_BASE_LEN +le16_to_cpu (tl.fc_len));break ;default : ret = state->fc_replay_num_tags ?
@@ -2139,9 +2140,10 @@ static int ext4_fc_replay(journal_t *journal, struct buffer_head *bh, start = (u8 *)bh->b_data; end = (__u8 *)bh->b_data + journal->j_blocksize -1 ; -for (cur = start; cur < end; cur = cur +sizeof (tl) +le16_to_cpu (tl.fc_len)) { -memcpy (&tl, cur,sizeof (tl)); - val = cur +sizeof (tl); +for (cur = start; cur < end; + cur = cur +EXT4_FC_TAG_BASE_LEN +le16_to_cpu (tl.fc_len)) { +memcpy (&tl, cur,EXT4_FC_TAG_BASE_LEN ); + val = cur +EXT4_FC_TAG_BASE_LEN ;if (state->fc_replay_num_tags ==0 ) { ret =JBD2_FC_REPLAY_STOP ;
fs/ext4/fast_commit.h +3 -0
@@ -70,6 +70,9 @@ struct ext4_fc_tail {__le32 fc_crc; }; +/* Tag base length */ +#define EXT4_FC_TAG_BASE_LEN (sizeof(struct ext4_fc_tl)) +/* * Fast commit status codes */
Signature · 1 line
2.31.1
PATCH 2/3 ext4: factor out ext4_fc_get_tl()
In reply to Ye BinFactor out ext4_fc_get_tl() to fill 'tl' with host byte order.
--- fs/ext4/fast_commit.c | 46 +++++++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 21 deletions(-)
fs/ext4/fast_commit.c +25 -21
@@ -1338,7 +1338,7 @@ struct dentry_info_args { };static inline void tl_to_darg (struct dentry_info_args *darg, -struct ext4_fc_tl *tl,u8 *val) +struct ext4_fc_tl *tl,u8 *val) {struct ext4_fc_dentry_info fcd;
@@ -1347,8 +1347,14 @@ static inline void tl_to_darg(struct dentry_info_args *darg, darg->parent_ino =le32_to_cpu (fcd.fc_parent_ino); darg->ino =le32_to_cpu (fcd.fc_ino); darg->dname = val + offsetof(struct ext4_fc_dentry_info , fc_dname); - darg->dname_len =le16_to_cpu (tl->fc_len) - -sizeof (struct ext4_fc_dentry_info ); + darg->dname_len = tl->fc_len -sizeof (struct ext4_fc_dentry_info ); +} + +static inline void ext4_fc_get_tl (struct ext4_fc_tl *tl,u8 *val) +{ +memcpy (tl, val,EXT4_FC_TAG_BASE_LEN ); + tl->fc_len =le16_to_cpu (tl->fc_len); + tl->fc_tag =le16_to_cpu (tl->fc_tag); }/* Unlink replay function */
@@ -1513,7 +1519,7 @@ static int ext4_fc_replay_inode(struct super_block *sb, struct ext4_fc_tl *tl,struct ext4_inode *raw_fc_inode;struct inode *inode =NULL ;struct ext4_iloc iloc; -int inode_len, ino, ret, tag =le16_to_cpu (tl->fc_tag); +int inode_len, ino, ret, tag = tl->fc_tag;struct ext4_extent_header *eh;memcpy (&fc_inode, val,sizeof (fc_inode));
@@ -1538,7 +1544,7 @@ static int ext4_fc_replay_inode(struct super_block *sb, struct ext4_fc_tl *tl,if (ret) goto out; - inode_len =le16_to_cpu (tl->fc_len) -sizeof (struct ext4_fc_inode ); + inode_len = tl->fc_len -sizeof (struct ext4_fc_inode ); raw_inode =ext4_raw_inode (&iloc);memcpy (raw_inode, raw_fc_inode, offsetof(struct ext4_inode , i_block));
@@ -2027,12 +2033,12 @@ static int ext4_fc_replay_scan(journal_t *journal, state->fc_replay_expected_off++;for (cur = start; cur < end; - cur = cur +EXT4_FC_TAG_BASE_LEN +le16_to_cpu (tl.fc_len)) { -memcpy (&tl, cur,EXT4_FC_TAG_BASE_LEN ); + cur = cur +EXT4_FC_TAG_BASE_LEN + tl.fc_len) { +ext4_fc_get_tl (&tl, cur); val = cur +EXT4_FC_TAG_BASE_LEN ;ext4_debug ("Scan phase, tag:%s, blk %lld\n" , -tag2str (le16_to_cpu (tl.fc_tag)), bh->b_blocknr); -switch (le16_to_cpu (tl.fc_tag)) { +tag2str (tl.fc_tag), bh->b_blocknr); +switch (tl.fc_tag) {case EXT4_FC_TAG_ADD_RANGE :memcpy (&ext, val,sizeof (ext)); ex = (struct ext4_extent *)&ext.fc_ex;
@@ -2052,7 +2058,7 @@ static int ext4_fc_replay_scan(journal_t *journal,case EXT4_FC_TAG_PAD : state->fc_cur_tag++; state->fc_crc =ext4_chksum (sbi, state->fc_crc, cur, -EXT4_FC_TAG_BASE_LEN +le16_to_cpu (tl.fc_len)); +EXT4_FC_TAG_BASE_LEN + tl.fc_len);break ;case EXT4_FC_TAG_TAIL : state->fc_cur_tag++;
@@ -2085,7 +2091,7 @@ static int ext4_fc_replay_scan(journal_t *journal, } state->fc_cur_tag++; state->fc_crc =ext4_chksum (sbi, state->fc_crc, cur, -EXT4_FC_TAG_BASE_LEN +le16_to_cpu (tl.fc_len)); +EXT4_FC_TAG_BASE_LEN + tl.fc_len);break ;default : ret = state->fc_replay_num_tags ?
@@ -2141,8 +2147,8 @@ static int ext4_fc_replay(journal_t *journal, struct buffer_head *bh, end = (__u8 *)bh->b_data + journal->j_blocksize -1 ;for (cur = start; cur < end; - cur = cur +EXT4_FC_TAG_BASE_LEN +le16_to_cpu (tl.fc_len)) { -memcpy (&tl, cur,EXT4_FC_TAG_BASE_LEN ); + cur = cur +EXT4_FC_TAG_BASE_LEN + tl.fc_len) { +ext4_fc_get_tl (&tl, cur); val = cur +EXT4_FC_TAG_BASE_LEN ;if (state->fc_replay_num_tags ==0 ) {
@@ -2150,10 +2156,9 @@ static int ext4_fc_replay(journal_t *journal, struct buffer_head *bh,ext4_fc_set_bitmaps_and_counters (sb);break ; } -ext4_debug ("Replay phase, tag:%s\n" , -tag2str (le16_to_cpu (tl.fc_tag))); +ext4_debug ("Replay phase, tag:%s\n" ,tag2str (tl.fc_tag)); state->fc_replay_num_tags--; -switch (le16_to_cpu (tl.fc_tag)) { +switch (tl.fc_tag) {case EXT4_FC_TAG_LINK : ret =ext4_fc_replay_link (sb, &tl, val);break ;
@@ -2174,19 +2179,18 @@ static int ext4_fc_replay(journal_t *journal, struct buffer_head *bh,break ;case EXT4_FC_TAG_PAD :trace_ext4_fc_replay (sb,EXT4_FC_TAG_PAD ,0 , -le16_to_cpu (tl.fc_len),0 ); + tl.fc_len,0 );break ;case EXT4_FC_TAG_TAIL : -trace_ext4_fc_replay (sb,EXT4_FC_TAG_TAIL ,0 , -le16_to_cpu (tl.fc_len),0 ); +trace_ext4_fc_replay (sb,EXT4_FC_TAG_TAIL , +0 , tl.fc_len,0 );memcpy (&tail, val,sizeof (tail));WARN_ON (le32_to_cpu (tail.fc_tid) != expected_tid);break ;case EXT4_FC_TAG_HEAD :break ;default : -trace_ext4_fc_replay (sb,le16_to_cpu (tl.fc_tag),0 , -le16_to_cpu (tl.fc_len),0 ); +trace_ext4_fc_replay (sb, tl.fc_tag,0 , tl.fc_len,0 ); ret = -ECANCELED ;break ; }
Signature · 1 line
2.31.1
PATCH 3/3 ext4: fix potential out of bound read in ext4_fc_replay_scan()
In reply to Ye BinFor scan loop must ensure that at least EXT4_FC_TAG_BASE_LEN space. If remain space less than EXT4_FC_TAG_BASE_LEN which will lead to out of bound read when mounting corrupt file system image.
ADD_RANGE/HEAD/TAIL is needed to add extra check when do journal scan, as this three tags will read data during scan, tag length couldn't less than data length which will read.
--- fs/ext4/fast_commit.c | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-)
fs/ext4/fast_commit.c +36 -2
@@ -1976,6 +1976,34 @@ 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) +{ +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) { +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); +case EXT4_FC_TAG_DEL_RANGE : +case EXT4_FC_TAG_LINK : +case EXT4_FC_TAG_UNLINK : +case EXT4_FC_TAG_CREAT : +case EXT4_FC_TAG_INODE : +case EXT4_FC_TAG_PAD : +default : +return true; + } +} +/* * Recovery Scan phase handler *
@@ -2032,10 +2060,15 @@ static int ext4_fc_replay_scan(journal_t *journal, } state->fc_replay_expected_off++; -for (cur = start; cur < end; +for (cur = start; cur < end -EXT4_FC_TAG_BASE_LEN ; 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)) { + ret = state->fc_replay_num_tags ? +JBD2_FC_REPLAY_STOP : -ECANCELED ; + goto out_err; + }ext4_debug ("Scan phase, tag:%s, blk %lld\n" ,tag2str (tl.fc_tag), bh->b_blocknr);switch (tl.fc_tag) {
@@ -2146,7 +2179,7 @@ static int ext4_fc_replay(journal_t *journal, struct buffer_head *bh, start = (u8 *)bh->b_data; end = (__u8 *)bh->b_data + journal->j_blocksize -1 ; -for (cur = start; cur < end; +for (cur = start; cur < end -EXT4_FC_TAG_BASE_LEN ; cur = cur +EXT4_FC_TAG_BASE_LEN + tl.fc_len) {ext4_fc_get_tl (&tl, cur); val = cur +EXT4_FC_TAG_BASE_LEN ;
@@ -2156,6 +2189,7 @@ static int ext4_fc_replay(journal_t *journal, struct buffer_head *bh,ext4_fc_set_bitmaps_and_counters (sb);break ; } +ext4_debug ("Replay phase, tag:%s\n" ,tag2str (tl.fc_tag)); state->fc_replay_num_tags--;switch (tl.fc_tag) {
Signature · 1 line
2.31.1
Quoted 10 lines · Ye Bin
Ye Bin (3):
ext4: introduceEXT4_FC_TAG_BASE_LENhelper
ext4: factor outext4_fc_get_tl()
ext4: fix potential out of bound read inext4_fc_replay_scan()
fs/ext4/fast_commit.c| 122 ++++++++++++++++++++++++++++--------------fs/ext4/fast_commit.h | 3 ++ 2 files changed, 84 insertions(+), 41 deletions(-)[...]
Applied, thanks!
[1/3] ext4: introduce EXT4_FC_TAG_BASE_LEN helper
commit: 8bbc7ccd1b2a58390ea6fe52d784c130cdea1b80
[2/3] ext4: factor out ext4_fc_get_tl()
commit: 521502e8682f049895d2023dc50c23ebb9d68e07
[3/3] ext4: fix potential out of bound read in ext4_fc_replay_scan()
commit: 536e190193671d5aa24446b60b151a5c90468266Best regards,
Signature · 1 line
Theodore Ts'o <[email protected]>