xfs: journal geometry is not properly bounds checked
[platform/kernel/linux-starfive.git] / fs / xfs / libxfs / xfs_sb.c
index ba0f17b..5e17468 100644 (file)
@@ -412,7 +412,6 @@ xfs_validate_sb_common(
            sbp->sb_inodelog < XFS_DINODE_MIN_LOG                       ||
            sbp->sb_inodelog > XFS_DINODE_MAX_LOG                       ||
            sbp->sb_inodesize != (1 << sbp->sb_inodelog)                ||
-           sbp->sb_logsunit > XLOG_MAX_RECORD_BSIZE                    ||
            sbp->sb_inopblock != howmany(sbp->sb_blocksize,sbp->sb_inodesize) ||
            XFS_FSB_TO_B(mp, sbp->sb_agblocks) < XFS_MIN_AG_BYTES       ||
            XFS_FSB_TO_B(mp, sbp->sb_agblocks) > XFS_MAX_AG_BYTES       ||
@@ -430,6 +429,61 @@ xfs_validate_sb_common(
                return -EFSCORRUPTED;
        }
 
+       /*
+        * Logs that are too large are not supported at all. Reject them
+        * outright. Logs that are too small are tolerated on v4 filesystems,
+        * but we can only check that when mounting the log. Hence we skip
+        * those checks here.
+        */
+       if (sbp->sb_logblocks > XFS_MAX_LOG_BLOCKS) {
+               xfs_notice(mp,
+               "Log size 0x%x blocks too large, maximum size is 0x%llx blocks",
+                        sbp->sb_logblocks, XFS_MAX_LOG_BLOCKS);
+               return -EFSCORRUPTED;
+       }
+
+       if (XFS_FSB_TO_B(mp, sbp->sb_logblocks) > XFS_MAX_LOG_BYTES) {
+               xfs_warn(mp,
+               "log size 0x%llx bytes too large, maximum size is 0x%llx bytes",
+                        XFS_FSB_TO_B(mp, sbp->sb_logblocks),
+                        XFS_MAX_LOG_BYTES);
+               return -EFSCORRUPTED;
+       }
+
+       /*
+        * Do not allow filesystems with corrupted log sector or stripe units to
+        * be mounted. We cannot safely size the iclogs or write to the log if
+        * the log stripe unit is not valid.
+        */
+       if (sbp->sb_versionnum & XFS_SB_VERSION_SECTORBIT) {
+               if (sbp->sb_logsectsize != (1U << sbp->sb_logsectlog)) {
+                       xfs_notice(mp,
+                       "log sector size in bytes/log2 (0x%x/0x%x) must match",
+                               sbp->sb_logsectsize, 1U << sbp->sb_logsectlog);
+                       return -EFSCORRUPTED;
+               }
+       } else if (sbp->sb_logsectsize || sbp->sb_logsectlog) {
+               xfs_notice(mp,
+               "log sector size in bytes/log2 (0x%x/0x%x) are not zero",
+                       sbp->sb_logsectsize, sbp->sb_logsectlog);
+               return -EFSCORRUPTED;
+       }
+
+       if (sbp->sb_logsunit > 1) {
+               if (sbp->sb_logsunit % sbp->sb_blocksize) {
+                       xfs_notice(mp,
+               "log stripe unit 0x%x bytes must be a multiple of block size",
+                               sbp->sb_logsunit);
+                       return -EFSCORRUPTED;
+               }
+               if (sbp->sb_logsunit > XLOG_MAX_RECORD_BSIZE) {
+                       xfs_notice(mp,
+               "log stripe unit 0x%x bytes over maximum size (0x%x bytes)",
+                               sbp->sb_logsunit, XLOG_MAX_RECORD_BSIZE);
+                       return -EFSCORRUPTED;
+               }
+       }
+
        /* Validate the realtime geometry; stolen from xfs_repair */
        if (sbp->sb_rextsize * sbp->sb_blocksize > XFS_MAX_RTEXTSIZE ||
            sbp->sb_rextsize * sbp->sb_blocksize < XFS_MIN_RTEXTSIZE) {