aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDarrick J. Wong <darrick.wong@oracle.com>2018-02-26 22:43:18 -0600
committerEric Sandeen <sandeen@redhat.com>2018-02-26 22:43:18 -0600
commite91df8449abbbb2087c64428c8e4af25c445ca0e (patch)
tree3968b158d387e53c1361b30b17aed2afd16f5557
parentfaaad1df3f51c03131bd5bc527c9ffdcecb0f0f1 (diff)
downloadxfsprogs-dev-e91df8449abbbb2087c64428c8e4af25c445ca0e.tar.gz
xfs: check sb_agblocks and sb_agblklog when validating superblock
Source kernel commit: 4bb73d014785cc55225686f9f46e7192fb59d26b Currently, we don't check sb_agblocks or sb_agblklog when we validate the superblock, which means that we can fuzz garbage values into those values and the mount succeeds. This leads to all sorts of UBSAN warnings in xfs/350 since we can then coerce other parts of xfs into shifting by ridiculously large values. Once we've validated agblocks, make sure the agcount makes sense. Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> Reviewed-by: Brian Foster <bfoster@redhat.com> [sandeen: fix up u32 usage now so we keep building] Signed-off-by: Eric Sandeen <sandeen@sandeen.net>
-rw-r--r--libxfs/libxfs_priv.h18
-rw-r--r--libxfs/xfs_fs.h7
-rw-r--r--libxfs/xfs_sb.c14
3 files changed, 39 insertions, 0 deletions
diff --git a/libxfs/libxfs_priv.h b/libxfs/libxfs_priv.h
index 637493d733..bc1ce226ae 100644
--- a/libxfs/libxfs_priv.h
+++ b/libxfs/libxfs_priv.h
@@ -231,6 +231,24 @@ static inline int __do_div(unsigned long long *n, unsigned base)
#define do_mod(a, b) ((a) % (b))
#define rol32(x,y) (((x) << (y)) | ((x) >> (32 - (y))))
+/**
+ * div_u64_rem - unsigned 64bit divide with 32bit divisor with remainder
+ * @dividend: unsigned 64bit dividend
+ * @divisor: unsigned 32bit divisor
+ * @remainder: pointer to unsigned 32bit remainder
+ *
+ * Return: sets ``*remainder``, then returns dividend / divisor
+ *
+ * This is commonly provided by 32bit archs to provide an optimized 64bit
+ * divide.
+ */
+static inline uint64_t
+div_u64_rem(uint64_t dividend, uint32_t divisor, uint32_t *remainder)
+{
+ *remainder = dividend % divisor;
+ return dividend / divisor;
+}
+
#define min_t(type,x,y) \
({ type __x = (x); type __y = (y); __x < __y ? __x: __y; })
#define max_t(type,x,y) \
diff --git a/libxfs/xfs_fs.h b/libxfs/xfs_fs.h
index 489f0f5721..86a379f6c1 100644
--- a/libxfs/xfs_fs.h
+++ b/libxfs/xfs_fs.h
@@ -254,6 +254,13 @@ typedef struct xfs_fsop_resblks {
#define XFS_MAX_LOG_BLOCKS (1024 * 1024ULL)
#define XFS_MIN_LOG_BYTES (10 * 1024 * 1024ULL)
+/*
+ * Limits on sb_agblocks/sb_agblklog -- mkfs won't format AGs smaller than
+ * 16MB or larger than 1TB.
+ */
+#define XFS_MIN_AG_BYTES (1ULL << 24) /* 16 MB */
+#define XFS_MAX_AG_BYTES (1ULL << 40) /* 1 TB */
+
/* keep the maximum size under 2^31 by a small amount */
#define XFS_MAX_LOG_BYTES \
((2 * 1024 * 1024 * 1024ULL) - XFS_MIN_LOG_BYTES)
diff --git a/libxfs/xfs_sb.c b/libxfs/xfs_sb.c
index 57c88d220e..bca65eeb66 100644
--- a/libxfs/xfs_sb.c
+++ b/libxfs/xfs_sb.c
@@ -115,6 +115,9 @@ xfs_mount_validate_sb(
bool check_inprogress,
bool check_version)
{
+ uint32_t agcount = 0;
+ uint32_t rem;
+
if (sbp->sb_magicnum != XFS_SB_MAGIC) {
xfs_warn(mp, "bad magic number");
return -EWRONGFS;
@@ -225,6 +228,13 @@ xfs_mount_validate_sb(
return -EINVAL;
}
+ /* Compute agcount for this number of dblocks and agblocks */
+ if (sbp->sb_agblocks) {
+ agcount = div_u64_rem(sbp->sb_dblocks, sbp->sb_agblocks, &rem);
+ if (rem)
+ agcount++;
+ }
+
/*
* More sanity checking. Most of these were stolen directly from
* xfs_repair.
@@ -249,6 +259,10 @@ xfs_mount_validate_sb(
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 ||
+ sbp->sb_agblklog != xfs_highbit32(sbp->sb_agblocks - 1) + 1 ||
+ agcount == 0 || agcount != sbp->sb_agcount ||
(sbp->sb_blocklog - sbp->sb_inodelog != sbp->sb_inopblog) ||
(sbp->sb_rextsize * sbp->sb_blocksize > XFS_MAX_RTEXTSIZE) ||
(sbp->sb_rextsize * sbp->sb_blocksize < XFS_MIN_RTEXTSIZE) ||