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
  *
        }
 
        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) {
        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;
                        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) {