Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

xfs: don't allow insert-range to shift extents past the maximum offset



Zorro Lang reports that generic/485 blows an assert on a filesystem with
512 byte blocks.  The test tries to fallocate a post-eof extent at the
maximum file size and calls insert range to shift the extents right by
two blocks.  On a 512b block filesystem this causes startoff to overflow
the 54-bit startoff field, leading to the assert.

Therefore, always check the rightmost extent to see if it would overflow
prior to invoking the insert range machinery.

Reported-by: default avatar <zlang@redhat.com>
Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=200137


Signed-off-by: default avatarDarrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: default avatarAllison Henderson <allison.henderson@oracle.com>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
parent aafe12ce
Loading
Loading
Loading
Loading
+26 −0
Original line number Diff line number Diff line
@@ -5780,6 +5780,32 @@ xfs_bmap_collapse_extents(
	return error;
}

/* Make sure we won't be right-shifting an extent past the maximum bound. */
int
xfs_bmap_can_insert_extents(
	struct xfs_inode	*ip,
	xfs_fileoff_t		off,
	xfs_fileoff_t		shift)
{
	struct xfs_bmbt_irec	got;
	int			is_empty;
	int			error = 0;

	ASSERT(xfs_isilocked(ip, XFS_IOLOCK_EXCL));

	if (XFS_FORCED_SHUTDOWN(ip->i_mount))
		return -EIO;

	xfs_ilock(ip, XFS_ILOCK_EXCL);
	error = xfs_bmap_last_extent(NULL, ip, XFS_DATA_FORK, &got, &is_empty);
	if (!error && !is_empty && got.br_startoff >= off &&
	    ((got.br_startoff + shift) & BMBT_STARTOFF_MASK) < got.br_startoff)
		error = -EINVAL;
	xfs_iunlock(ip, XFS_ILOCK_EXCL);

	return error;
}

int
xfs_bmap_insert_extents(
	struct xfs_trans	*tp,
+2 −0
Original line number Diff line number Diff line
@@ -227,6 +227,8 @@ int xfs_bmap_collapse_extents(struct xfs_trans *tp, struct xfs_inode *ip,
		xfs_fileoff_t *next_fsb, xfs_fileoff_t offset_shift_fsb,
		bool *done, xfs_fsblock_t *firstblock,
		struct xfs_defer_ops *dfops);
int	xfs_bmap_can_insert_extents(struct xfs_inode *ip, xfs_fileoff_t off,
		xfs_fileoff_t shift);
int	xfs_bmap_insert_extents(struct xfs_trans *tp, struct xfs_inode *ip,
		xfs_fileoff_t *next_fsb, xfs_fileoff_t offset_shift_fsb,
		bool *done, xfs_fileoff_t stop_fsb, xfs_fsblock_t *firstblock,
+2 −0
Original line number Diff line number Diff line
@@ -1529,6 +1529,8 @@ typedef struct xfs_bmdr_block {
#define BMBT_STARTBLOCK_BITLEN	52
#define BMBT_BLOCKCOUNT_BITLEN	21

#define BMBT_STARTOFF_MASK	((1ULL << BMBT_STARTOFF_BITLEN) - 1)

typedef struct xfs_bmbt_rec {
	__be64			l0, l1;
} xfs_bmbt_rec_t;
+4 −0
Original line number Diff line number Diff line
@@ -1383,6 +1383,10 @@ xfs_insert_file_space(

	trace_xfs_insert_file_space(ip);

	error = xfs_bmap_can_insert_extents(ip, stop_fsb, shift_fsb);
	if (error)
		return error;

	error = xfs_prepare_shift(ip, offset);
	if (error)
		return error;