xfs: add a few more verifier tests
authorEric Sandeen <sandeen@sandeen.net>
Tue, 9 Sep 2014 01:47:24 +0000 (11:47 +1000)
committerDave Chinner <david@fromorbit.com>
Tue, 9 Sep 2014 01:47:24 +0000 (11:47 +1000)
These were exposed by fsfuzzer runs; without them we fail
in various exciting and sometimes convoluted ways when we
encounter disk corruption.

Without the MAXLEVELS tests we tend to walk off the end of
an array in a loop like this:

        for (i = 0; i < cur->bc_nlevels; i++) {
                if (cur->bc_bufs[i])

Without the dirblklog test we try to allocate more memory
than we could possibly hope for and loop forever:

xfs_dabuf_map()
nfsb = mp->m_dir_geo->fsbcount;
irecs = kmem_zalloc(sizeof(irec) * nfsb, KM_SLEEP...

As for the logbsize check, that's the convoluted one.

If logbsize is specified at mount time, it's sanitized
in xfs_parseargs; in particular it makes sure that it's
not > XLOG_MAX_RECORD_BSIZE.

If not specified at mount time, it comes from the superblock
via sb_logsunit; this is limited to 256k at mkfs time as well;
it's copied into m_logbsize in xfs_finish_flags().

However, if for some reason the on-disk value is corrupt and
too large, nothing catches it.  It's a circuitous path, but
that size eventually finds its way to places that make the kernel
very unhappy, leading to oopses in xlog_pack_data() because we
use the size as an index into iclog->ic_data, but the array
is not necessarily that big.

Anyway - bounds checking when we read from disk is a good thing!

Signed-off-by: Eric Sandeen <sandeen@redhat.com>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Dave Chinner <david@fromorbit.com>
fs/xfs/libxfs/xfs_alloc.c
fs/xfs/libxfs/xfs_ialloc.c
fs/xfs/libxfs/xfs_sb.c

index 4bffffe038a1a8ac329b9dc19e5a885878270088..eff34218f405a0d7473ccfa35221b1f9969f168d 100644 (file)
@@ -2209,6 +2209,10 @@ xfs_agf_verify(
              be32_to_cpu(agf->agf_flcount) <= XFS_AGFL_SIZE(mp)))
                return false;
 
+       if (be32_to_cpu(agf->agf_levels[XFS_BTNUM_BNO]) > XFS_BTREE_MAXLEVELS ||
+           be32_to_cpu(agf->agf_levels[XFS_BTNUM_CNT]) > XFS_BTREE_MAXLEVELS)
+               return false;
+
        /*
         * during growfs operations, the perag is not fully initialised,
         * so we can't use it for any useful checking. growfs ensures we can't
index b62771f1f4b5b7028569b32af3b16bc806005132..d213a2eae95e0984ea5f4045c15f4dfd1e9d857f 100644 (file)
@@ -2051,6 +2051,8 @@ xfs_agi_verify(
        if (!XFS_AGI_GOOD_VERSION(be32_to_cpu(agi->agi_versionnum)))
                return false;
 
+       if (be32_to_cpu(agi->agi_level) > XFS_BTREE_MAXLEVELS)
+               return false;
        /*
         * during growfs operations, the perag is not fully initialised,
         * so we can't use it for any useful checking. growfs ensures we can't
index ad525a5623a49557c9554f23aa72d323e9ed8b77..8426e5e2682ef47e677a174b9e4d12e58fe190a1 100644 (file)
@@ -279,11 +279,13 @@ xfs_mount_validate_sb(
            sbp->sb_blocklog < XFS_MIN_BLOCKSIZE_LOG                    ||
            sbp->sb_blocklog > XFS_MAX_BLOCKSIZE_LOG                    ||
            sbp->sb_blocksize != (1 << sbp->sb_blocklog)                ||
+           sbp->sb_dirblklog > XFS_MAX_BLOCKSIZE_LOG                   ||
            sbp->sb_inodesize < XFS_DINODE_MIN_SIZE                     ||
            sbp->sb_inodesize > XFS_DINODE_MAX_SIZE                     ||
            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) ||
            (sbp->sb_blocklog - sbp->sb_inodelog != sbp->sb_inopblog)   ||
            (sbp->sb_rextsize * sbp->sb_blocksize > XFS_MAX_RTEXTSIZE)  ||