Commit 402eef10 authored by Darrick J. Wong's avatar Darrick J. Wong
Browse files

xfs: validate directory leaf buffer owners



Check the owner field of directory leaf blocks.

Signed-off-by: default avatarDarrick J. Wong <djwong@kernel.org>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
parent d44bea9b
Loading
Loading
Loading
Loading
+16 −0
Original line number Diff line number Diff line
@@ -288,8 +288,12 @@ xfs_da3_header_check(
		return xfs_attr3_leaf_header_check(bp, owner);
	case cpu_to_be16(XFS_DA3_NODE_MAGIC):
		return xfs_da3_node_header_check(bp, owner);
	case cpu_to_be16(XFS_DIR3_LEAF1_MAGIC):
	case cpu_to_be16(XFS_DIR3_LEAFN_MAGIC):
		return xfs_dir3_leaf_header_check(bp, owner);
	}

	ASSERT(0);
	return NULL;
}

@@ -1700,6 +1704,12 @@ xfs_da3_node_lookup_int(

		if (magic == XFS_DIR2_LEAFN_MAGIC ||
		    magic == XFS_DIR3_LEAFN_MAGIC) {
			fa = xfs_dir3_leaf_header_check(blk->bp, args->owner);
			if (fa) {
				__xfs_buf_mark_corrupt(blk->bp, fa);
				xfs_da_mark_sick(args);
				return -EFSCORRUPTED;
			}
			blk->magic = XFS_DIR2_LEAFN_MAGIC;
			blk->hashval = xfs_dir2_leaf_lasthash(args->dp,
							      blk->bp, NULL);
@@ -2208,6 +2218,12 @@ xfs_da3_path_shift(
			break;
		case XFS_DIR2_LEAFN_MAGIC:
		case XFS_DIR3_LEAFN_MAGIC:
			fa = xfs_dir3_leaf_header_check(blk->bp, args->owner);
			if (fa) {
				__xfs_buf_mark_corrupt(blk->bp, fa);
				xfs_da_mark_sick(args);
				return -EFSCORRUPTED;
			}
			blk->magic = XFS_DIR2_LEAFN_MAGIC;
			ASSERT(level == path->active-1);
			blk->index = 0;
+2 −0
Original line number Diff line number Diff line
@@ -101,6 +101,8 @@ extern struct xfs_dir2_data_free *xfs_dir2_data_freefind(

extern int xfs_dir_ino_validate(struct xfs_mount *mp, xfs_ino_t ino);

xfs_failaddr_t xfs_dir3_leaf_header_check(struct xfs_buf *bp, xfs_ino_t owner);

extern const struct xfs_buf_ops xfs_dir3_block_buf_ops;
extern const struct xfs_buf_ops xfs_dir3_leafn_buf_ops;
extern const struct xfs_buf_ops xfs_dir3_leaf1_buf_ops;
+59 −6
Original line number Diff line number Diff line
@@ -208,6 +208,29 @@ xfs_dir3_leaf_verify(
	return xfs_dir3_leaf_check_int(mp, &leafhdr, bp->b_addr, true);
}

xfs_failaddr_t
xfs_dir3_leaf_header_check(
	struct xfs_buf		*bp,
	xfs_ino_t		owner)
{
	struct xfs_mount	*mp = bp->b_mount;

	if (xfs_has_crc(mp)) {
		struct xfs_dir3_leaf *hdr3 = bp->b_addr;

		if (hdr3->hdr.info.hdr.magic !=
					cpu_to_be16(XFS_DIR3_LEAF1_MAGIC) &&
		    hdr3->hdr.info.hdr.magic !=
					cpu_to_be16(XFS_DIR3_LEAFN_MAGIC))
			return __this_address;

		if (be64_to_cpu(hdr3->hdr.info.owner) != owner)
			return __this_address;
	}

	return NULL;
}

static void
xfs_dir3_leaf_read_verify(
	struct xfs_buf  *bp)
@@ -271,32 +294,60 @@ int
xfs_dir3_leaf_read(
	struct xfs_trans	*tp,
	struct xfs_inode	*dp,
	xfs_ino_t		owner,
	xfs_dablk_t		fbno,
	struct xfs_buf		**bpp)
{
	xfs_failaddr_t		fa;
	int			err;

	err = xfs_da_read_buf(tp, dp, fbno, 0, bpp, XFS_DATA_FORK,
			&xfs_dir3_leaf1_buf_ops);
	if (!err && tp && *bpp)
		xfs_trans_buf_set_type(tp, *bpp, XFS_BLFT_DIR_LEAF1_BUF);
	if (err || !(*bpp))
		return err;

	fa = xfs_dir3_leaf_header_check(*bpp, owner);
	if (fa) {
		__xfs_buf_mark_corrupt(*bpp, fa);
		xfs_trans_brelse(tp, *bpp);
		*bpp = NULL;
		xfs_dirattr_mark_sick(dp, XFS_DATA_FORK);
		return -EFSCORRUPTED;
	}

	if (tp)
		xfs_trans_buf_set_type(tp, *bpp, XFS_BLFT_DIR_LEAF1_BUF);
	return 0;
}

int
xfs_dir3_leafn_read(
	struct xfs_trans	*tp,
	struct xfs_inode	*dp,
	xfs_ino_t		owner,
	xfs_dablk_t		fbno,
	struct xfs_buf		**bpp)
{
	xfs_failaddr_t		fa;
	int			err;

	err = xfs_da_read_buf(tp, dp, fbno, 0, bpp, XFS_DATA_FORK,
			&xfs_dir3_leafn_buf_ops);
	if (!err && tp && *bpp)
		xfs_trans_buf_set_type(tp, *bpp, XFS_BLFT_DIR_LEAFN_BUF);
	if (err || !(*bpp))
		return err;

	fa = xfs_dir3_leaf_header_check(*bpp, owner);
	if (fa) {
		__xfs_buf_mark_corrupt(*bpp, fa);
		xfs_trans_brelse(tp, *bpp);
		*bpp = NULL;
		xfs_dirattr_mark_sick(dp, XFS_DATA_FORK);
		return -EFSCORRUPTED;
	}

	if (tp)
		xfs_trans_buf_set_type(tp, *bpp, XFS_BLFT_DIR_LEAFN_BUF);
	return 0;
}

/*
@@ -646,7 +697,8 @@ xfs_dir2_leaf_addname(

	trace_xfs_dir2_leaf_addname(args);

	error = xfs_dir3_leaf_read(tp, dp, args->geo->leafblk, &lbp);
	error = xfs_dir3_leaf_read(tp, dp, args->owner, args->geo->leafblk,
			&lbp);
	if (error)
		return error;

@@ -1237,7 +1289,8 @@ xfs_dir2_leaf_lookup_int(
	tp = args->trans;
	mp = dp->i_mount;

	error = xfs_dir3_leaf_read(tp, dp, args->geo->leafblk, &lbp);
	error = xfs_dir3_leaf_read(tp, dp, args->owner, args->geo->leafblk,
			&lbp);
	if (error)
		return error;

+2 −1
Original line number Diff line number Diff line
@@ -1562,7 +1562,8 @@ xfs_dir2_leafn_toosmall(
		/*
		 * Read the sibling leaf block.
		 */
		error = xfs_dir3_leafn_read(state->args->trans, dp, blkno, &bp);
		error = xfs_dir3_leafn_read(state->args->trans, dp,
				state->args->owner, blkno, &bp);
		if (error)
			return error;

+2 −2
Original line number Diff line number Diff line
@@ -95,9 +95,9 @@ void xfs_dir2_leaf_hdr_from_disk(struct xfs_mount *mp,
void xfs_dir2_leaf_hdr_to_disk(struct xfs_mount *mp, struct xfs_dir2_leaf *to,
		struct xfs_dir3_icleaf_hdr *from);
int xfs_dir3_leaf_read(struct xfs_trans *tp, struct xfs_inode *dp,
		xfs_dablk_t fbno, struct xfs_buf **bpp);
		xfs_ino_t owner, xfs_dablk_t fbno, struct xfs_buf **bpp);
int xfs_dir3_leafn_read(struct xfs_trans *tp, struct xfs_inode *dp,
		xfs_dablk_t fbno, struct xfs_buf **bpp);
		xfs_ino_t owner, xfs_dablk_t fbno, struct xfs_buf **bpp);
extern int xfs_dir2_block_to_leaf(struct xfs_da_args *args,
		struct xfs_buf *dbp);
extern int xfs_dir2_leaf_addname(struct xfs_da_args *args);
Loading