Commit 86810a9e authored by Dave Chinner's avatar Dave Chinner

Merge branch 'guilt/xfs-5.19-fuzz-fixes' into xfs-5.19-for-next

parents 166afc45 f0f5f658
...@@ -51,6 +51,52 @@ xfs_btree_magic( ...@@ -51,6 +51,52 @@ xfs_btree_magic(
return magic; return magic;
} }
static xfs_failaddr_t
xfs_btree_check_lblock_siblings(
struct xfs_mount *mp,
struct xfs_btree_cur *cur,
int level,
xfs_fsblock_t fsb,
xfs_fsblock_t sibling)
{
if (sibling == NULLFSBLOCK)
return NULL;
if (sibling == fsb)
return __this_address;
if (level >= 0) {
if (!xfs_btree_check_lptr(cur, sibling, level + 1))
return __this_address;
} else {
if (!xfs_verify_fsbno(mp, sibling))
return __this_address;
}
return NULL;
}
static xfs_failaddr_t
xfs_btree_check_sblock_siblings(
struct xfs_mount *mp,
struct xfs_btree_cur *cur,
int level,
xfs_agnumber_t agno,
xfs_agblock_t agbno,
xfs_agblock_t sibling)
{
if (sibling == NULLAGBLOCK)
return NULL;
if (sibling == agbno)
return __this_address;
if (level >= 0) {
if (!xfs_btree_check_sptr(cur, sibling, level + 1))
return __this_address;
} else {
if (!xfs_verify_agbno(mp, agno, sibling))
return __this_address;
}
return NULL;
}
/* /*
* Check a long btree block header. Return the address of the failing check, * Check a long btree block header. Return the address of the failing check,
* or NULL if everything is ok. * or NULL if everything is ok.
...@@ -65,6 +111,8 @@ __xfs_btree_check_lblock( ...@@ -65,6 +111,8 @@ __xfs_btree_check_lblock(
struct xfs_mount *mp = cur->bc_mp; struct xfs_mount *mp = cur->bc_mp;
xfs_btnum_t btnum = cur->bc_btnum; xfs_btnum_t btnum = cur->bc_btnum;
int crc = xfs_has_crc(mp); int crc = xfs_has_crc(mp);
xfs_failaddr_t fa;
xfs_fsblock_t fsb = NULLFSBLOCK;
if (crc) { if (crc) {
if (!uuid_equal(&block->bb_u.l.bb_uuid, &mp->m_sb.sb_meta_uuid)) if (!uuid_equal(&block->bb_u.l.bb_uuid, &mp->m_sb.sb_meta_uuid))
...@@ -83,16 +131,16 @@ __xfs_btree_check_lblock( ...@@ -83,16 +131,16 @@ __xfs_btree_check_lblock(
if (be16_to_cpu(block->bb_numrecs) > if (be16_to_cpu(block->bb_numrecs) >
cur->bc_ops->get_maxrecs(cur, level)) cur->bc_ops->get_maxrecs(cur, level))
return __this_address; return __this_address;
if (block->bb_u.l.bb_leftsib != cpu_to_be64(NULLFSBLOCK) &&
!xfs_btree_check_lptr(cur, be64_to_cpu(block->bb_u.l.bb_leftsib),
level + 1))
return __this_address;
if (block->bb_u.l.bb_rightsib != cpu_to_be64(NULLFSBLOCK) &&
!xfs_btree_check_lptr(cur, be64_to_cpu(block->bb_u.l.bb_rightsib),
level + 1))
return __this_address;
return NULL; if (bp)
fsb = XFS_DADDR_TO_FSB(mp, xfs_buf_daddr(bp));
fa = xfs_btree_check_lblock_siblings(mp, cur, level, fsb,
be64_to_cpu(block->bb_u.l.bb_leftsib));
if (!fa)
fa = xfs_btree_check_lblock_siblings(mp, cur, level, fsb,
be64_to_cpu(block->bb_u.l.bb_rightsib));
return fa;
} }
/* Check a long btree block header. */ /* Check a long btree block header. */
...@@ -130,6 +178,9 @@ __xfs_btree_check_sblock( ...@@ -130,6 +178,9 @@ __xfs_btree_check_sblock(
struct xfs_mount *mp = cur->bc_mp; struct xfs_mount *mp = cur->bc_mp;
xfs_btnum_t btnum = cur->bc_btnum; xfs_btnum_t btnum = cur->bc_btnum;
int crc = xfs_has_crc(mp); int crc = xfs_has_crc(mp);
xfs_failaddr_t fa;
xfs_agblock_t agbno = NULLAGBLOCK;
xfs_agnumber_t agno = NULLAGNUMBER;
if (crc) { if (crc) {
if (!uuid_equal(&block->bb_u.s.bb_uuid, &mp->m_sb.sb_meta_uuid)) if (!uuid_equal(&block->bb_u.s.bb_uuid, &mp->m_sb.sb_meta_uuid))
...@@ -146,16 +197,18 @@ __xfs_btree_check_sblock( ...@@ -146,16 +197,18 @@ __xfs_btree_check_sblock(
if (be16_to_cpu(block->bb_numrecs) > if (be16_to_cpu(block->bb_numrecs) >
cur->bc_ops->get_maxrecs(cur, level)) cur->bc_ops->get_maxrecs(cur, level))
return __this_address; return __this_address;
if (block->bb_u.s.bb_leftsib != cpu_to_be32(NULLAGBLOCK) &&
!xfs_btree_check_sptr(cur, be32_to_cpu(block->bb_u.s.bb_leftsib),
level + 1))
return __this_address;
if (block->bb_u.s.bb_rightsib != cpu_to_be32(NULLAGBLOCK) &&
!xfs_btree_check_sptr(cur, be32_to_cpu(block->bb_u.s.bb_rightsib),
level + 1))
return __this_address;
return NULL; if (bp) {
agbno = xfs_daddr_to_agbno(mp, xfs_buf_daddr(bp));
agno = xfs_daddr_to_agno(mp, xfs_buf_daddr(bp));
}
fa = xfs_btree_check_sblock_siblings(mp, cur, level, agno, agbno,
be32_to_cpu(block->bb_u.s.bb_leftsib));
if (!fa)
fa = xfs_btree_check_sblock_siblings(mp, cur, level, agno,
agbno, be32_to_cpu(block->bb_u.s.bb_rightsib));
return fa;
} }
/* Check a short btree block header. */ /* Check a short btree block header. */
...@@ -4271,6 +4324,21 @@ xfs_btree_visit_block( ...@@ -4271,6 +4324,21 @@ xfs_btree_visit_block(
if (xfs_btree_ptr_is_null(cur, &rptr)) if (xfs_btree_ptr_is_null(cur, &rptr))
return -ENOENT; return -ENOENT;
/*
* We only visit blocks once in this walk, so we have to avoid the
* internal xfs_btree_lookup_get_block() optimisation where it will
* return the same block without checking if the right sibling points
* back to us and creates a cyclic reference in the btree.
*/
if (cur->bc_flags & XFS_BTREE_LONG_PTRS) {
if (be64_to_cpu(rptr.l) == XFS_DADDR_TO_FSB(cur->bc_mp,
xfs_buf_daddr(bp)))
return -EFSCORRUPTED;
} else {
if (be32_to_cpu(rptr.s) == xfs_daddr_to_agbno(cur->bc_mp,
xfs_buf_daddr(bp)))
return -EFSCORRUPTED;
}
return xfs_btree_lookup_get_block(cur, level, &rptr, &block); return xfs_btree_lookup_get_block(cur, level, &rptr, &block);
} }
...@@ -4445,20 +4513,21 @@ xfs_btree_lblock_verify( ...@@ -4445,20 +4513,21 @@ xfs_btree_lblock_verify(
{ {
struct xfs_mount *mp = bp->b_mount; struct xfs_mount *mp = bp->b_mount;
struct xfs_btree_block *block = XFS_BUF_TO_BLOCK(bp); struct xfs_btree_block *block = XFS_BUF_TO_BLOCK(bp);
xfs_fsblock_t fsb;
xfs_failaddr_t fa;
/* numrecs verification */ /* numrecs verification */
if (be16_to_cpu(block->bb_numrecs) > max_recs) if (be16_to_cpu(block->bb_numrecs) > max_recs)
return __this_address; return __this_address;
/* sibling pointer verification */ /* sibling pointer verification */
if (block->bb_u.l.bb_leftsib != cpu_to_be64(NULLFSBLOCK) && fsb = XFS_DADDR_TO_FSB(mp, xfs_buf_daddr(bp));
!xfs_verify_fsbno(mp, be64_to_cpu(block->bb_u.l.bb_leftsib))) fa = xfs_btree_check_lblock_siblings(mp, NULL, -1, fsb,
return __this_address; be64_to_cpu(block->bb_u.l.bb_leftsib));
if (block->bb_u.l.bb_rightsib != cpu_to_be64(NULLFSBLOCK) && if (!fa)
!xfs_verify_fsbno(mp, be64_to_cpu(block->bb_u.l.bb_rightsib))) fa = xfs_btree_check_lblock_siblings(mp, NULL, -1, fsb,
return __this_address; be64_to_cpu(block->bb_u.l.bb_rightsib));
return fa;
return NULL;
} }
/** /**
...@@ -4499,7 +4568,9 @@ xfs_btree_sblock_verify( ...@@ -4499,7 +4568,9 @@ xfs_btree_sblock_verify(
{ {
struct xfs_mount *mp = bp->b_mount; struct xfs_mount *mp = bp->b_mount;
struct xfs_btree_block *block = XFS_BUF_TO_BLOCK(bp); struct xfs_btree_block *block = XFS_BUF_TO_BLOCK(bp);
xfs_agblock_t agno; xfs_agnumber_t agno;
xfs_agblock_t agbno;
xfs_failaddr_t fa;
/* numrecs verification */ /* numrecs verification */
if (be16_to_cpu(block->bb_numrecs) > max_recs) if (be16_to_cpu(block->bb_numrecs) > max_recs)
...@@ -4507,14 +4578,13 @@ xfs_btree_sblock_verify( ...@@ -4507,14 +4578,13 @@ xfs_btree_sblock_verify(
/* sibling pointer verification */ /* sibling pointer verification */
agno = xfs_daddr_to_agno(mp, xfs_buf_daddr(bp)); agno = xfs_daddr_to_agno(mp, xfs_buf_daddr(bp));
if (block->bb_u.s.bb_leftsib != cpu_to_be32(NULLAGBLOCK) && agbno = xfs_daddr_to_agbno(mp, xfs_buf_daddr(bp));
!xfs_verify_agbno(mp, agno, be32_to_cpu(block->bb_u.s.bb_leftsib))) fa = xfs_btree_check_sblock_siblings(mp, NULL, -1, agno, agbno,
return __this_address; be32_to_cpu(block->bb_u.s.bb_leftsib));
if (block->bb_u.s.bb_rightsib != cpu_to_be32(NULLAGBLOCK) && if (!fa)
!xfs_verify_agbno(mp, agno, be32_to_cpu(block->bb_u.s.bb_rightsib))) fa = xfs_btree_check_sblock_siblings(mp, NULL, -1, agno, agbno,
return __this_address; be32_to_cpu(block->bb_u.s.bb_rightsib));
return fa;
return NULL;
} }
/* /*
......
...@@ -357,21 +357,38 @@ xfs_dinode_verify_fork( ...@@ -357,21 +357,38 @@ xfs_dinode_verify_fork(
{ {
xfs_extnum_t di_nextents; xfs_extnum_t di_nextents;
xfs_extnum_t max_extents; xfs_extnum_t max_extents;
mode_t mode = be16_to_cpu(dip->di_mode);
uint32_t fork_size = XFS_DFORK_SIZE(dip, mp, whichfork);
uint32_t fork_format = XFS_DFORK_FORMAT(dip, whichfork);
di_nextents = xfs_dfork_nextents(dip, whichfork); di_nextents = xfs_dfork_nextents(dip, whichfork);
switch (XFS_DFORK_FORMAT(dip, whichfork)) { /*
* For fork types that can contain local data, check that the fork
* format matches the size of local data contained within the fork.
*
* For all types, check that when the size says the should be in extent
* or btree format, the inode isn't claiming it is in local format.
*/
if (whichfork == XFS_DATA_FORK) {
if (S_ISDIR(mode) || S_ISLNK(mode)) {
if (be64_to_cpu(dip->di_size) <= fork_size &&
fork_format != XFS_DINODE_FMT_LOCAL)
return __this_address;
}
if (be64_to_cpu(dip->di_size) > fork_size &&
fork_format == XFS_DINODE_FMT_LOCAL)
return __this_address;
}
switch (fork_format) {
case XFS_DINODE_FMT_LOCAL: case XFS_DINODE_FMT_LOCAL:
/* /*
* no local regular files yet * No local regular files yet.
*/ */
if (whichfork == XFS_DATA_FORK) { if (S_ISREG(mode) && whichfork == XFS_DATA_FORK)
if (S_ISREG(be16_to_cpu(dip->di_mode))) return __this_address;
return __this_address;
if (be64_to_cpu(dip->di_size) >
XFS_DFORK_SIZE(dip, mp, whichfork))
return __this_address;
}
if (di_nextents) if (di_nextents)
return __this_address; return __this_address;
break; break;
......
...@@ -30,6 +30,47 @@ ...@@ -30,6 +30,47 @@
* Physical superblock buffer manipulations. Shared with libxfs in userspace. * Physical superblock buffer manipulations. Shared with libxfs in userspace.
*/ */
/*
* Check that all the V4 feature bits that the V5 filesystem format requires are
* correctly set.
*/
static bool
xfs_sb_validate_v5_features(
struct xfs_sb *sbp)
{
/* We must not have any unknown V4 feature bits set */
if (sbp->sb_versionnum & ~XFS_SB_VERSION_OKBITS)
return false;
/*
* The CRC bit is considered an invalid V4 flag, so we have to add it
* manually to the OKBITS mask.
*/
if (sbp->sb_features2 & ~(XFS_SB_VERSION2_OKBITS |
XFS_SB_VERSION2_CRCBIT))
return false;
/* Now check all the required V4 feature flags are set. */
#define V5_VERS_FLAGS (XFS_SB_VERSION_NLINKBIT | \
XFS_SB_VERSION_ALIGNBIT | \
XFS_SB_VERSION_LOGV2BIT | \
XFS_SB_VERSION_EXTFLGBIT | \
XFS_SB_VERSION_DIRV2BIT | \
XFS_SB_VERSION_MOREBITSBIT)
#define V5_FEAT_FLAGS (XFS_SB_VERSION2_LAZYSBCOUNTBIT | \
XFS_SB_VERSION2_ATTR2BIT | \
XFS_SB_VERSION2_PROJID32BIT | \
XFS_SB_VERSION2_CRCBIT)
if ((sbp->sb_versionnum & V5_VERS_FLAGS) != V5_VERS_FLAGS)
return false;
if ((sbp->sb_features2 & V5_FEAT_FLAGS) != V5_FEAT_FLAGS)
return false;
return true;
}
/* /*
* We support all XFS versions newer than a v4 superblock with V2 directories. * We support all XFS versions newer than a v4 superblock with V2 directories.
*/ */
...@@ -37,9 +78,19 @@ bool ...@@ -37,9 +78,19 @@ bool
xfs_sb_good_version( xfs_sb_good_version(
struct xfs_sb *sbp) struct xfs_sb *sbp)
{ {
/* all v5 filesystems are supported */ /*
* All v5 filesystems are supported, but we must check that all the
* required v4 feature flags are enabled correctly as the code checks
* those flags and not for v5 support.
*/
if (xfs_sb_is_v5(sbp)) if (xfs_sb_is_v5(sbp))
return true; return xfs_sb_validate_v5_features(sbp);
/* We must not have any unknown v4 feature bits set */
if ((sbp->sb_versionnum & ~XFS_SB_VERSION_OKBITS) ||
((sbp->sb_versionnum & XFS_SB_VERSION_MOREBITSBIT) &&
(sbp->sb_features2 & ~XFS_SB_VERSION2_OKBITS)))
return false;
/* versions prior to v4 are not supported */ /* versions prior to v4 are not supported */
if (XFS_SB_VERSION_NUM(sbp) < XFS_SB_VERSION_4) if (XFS_SB_VERSION_NUM(sbp) < XFS_SB_VERSION_4)
...@@ -51,12 +102,6 @@ xfs_sb_good_version( ...@@ -51,12 +102,6 @@ xfs_sb_good_version(
if (!(sbp->sb_versionnum & XFS_SB_VERSION_EXTFLGBIT)) if (!(sbp->sb_versionnum & XFS_SB_VERSION_EXTFLGBIT))
return false; return false;
/* And must not have any unknown v4 feature bits set */
if ((sbp->sb_versionnum & ~XFS_SB_VERSION_OKBITS) ||
((sbp->sb_versionnum & XFS_SB_VERSION_MOREBITSBIT) &&
(sbp->sb_features2 & ~XFS_SB_VERSION2_OKBITS)))
return false;
/* It's a supported v4 filesystem */ /* It's a supported v4 filesystem */
return true; return true;
} }
...@@ -70,6 +115,8 @@ xfs_sb_version_to_features( ...@@ -70,6 +115,8 @@ xfs_sb_version_to_features(
/* optional V4 features */ /* optional V4 features */
if (sbp->sb_rblocks > 0) if (sbp->sb_rblocks > 0)
features |= XFS_FEAT_REALTIME; features |= XFS_FEAT_REALTIME;
if (sbp->sb_versionnum & XFS_SB_VERSION_NLINKBIT)
features |= XFS_FEAT_NLINK;
if (sbp->sb_versionnum & XFS_SB_VERSION_ATTRBIT) if (sbp->sb_versionnum & XFS_SB_VERSION_ATTRBIT)
features |= XFS_FEAT_ATTR; features |= XFS_FEAT_ATTR;
if (sbp->sb_versionnum & XFS_SB_VERSION_QUOTABIT) if (sbp->sb_versionnum & XFS_SB_VERSION_QUOTABIT)
...@@ -265,12 +312,15 @@ xfs_validate_sb_common( ...@@ -265,12 +312,15 @@ xfs_validate_sb_common(
bool has_dalign; bool has_dalign;
if (!xfs_verify_magic(bp, dsb->sb_magicnum)) { if (!xfs_verify_magic(bp, dsb->sb_magicnum)) {
xfs_warn(mp, "bad magic number"); xfs_warn(mp,
"Superblock has bad magic number 0x%x. Not an XFS filesystem?",
be32_to_cpu(dsb->sb_magicnum));
return -EWRONGFS; return -EWRONGFS;
} }
if (!xfs_sb_good_version(sbp)) { if (!xfs_sb_good_version(sbp)) {
xfs_warn(mp, "bad version"); xfs_warn(mp,
"Superblock has unknown features enabled or corrupted feature masks.");
return -EWRONGFS; return -EWRONGFS;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment