Re: [PATCH v3] xfs_db: make sure agblocks is valid to prevent corruption

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Tue, Sep 03, 2024 at 06:24:02PM GMT, liuhuan01@xxxxxxxxxx wrote:
> From: liuh <liuhuan01@xxxxxxxxxx>
> 
> Recently, I was testing xfstests. When I run xfs/350 case, it always generate coredump during the process.
> 	xfs_db -c "sb 0" -c "p agblocks" /dev/loop1

Please, don't send new versions in reply to old versions.

Carlos

> 
> System will generate signal SIGFPE corrupt the process. And the stack as follow:
> corrupt at: (*bpp)->b_pag = xfs_perag_get(btp->bt_mount, xfs_daddr_to_agno(btp->bt_mount, blkno)); in function libxfs_getbuf_flags
> 	#0  libxfs_getbuf_flags
> 	#1  libxfs_getbuf_flags
> 	#2  libxfs_buf_read_map
> 	#3  libxfs_buf_read
> 	#4  libxfs_mount
> 	#5  init
> 	#6  main
> 
> The coredump was caused by the corrupt superblock metadata: (mp)->m_sb.sb_agblocks, it was 0.
> In this case, user cannot run in expert mode also.
> 
> So, try to get agblocks from agf/agi 0, if agf/agi 0 length match, use it as agblocks.
> If failed use the default geometry to calc agblocks.
> 
> Signed-off-by: liuh <liuhuan01@xxxxxxxxxx>
> ---
>  db/Makefile |   2 +-
>  db/init.c   | 142 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 143 insertions(+), 1 deletion(-)
> 
> diff --git a/db/Makefile b/db/Makefile
> index 83389376..322d5617 100644
> --- a/db/Makefile
> +++ b/db/Makefile
> @@ -68,7 +68,7 @@ CFILES = $(HFILES:.h=.c) \
>  LSRCFILES = xfs_admin.sh xfs_ncheck.sh xfs_metadump.sh
>  
>  LLDLIBS	= $(LIBXFS) $(LIBXLOG) $(LIBFROG) $(LIBUUID) $(LIBRT) $(LIBURCU) \
> -	  $(LIBPTHREAD)
> +	  $(LIBPTHREAD) $(LIBBLKID)
>  LTDEPENDENCIES = $(LIBXFS) $(LIBXLOG) $(LIBFROG)
>  LLDFLAGS += -static-libtool-libs
>  
> diff --git a/db/init.c b/db/init.c
> index cea25ae5..167bc777 100644
> --- a/db/init.c
> +++ b/db/init.c
> @@ -38,6 +38,138 @@ usage(void)
>  	exit(1);
>  }
>  
> +static void
> +xfs_guess_default_ag_geometry(uint64_t *agsize, uint64_t *agcount, struct libxfs_init *x)
> +{
> +	struct fs_topology	ft;
> +	int			blocklog;
> +	uint64_t		dblocks;
> +	int			multidisk;
> +
> +	fprintf(stderr, "Attempting to guess AG length from device geometry. This may not work.\n");
> +
> +	memset(&ft, 0, sizeof(ft));
> +	get_topology(x, &ft, 1);
> +
> +	/*
> +	 * get geometry from get_topology result.
> +	 * Use default block size (2^12)
> +	 */
> +	blocklog = 12;
> +	multidisk = ft.data.swidth | ft.data.sunit;
> +	dblocks = x->data.size >> (blocklog - BBSHIFT);
> +	calc_default_ag_geometry(blocklog, dblocks, multidisk,
> +				 agsize, agcount);
> +
> +	if (*agsize >= XFS_MIN_AG_BLOCKS && *agsize <= XFS_MAX_AG_BLOCKS)
> +		fprintf(stderr, "Guessed AG length is %lu blocks.\n", *agsize);
> +}
> +
> +static xfs_agblock_t
> +xfs_get_agblock_from_agf(struct xfs_mount *mp)
> +{
> +	xfs_agblock_t agblocks = NULLAGBLOCK;
> +	int error;
> +	struct xfs_buf *bp;
> +	struct xfs_agf *agf;
> +
> +	error = -libxfs_buf_read_uncached(mp->m_ddev_targp,
> +			XFS_AGF_DADDR(mp), 1, 0, &bp, NULL);
> +	if (error) {
> +		fprintf(stderr, "AGF 0 length recovery failed\n");
> +		return NULLAGBLOCK;
> +	}
> +
> +	agf = bp->b_addr;
> +	if (be32_to_cpu(agf->agf_magicnum) == XFS_AGF_MAGIC)
> +		agblocks = be32_to_cpu(agf->agf_length);
> +
> +	libxfs_buf_relse(bp);
> +
> +	if (agblocks != NULLAGBLOCK)
> +		fprintf(stderr, "AGF 0 length %u blocks found.\n", agblocks);
> +	else
> +		fprintf(stderr, "AGF 0 length recovery failed.\n");
> +
> +	return agblocks;
> +}
> +
> +static xfs_agblock_t
> +xfs_get_agblock_from_agi(struct xfs_mount *mp)
> +{
> +	xfs_agblock_t agblocks = NULLAGBLOCK;
> +	int error;
> +	struct xfs_buf *bp;
> +	struct xfs_agi *agi;
> +
> +	error = -libxfs_buf_read_uncached(mp->m_ddev_targp,
> +			XFS_AGI_DADDR(mp), 1, 0, &bp, NULL);
> +	if (error) {
> +		fprintf(stderr, "AGI 0 length recovery failed\n");
> +		return NULLAGBLOCK;
> +	}
> +
> +
> +	agi = bp->b_addr;
> +	if (be32_to_cpu(agi->agi_magicnum) == XFS_AGI_MAGIC)
> +		agblocks = be32_to_cpu(agi->agi_length);
> +
> +	libxfs_buf_relse(bp);
> +
> +	if (agblocks != NULLAGBLOCK)
> +		fprintf(stderr, "AGI 0 length %u blocks found.\n", agblocks);
> +	else
> +		fprintf(stderr, "AGI 0 length recovery failed.\n");
> +
> +	return agblocks;
> +}
> +
> +/*
> + * Try to get it from agf/agi length when primary superblock agblocks damaged.
> + * If agf matchs agi length, use it as agblocks, otherwise use the default geometry
> + * to calc agblocks
> + */
> +static xfs_agblock_t
> +xfs_try_get_agblocks(struct xfs_mount *mp, struct libxfs_init *x)
> +{
> +	xfs_agblock_t agblocks = NULLAGBLOCK;
> +	xfs_agblock_t agblocks_agf, agblocks_agi;
> +	uint64_t agsize, agcount;
> +
> +	fprintf(stderr, "Attempting recovery from AGF/AGI 0 metadata...\n");
> +
> +	agblocks_agf = xfs_get_agblock_from_agf(mp);
> +	agblocks_agi = xfs_get_agblock_from_agi(mp);
> +
> +	if (agblocks_agf == agblocks_agi && agblocks_agf >= XFS_MIN_AG_BLOCKS && agblocks_agf <= XFS_MAX_AG_BLOCKS) {
> +		fprintf(stderr, "AGF/AGI 0 length matches.\n");
> +		fprintf(stderr, "Using %u blocks for superblock agblocks\n", agblocks_agf);
> +		return agblocks_agf;
> +	}
> +
> +	/* use default geometry to calc agblocks/agcount */
> +	xfs_guess_default_ag_geometry(&agsize, &agcount, x);
> +
> +	/* choose the agblocks among agf/agi length and agsize */
> +	if (agblocks_agf == agsize && agsize >= XFS_MIN_AG_BLOCKS && agsize <= XFS_MAX_AG_BLOCKS) {
> +		fprintf(stderr, "Guessed AG matchs AGF length\n");
> +		agblocks = agsize;
> +	} else if (agblocks_agi == agsize && agsize >= XFS_MIN_AG_BLOCKS && agsize <= XFS_MAX_AG_BLOCKS) {
> +		fprintf(stderr, "Guessed AG matchs AGI length\n");
> +		agblocks = agsize;
> +	} else if (agsize >= XFS_MIN_AG_BLOCKS && agsize <= XFS_MAX_AG_BLOCKS) {
> +		fprintf(stderr, "Guessed AG does not match AGF/AGI 0 length.\n");
> +		agblocks =  agsize;
> +	} else {
> +		fprintf(stderr, "_(%s: device too small to hold a valid XFS filesystem)", progname);
> +		exit(1);
> +	}
> +
> +	fprintf(stderr, "Using %u blocks for superblock agblocks.\n", agblocks);
> +
> +	return agblocks;
> +}
> +
>  static void
>  init(
>  	int		argc,
> @@ -129,6 +261,16 @@ init(
>  		}
>  	}
>  
> +	/* If sb_agblocks was damaged, try to get agblocks */
> +	if (sbp->sb_agblocks < XFS_MIN_AG_BLOCKS || sbp->sb_agblocks > XFS_MAX_AG_BLOCKS) {
> +		xfs_agblock_t agblocks;
> +
> +		fprintf(stderr, "Out of bounds superblock agblocks (%u) found.\n", sbp->sb_agblocks);
> +
> +		agblocks = xfs_try_get_agblocks(&xmount, &x);
> +		sbp->sb_agblocks = agblocks;
> +	}
> +
>  	agcount = sbp->sb_agcount;
>  	mp = libxfs_mount(&xmount, sbp, &x, LIBXFS_MOUNT_DEBUGGER);
>  	if (!mp) {
> -- 
> 2.43.0
> 
> 




[Index of Archives]     [XFS Filesystem Development (older mail)]     [Linux Filesystem Development]     [Linux Audio Users]     [Yosemite Trails]     [Linux Kernel]     [Linux RAID]     [Linux SCSI]


  Powered by Linux