diff options
author | Darrick J. Wong <darrick.wong@oracle.com> | 2018-06-04 13:58:34 -0700 |
---|---|---|
committer | Darrick J. Wong <darrick.wong@oracle.com> | 2018-06-04 18:25:05 -0700 |
commit | e63a1008ee08334ea6a9c9edb59b1d2763f5b231 (patch) | |
tree | e831aa46e58916f523a26409f42a1cc8feaf8f23 /fs/xfs | |
parent | 4cbae4b816fd61e63abbe8330741e257fa95d4c5 (diff) | |
download | linux-e63a1008ee08334ea6a9c9edb59b1d2763f5b231.tar.bz2 |
xfs: strengthen btree pointer checks before use
Instead of ASSERTing on null btree pointers in xfs_btree_ptr_to_daddr,
use the new block number verifiers to ensure that the btree pointer
doesn't point to any sensitive areas (AG headers, past-EOFS) and return
-EFSCORRUPTED if this is the case. Remove the ASSERT because on-disk
corruptions shouldn't trigger ASSERTs.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Diffstat (limited to 'fs/xfs')
-rw-r--r-- | fs/xfs/libxfs/xfs_btree.c | 50 |
1 files changed, 35 insertions, 15 deletions
diff --git a/fs/xfs/libxfs/xfs_btree.c b/fs/xfs/libxfs/xfs_btree.c index f30d74d807e9..3fcddfeaa662 100644 --- a/fs/xfs/libxfs/xfs_btree.c +++ b/fs/xfs/libxfs/xfs_btree.c @@ -992,22 +992,30 @@ xfs_btree_readahead( return xfs_btree_readahead_sblock(cur, lr, block); } -STATIC xfs_daddr_t +STATIC int xfs_btree_ptr_to_daddr( struct xfs_btree_cur *cur, - union xfs_btree_ptr *ptr) + union xfs_btree_ptr *ptr, + xfs_daddr_t *daddr) { - if (cur->bc_flags & XFS_BTREE_LONG_PTRS) { - ASSERT(ptr->l != cpu_to_be64(NULLFSBLOCK)); + xfs_fsblock_t fsbno; + xfs_agblock_t agbno; + int error; - return XFS_FSB_TO_DADDR(cur->bc_mp, be64_to_cpu(ptr->l)); - } else { - ASSERT(cur->bc_private.a.agno != NULLAGNUMBER); - ASSERT(ptr->s != cpu_to_be32(NULLAGBLOCK)); + error = xfs_btree_check_ptr(cur, ptr, 0, 1); + if (error) + return error; - return XFS_AGB_TO_DADDR(cur->bc_mp, cur->bc_private.a.agno, - be32_to_cpu(ptr->s)); + if (cur->bc_flags & XFS_BTREE_LONG_PTRS) { + fsbno = be64_to_cpu(ptr->l); + *daddr = XFS_FSB_TO_DADDR(cur->bc_mp, fsbno); + } else { + agbno = be32_to_cpu(ptr->s); + *daddr = XFS_AGB_TO_DADDR(cur->bc_mp, cur->bc_private.a.agno, + agbno); } + + return 0; } /* @@ -1022,8 +1030,11 @@ xfs_btree_readahead_ptr( union xfs_btree_ptr *ptr, xfs_extlen_t count) { - xfs_buf_readahead(cur->bc_mp->m_ddev_targp, - xfs_btree_ptr_to_daddr(cur, ptr), + xfs_daddr_t daddr; + + if (xfs_btree_ptr_to_daddr(cur, ptr, &daddr)) + return; + xfs_buf_readahead(cur->bc_mp->m_ddev_targp, daddr, cur->bc_mp->m_bsize * count, cur->bc_ops->buf_ops); } @@ -1286,11 +1297,14 @@ xfs_btree_get_buf_block( { struct xfs_mount *mp = cur->bc_mp; xfs_daddr_t d; + int error; /* need to sort out how callers deal with failures first */ ASSERT(!(flags & XBF_TRYLOCK)); - d = xfs_btree_ptr_to_daddr(cur, ptr); + error = xfs_btree_ptr_to_daddr(cur, ptr, &d); + if (error) + return error; *bpp = xfs_trans_get_buf(cur->bc_tp, mp->m_ddev_targp, d, mp->m_bsize, flags); @@ -1321,7 +1335,9 @@ xfs_btree_read_buf_block( /* need to sort out how callers deal with failures first */ ASSERT(!(flags & XBF_TRYLOCK)); - d = xfs_btree_ptr_to_daddr(cur, ptr); + error = xfs_btree_ptr_to_daddr(cur, ptr, &d); + if (error) + return error; error = xfs_trans_read_buf(mp, cur->bc_tp, mp->m_ddev_targp, d, mp->m_bsize, flags, bpp, cur->bc_ops->buf_ops); @@ -1768,6 +1784,7 @@ xfs_btree_lookup_get_block( struct xfs_btree_block **blkp) /* return btree block */ { struct xfs_buf *bp; /* buffer pointer for btree block */ + xfs_daddr_t daddr; int error = 0; /* special case the root block if in an inode */ @@ -1784,7 +1801,10 @@ xfs_btree_lookup_get_block( * Otherwise throw it away and get a new one. */ bp = cur->bc_bufs[level]; - if (bp && XFS_BUF_ADDR(bp) == xfs_btree_ptr_to_daddr(cur, pp)) { + error = xfs_btree_ptr_to_daddr(cur, pp, &daddr); + if (error) + return error; + if (bp && XFS_BUF_ADDR(bp) == daddr) { *blkp = XFS_BUF_TO_BLOCK(bp); return 0; } |