Commit 64c3dd0b authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'xfs-6.1-fixes-4' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux

Pull xfs fixes from Darrick Wong:
 "Dave and I had thought that this would be a very quiet cycle, but we
  thought wrong.

  At first there were the usual trickle of minor bugfixes, but then
  Zorro pulled -rc1 and noticed complaints about the stronger memcpy
  checks w.r.t. flex arrays.

  Analyzing how to fix that revealed a bunch of validation gaps in
  validating ondisk log items during recovery, and then a customer hit
  an infinite loop in the refcounting code on a corrupt filesystem.

  So. This largeish batch of fixes addresses all those problems, I hope.

  Summary:

   - Fix a UAF bug during log recovery

   - Fix memory leaks when mount fails

   - Detect corrupt bestfree information in a directory block

   - Fix incorrect return value type for the dax page fault handlers

   - Fix fortify complaints about memcpy of xfs log item objects

   - Strengthen inadequate validation of recovered log items

   - Fix incorrectly declared flex array in EFI log item structs

   - Log corrupt log items for debugging purposes

   - Fix infinite loop problems in the refcount code if the refcount
     btree node block keys are corrupt

   - Fix infinite loop problems in the refcount code if the refcount
     btree records suffer MSB bitflips

   - Add more sanity checking to continued defer ops to prevent
     overflows from one AG to the next or off EOFS"

* tag 'xfs-6.1-fixes-4' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux: (28 commits)
  xfs: rename XFS_REFC_COW_START to _COWFLAG
  xfs: fix uninitialized list head in struct xfs_refcount_recovery
  xfs: fix agblocks check in the cow leftover recovery function
  xfs: check record domain when accessing refcount records
  xfs: remove XFS_FIND_RCEXT_SHARED and _COW
  xfs: refactor domain and refcount checking
  xfs: report refcount domain in tracepoints
  xfs: track cow/shared record domains explicitly in xfs_refcount_irec
  xfs: refactor refcount record usage in xchk_refcountbt_rec
  xfs: dump corrupt recovered log intent items to dmesg consistently
  xfs: move _irec structs to xfs_types.h
  xfs: actually abort log recovery on corrupt intent-done log items
  xfs: check deferred refcount op continuation parameters
  xfs: refactor all the EFI/EFD log item sizeof logic
  xfs: create a predicate to verify per-AG extents
  xfs: fix memcpy fortify errors in EFI log format copying
  xfs: make sure aglen never goes negative in xfs_refcount_adjust_extents
  xfs: fix memcpy fortify errors in RUI log format copying
  xfs: fix memcpy fortify errors in CUI log format copying
  xfs: fix memcpy fortify errors in BUI log format copying
  ...
parents 5d8401be 4eb559dd
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
@@ -133,6 +133,21 @@ xfs_verify_agbno(struct xfs_perag *pag, xfs_agblock_t agbno)
	return true;
}

static inline bool
xfs_verify_agbext(
	struct xfs_perag	*pag,
	xfs_agblock_t		agbno,
	xfs_agblock_t		len)
{
	if (agbno + len <= agbno)
		return false;

	if (!xfs_verify_agbno(pag, agbno))
		return false;

	return xfs_verify_agbno(pag, agbno + len - 1);
}

/*
 * Verify that an AG inode number pointer neither points outside the AG
 * nor points at static metadata.
+1 −5
Original line number Diff line number Diff line
@@ -263,11 +263,7 @@ xfs_alloc_get_rec(
		goto out_bad_rec;

	/* check for valid extent range, including overflow */
	if (!xfs_verify_agbno(pag, *bno))
		goto out_bad_rec;
	if (*bno > *bno + *len)
		goto out_bad_rec;
	if (!xfs_verify_agbno(pag, *bno + *len - 1))
	if (!xfs_verify_agbext(pag, *bno, *len))
		goto out_bad_rec;

	return 0;
+7 −2
Original line number Diff line number Diff line
@@ -146,6 +146,8 @@ xfs_dir3_leaf_check_int(
	xfs_dir2_leaf_tail_t		*ltp;
	int				stale;
	int				i;
	bool				isleaf1 = (hdr->magic == XFS_DIR2_LEAF1_MAGIC ||
						   hdr->magic == XFS_DIR3_LEAF1_MAGIC);

	ltp = xfs_dir2_leaf_tail_p(geo, leaf);

@@ -158,8 +160,7 @@ xfs_dir3_leaf_check_int(
		return __this_address;

	/* Leaves and bests don't overlap in leaf format. */
	if ((hdr->magic == XFS_DIR2_LEAF1_MAGIC ||
	     hdr->magic == XFS_DIR3_LEAF1_MAGIC) &&
	if (isleaf1 &&
	    (char *)&hdr->ents[hdr->count] > (char *)xfs_dir2_leaf_bests_p(ltp))
		return __this_address;

@@ -175,6 +176,10 @@ xfs_dir3_leaf_check_int(
		}
		if (hdr->ents[i].address == cpu_to_be32(XFS_DIR2_NULL_DATAPTR))
			stale++;
		if (isleaf1 && xfs_dir2_dataptr_to_db(geo,
				be32_to_cpu(hdr->ents[i].address)) >=
				be32_to_cpu(ltp->bestcount))
			return __this_address;
	}
	if (hdr->stale != stale)
		return __this_address;
+1 −21
Original line number Diff line number Diff line
@@ -1564,20 +1564,6 @@ struct xfs_rmap_rec {
#define RMAPBT_UNUSED_OFFSET_BITLEN	7
#define RMAPBT_OFFSET_BITLEN		54

#define XFS_RMAP_ATTR_FORK		(1 << 0)
#define XFS_RMAP_BMBT_BLOCK		(1 << 1)
#define XFS_RMAP_UNWRITTEN		(1 << 2)
#define XFS_RMAP_KEY_FLAGS		(XFS_RMAP_ATTR_FORK | \
					 XFS_RMAP_BMBT_BLOCK)
#define XFS_RMAP_REC_FLAGS		(XFS_RMAP_UNWRITTEN)
struct xfs_rmap_irec {
	xfs_agblock_t	rm_startblock;	/* extent start block */
	xfs_extlen_t	rm_blockcount;	/* extent length */
	uint64_t	rm_owner;	/* extent owner */
	uint64_t	rm_offset;	/* offset within the owner */
	unsigned int	rm_flags;	/* state flags */
};

/*
 * Key structure
 *
@@ -1626,7 +1612,7 @@ unsigned int xfs_refc_block(struct xfs_mount *mp);
 * on the startblock.  This speeds up mount time deletion of stale
 * staging extents because they're all at the right side of the tree.
 */
#define XFS_REFC_COW_START		((xfs_agblock_t)(1U << 31))
#define XFS_REFC_COWFLAG		(1U << 31)
#define REFCNTBT_COWFLAG_BITLEN		1
#define REFCNTBT_AGBLOCK_BITLEN		31

@@ -1640,12 +1626,6 @@ struct xfs_refcount_key {
	__be32		rc_startblock;	/* starting block number */
};

struct xfs_refcount_irec {
	xfs_agblock_t	rc_startblock;	/* starting block number */
	xfs_extlen_t	rc_blockcount;	/* count of free blocks */
	xfs_nlink_t	rc_refcount;	/* number of inodes linked here */
};

#define MAXREFCOUNT	((xfs_nlink_t)~0U)
#define MAXREFCEXTLEN	((xfs_extlen_t)~0U)

+54 −6
Original line number Diff line number Diff line
@@ -613,25 +613,49 @@ typedef struct xfs_efi_log_format {
	uint16_t		efi_size;	/* size of this item */
	uint32_t		efi_nextents;	/* # extents to free */
	uint64_t		efi_id;		/* efi identifier */
	xfs_extent_t		efi_extents[1];	/* array of extents to free */
	xfs_extent_t		efi_extents[];	/* array of extents to free */
} xfs_efi_log_format_t;

static inline size_t
xfs_efi_log_format_sizeof(
	unsigned int		nr)
{
	return sizeof(struct xfs_efi_log_format) +
			nr * sizeof(struct xfs_extent);
}

typedef struct xfs_efi_log_format_32 {
	uint16_t		efi_type;	/* efi log item type */
	uint16_t		efi_size;	/* size of this item */
	uint32_t		efi_nextents;	/* # extents to free */
	uint64_t		efi_id;		/* efi identifier */
	xfs_extent_32_t		efi_extents[1];	/* array of extents to free */
	xfs_extent_32_t		efi_extents[];	/* array of extents to free */
} __attribute__((packed)) xfs_efi_log_format_32_t;

static inline size_t
xfs_efi_log_format32_sizeof(
	unsigned int		nr)
{
	return sizeof(struct xfs_efi_log_format_32) +
			nr * sizeof(struct xfs_extent_32);
}

typedef struct xfs_efi_log_format_64 {
	uint16_t		efi_type;	/* efi log item type */
	uint16_t		efi_size;	/* size of this item */
	uint32_t		efi_nextents;	/* # extents to free */
	uint64_t		efi_id;		/* efi identifier */
	xfs_extent_64_t		efi_extents[1];	/* array of extents to free */
	xfs_extent_64_t		efi_extents[];	/* array of extents to free */
} xfs_efi_log_format_64_t;

static inline size_t
xfs_efi_log_format64_sizeof(
	unsigned int		nr)
{
	return sizeof(struct xfs_efi_log_format_64) +
			nr * sizeof(struct xfs_extent_64);
}

/*
 * This is the structure used to lay out an efd log item in the
 * log.  The efd_extents array is a variable size array whose
@@ -642,25 +666,49 @@ typedef struct xfs_efd_log_format {
	uint16_t		efd_size;	/* size of this item */
	uint32_t		efd_nextents;	/* # of extents freed */
	uint64_t		efd_efi_id;	/* id of corresponding efi */
	xfs_extent_t		efd_extents[1];	/* array of extents freed */
	xfs_extent_t		efd_extents[];	/* array of extents freed */
} xfs_efd_log_format_t;

static inline size_t
xfs_efd_log_format_sizeof(
	unsigned int		nr)
{
	return sizeof(struct xfs_efd_log_format) +
			nr * sizeof(struct xfs_extent);
}

typedef struct xfs_efd_log_format_32 {
	uint16_t		efd_type;	/* efd log item type */
	uint16_t		efd_size;	/* size of this item */
	uint32_t		efd_nextents;	/* # of extents freed */
	uint64_t		efd_efi_id;	/* id of corresponding efi */
	xfs_extent_32_t		efd_extents[1];	/* array of extents freed */
	xfs_extent_32_t		efd_extents[];	/* array of extents freed */
} __attribute__((packed)) xfs_efd_log_format_32_t;

static inline size_t
xfs_efd_log_format32_sizeof(
	unsigned int		nr)
{
	return sizeof(struct xfs_efd_log_format_32) +
			nr * sizeof(struct xfs_extent_32);
}

typedef struct xfs_efd_log_format_64 {
	uint16_t		efd_type;	/* efd log item type */
	uint16_t		efd_size;	/* size of this item */
	uint32_t		efd_nextents;	/* # of extents freed */
	uint64_t		efd_efi_id;	/* id of corresponding efi */
	xfs_extent_64_t		efd_extents[1];	/* array of extents freed */
	xfs_extent_64_t		efd_extents[];	/* array of extents freed */
} xfs_efd_log_format_64_t;

static inline size_t
xfs_efd_log_format64_sizeof(
	unsigned int		nr)
{
	return sizeof(struct xfs_efd_log_format_64) +
			nr * sizeof(struct xfs_extent_64);
}

/*
 * RUI/RUD (reverse mapping) log format definitions
 */
Loading