On Tue, Jun 28, 2022 at 01:49:20PM -0700, Darrick J. Wong wrote: > From: Darrick J. Wong <djwong@xxxxxxxxxx> > > I accidentally tried to xfs_copy an ext4 filesystem, but instead of > rejecting the filesystem, the program instead crashed. I figured out > that zeroing the superblock was enough to trigger this: > > # dd if=/dev/zero of=/dev/sda bs=1024k count=1 > # xfs_copy /dev/sda /dev/sdb > Floating point exception > > The exact crash happens in this line from libxfs_getbuf_flags, which is > called from the main() routine of xfs_copy: > > if (btp == btp->bt_mount->m_ddev_targp) { > (*bpp)->b_pag = xfs_perag_get(btp->bt_mount, > xfs_daddr_to_agno(btp->bt_mount, blkno)); > > The problem here is that the uncached read filled the incore superblock > with zeroes, which means mbuf.sb_agblocks is zero. This causes a > division by zero in xfs_daddr_to_agno, thereby crashing the program. > > In commit f8b581d6, we made it so that xfs_buf structures contain a > passive reference to the associated perag structure. That commit > assumes that no program would try a cached buffer read until the buffer > cache is fully set up, which is true throughout xfsprogs... except for > the beginning of xfs_copy. For whatever reason, it attempts an uncached > read of the superblock to figure out the real superblock size, then > performs a *cached* read with the proper buffer length and verifier. > The cached read crashes the program. > > Fix the problem by changing the (second) cached read into an uncached read. > > Fixes: f8b581d6 ("libxfs: actually make buffers track the per-ag structures") > Signed-off-by: Darrick J. Wong <djwong@xxxxxxxxxx> > --- > copy/xfs_copy.c | 2 +- > 1 file changed, 1 insertion(+), 1 deletion(-) > > > diff --git a/copy/xfs_copy.c b/copy/xfs_copy.c > index 41f594bd..79f65946 100644 > --- a/copy/xfs_copy.c > +++ b/copy/xfs_copy.c > @@ -748,7 +748,7 @@ main(int argc, char **argv) > /* Do it again, now with proper length and verifier */ > libxfs_buf_relse(sbp); > > - error = -libxfs_buf_read(mbuf.m_ddev_targp, XFS_SB_DADDR, > + error = -libxfs_buf_read_uncached(mbuf.m_ddev_targp, XFS_SB_DADDR, > 1 << (sb->sb_sectlog - BBSHIFT), 0, &sbp, > &xfs_sb_buf_ops); > if (error) { Looks good. Reviewed-by: Dave Chinner <dchinner@xxxxxxxxxx> -- Dave Chinner david@xxxxxxxxxxxxx