Skip to content

Commit f1e1765

Browse files
Dave ChinnerDarrick J. Wong
authored andcommitted
xfs: journal geometry is not properly bounds checked
If the journal geometry results in a sector or log stripe unit validation problem, it indicates that we cannot set the log up to safely write to the the journal. In these cases, we must abort the mount because the corruption needs external intervention to resolve. Similarly, a journal that is too large cannot be written to safely, either, so we shouldn't allow those geometries to mount, either. If the log is too small, we risk having transaction reservations overruning the available log space and the system hanging waiting for space it can never provide. This is purely a runtime hang issue, not a corruption issue as per the first cases listed above. We abort mounts of the log is too small for V5 filesystems, but we must allow v4 filesystems to mount because, historically, there was no log size validity checking and so some systems may still be out there with undersized logs. The problem is that on V4 filesystems, when we discover a log geometry problem, we skip all the remaining checks and then allow the log to continue mounting. This mean that if one of the log size checks fails, we skip the log stripe unit check. i.e. we allow the mount because a "non-fatal" geometry is violated, and then fail to check the hard fail geometries that should fail the mount. Move all these fatal checks to the superblock verifier, and add a new check for the two log sector size geometry variables having the same values. This will prevent any attempt to mount a log that has invalid or inconsistent geometries long before we attempt to mount the log. However, for the minimum log size checks, we can only do that once we've setup up the log and calculated all the iclog sizes and roundoffs. Hence this needs to remain in the log mount code after the log has been initialised. It is also the only case where we should allow a v4 filesystem to continue running, so leave that handling in place, too. Signed-off-by: Dave Chinner <dchinner@redhat.com> Reviewed-by: Christoph Hellwig <hch@lst.de> Reviewed-by: Darrick J. Wong <djwong@kernel.org> Signed-off-by: Darrick J. Wong <djwong@kernel.org>
1 parent 8ebbf26 commit f1e1765

File tree

2 files changed

+70
-33
lines changed

2 files changed

+70
-33
lines changed

fs/xfs/libxfs/xfs_sb.c

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,6 @@ xfs_validate_sb_common(
412412
sbp->sb_inodelog < XFS_DINODE_MIN_LOG ||
413413
sbp->sb_inodelog > XFS_DINODE_MAX_LOG ||
414414
sbp->sb_inodesize != (1 << sbp->sb_inodelog) ||
415-
sbp->sb_logsunit > XLOG_MAX_RECORD_BSIZE ||
416415
sbp->sb_inopblock != howmany(sbp->sb_blocksize,sbp->sb_inodesize) ||
417416
XFS_FSB_TO_B(mp, sbp->sb_agblocks) < XFS_MIN_AG_BYTES ||
418417
XFS_FSB_TO_B(mp, sbp->sb_agblocks) > XFS_MAX_AG_BYTES ||
@@ -430,6 +429,61 @@ xfs_validate_sb_common(
430429
return -EFSCORRUPTED;
431430
}
432431

432+
/*
433+
* Logs that are too large are not supported at all. Reject them
434+
* outright. Logs that are too small are tolerated on v4 filesystems,
435+
* but we can only check that when mounting the log. Hence we skip
436+
* those checks here.
437+
*/
438+
if (sbp->sb_logblocks > XFS_MAX_LOG_BLOCKS) {
439+
xfs_notice(mp,
440+
"Log size 0x%x blocks too large, maximum size is 0x%llx blocks",
441+
sbp->sb_logblocks, XFS_MAX_LOG_BLOCKS);
442+
return -EFSCORRUPTED;
443+
}
444+
445+
if (XFS_FSB_TO_B(mp, sbp->sb_logblocks) > XFS_MAX_LOG_BYTES) {
446+
xfs_warn(mp,
447+
"log size 0x%llx bytes too large, maximum size is 0x%llx bytes",
448+
XFS_FSB_TO_B(mp, sbp->sb_logblocks),
449+
XFS_MAX_LOG_BYTES);
450+
return -EFSCORRUPTED;
451+
}
452+
453+
/*
454+
* Do not allow filesystems with corrupted log sector or stripe units to
455+
* be mounted. We cannot safely size the iclogs or write to the log if
456+
* the log stripe unit is not valid.
457+
*/
458+
if (sbp->sb_versionnum & XFS_SB_VERSION_SECTORBIT) {
459+
if (sbp->sb_logsectsize != (1U << sbp->sb_logsectlog)) {
460+
xfs_notice(mp,
461+
"log sector size in bytes/log2 (0x%x/0x%x) must match",
462+
sbp->sb_logsectsize, 1U << sbp->sb_logsectlog);
463+
return -EFSCORRUPTED;
464+
}
465+
} else if (sbp->sb_logsectsize || sbp->sb_logsectlog) {
466+
xfs_notice(mp,
467+
"log sector size in bytes/log2 (0x%x/0x%x) are not zero",
468+
sbp->sb_logsectsize, sbp->sb_logsectlog);
469+
return -EFSCORRUPTED;
470+
}
471+
472+
if (sbp->sb_logsunit > 1) {
473+
if (sbp->sb_logsunit % sbp->sb_blocksize) {
474+
xfs_notice(mp,
475+
"log stripe unit 0x%x bytes must be a multiple of block size",
476+
sbp->sb_logsunit);
477+
return -EFSCORRUPTED;
478+
}
479+
if (sbp->sb_logsunit > XLOG_MAX_RECORD_BSIZE) {
480+
xfs_notice(mp,
481+
"log stripe unit 0x%x bytes over maximum size (0x%x bytes)",
482+
sbp->sb_logsunit, XLOG_MAX_RECORD_BSIZE);
483+
return -EFSCORRUPTED;
484+
}
485+
}
486+
433487
/* Validate the realtime geometry; stolen from xfs_repair */
434488
if (sbp->sb_rextsize * sbp->sb_blocksize > XFS_MAX_RTEXTSIZE ||
435489
sbp->sb_rextsize * sbp->sb_blocksize < XFS_MIN_RTEXTSIZE) {

fs/xfs/xfs_log.c

Lines changed: 15 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -639,7 +639,6 @@ xfs_log_mount(
639639
int num_bblks)
640640
{
641641
struct xlog *log;
642-
bool fatal = xfs_has_crc(mp);
643642
int error = 0;
644643
int min_logfsbs;
645644

@@ -663,53 +662,37 @@ xfs_log_mount(
663662
mp->m_log = log;
664663

665664
/*
666-
* Validate the given log space and drop a critical message via syslog
667-
* if the log size is too small that would lead to some unexpected
668-
* situations in transaction log space reservation stage.
665+
* Now that we have set up the log and it's internal geometry
666+
* parameters, we can validate the given log space and drop a critical
667+
* message via syslog if the log size is too small. A log that is too
668+
* small can lead to unexpected situations in transaction log space
669+
* reservation stage. The superblock verifier has already validated all
670+
* the other log geometry constraints, so we don't have to check those
671+
* here.
669672
*
670-
* Note: we can't just reject the mount if the validation fails. This
671-
* would mean that people would have to downgrade their kernel just to
672-
* remedy the situation as there is no way to grow the log (short of
673-
* black magic surgery with xfs_db).
673+
* Note: For v4 filesystems, we can't just reject the mount if the
674+
* validation fails. This would mean that people would have to
675+
* downgrade their kernel just to remedy the situation as there is no
676+
* way to grow the log (short of black magic surgery with xfs_db).
674677
*
675-
* We can, however, reject mounts for CRC format filesystems, as the
678+
* We can, however, reject mounts for V5 format filesystems, as the
676679
* mkfs binary being used to make the filesystem should never create a
677680
* filesystem with a log that is too small.
678681
*/
679682
min_logfsbs = xfs_log_calc_minimum_size(mp);
680-
681683
if (mp->m_sb.sb_logblocks < min_logfsbs) {
682684
xfs_warn(mp,
683685
"Log size %d blocks too small, minimum size is %d blocks",
684686
mp->m_sb.sb_logblocks, min_logfsbs);
685-
error = -EINVAL;
686-
} else if (mp->m_sb.sb_logblocks > XFS_MAX_LOG_BLOCKS) {
687-
xfs_warn(mp,
688-
"Log size %d blocks too large, maximum size is %lld blocks",
689-
mp->m_sb.sb_logblocks, XFS_MAX_LOG_BLOCKS);
690-
error = -EINVAL;
691-
} else if (XFS_FSB_TO_B(mp, mp->m_sb.sb_logblocks) > XFS_MAX_LOG_BYTES) {
692-
xfs_warn(mp,
693-
"log size %lld bytes too large, maximum size is %lld bytes",
694-
XFS_FSB_TO_B(mp, mp->m_sb.sb_logblocks),
695-
XFS_MAX_LOG_BYTES);
696-
error = -EINVAL;
697-
} else if (mp->m_sb.sb_logsunit > 1 &&
698-
mp->m_sb.sb_logsunit % mp->m_sb.sb_blocksize) {
699-
xfs_warn(mp,
700-
"log stripe unit %u bytes must be a multiple of block size",
701-
mp->m_sb.sb_logsunit);
702-
error = -EINVAL;
703-
fatal = true;
704-
}
705-
if (error) {
687+
706688
/*
707689
* Log check errors are always fatal on v5; or whenever bad
708690
* metadata leads to a crash.
709691
*/
710-
if (fatal) {
692+
if (xfs_has_crc(mp)) {
711693
xfs_crit(mp, "AAIEEE! Log failed size checks. Abort!");
712694
ASSERT(0);
695+
error = -EINVAL;
713696
goto out_free_log;
714697
}
715698
xfs_crit(mp, "Log size out of supported range.");

0 commit comments

Comments
 (0)