aboutsummaryrefslogtreecommitdiffstats
path: root/fs/xfs/libxfs
diff options
context:
space:
mode:
authorDave Chinner <dchinner@redhat.com>2018-09-30 18:11:07 -0400
committerDave Chinner <david@fromorbit.com>2018-09-30 18:11:07 -0400
commite55ec4ddbef9897199c307dfb23167e3801fdaf5 (patch)
treef9912a8d54f79f25f28c3aa0b4dcdb149d769d58 /fs/xfs/libxfs
parent561295a32579e2db3b22f74d1f45d5c4c4fd9375 (diff)
xfs: fix error handling in xfs_bmap_extents_to_btree
Commit 01239d77b9dd ("xfs: fix a null pointer dereference in xfs_bmap_extents_to_btree") attempted to fix a null pointer dreference when a fuzzing corruption of some kind was found. This fix was flawed, resulting in assert failures like: XFS: Assertion failed: ifp->if_broot == NULL, file: fs/xfs/libxfs/xfs_bmap.c, line: 715 ..... Call Trace: xfs_bmap_extents_to_btree+0x6b9/0x7b0 __xfs_bunmapi+0xae7/0xf00 ? xfs_log_reserve+0x1c8/0x290 xfs_reflink_remap_extent+0x20b/0x620 xfs_reflink_remap_blocks+0x7e/0x290 xfs_reflink_remap_range+0x311/0x530 vfs_dedupe_file_range_one+0xd7/0xe0 vfs_dedupe_file_range+0x15b/0x1a0 do_vfs_ioctl+0x267/0x6c0 The problem is that the error handling code now asserts that the inode fork is not in btree format before the error handling code undoes the modifications that put the fork back in extent format. Fix this by moving the assert back to after the xfs_iroot_realloc() call that returns the fork to extent format, and clean up the jump labels to be meaningful. Also, returning ENOSPC when xfs_btree_get_bufl() fails to instantiate the buffer that was allocated (the actual fix in the commit mentioned above) is incorrect. This is a fatal error - only an invalid block address or a filesystem shutdown can result in failing to get a buffer here. Hence change this to EFSCORRUPTED so that the higher layer knows this was a corruption related failure and should not treat it as an ENOSPC error. This should result in a shutdown (via cancelling a dirty transaction) which is necessary as we do not attempt to clean up the (invalid) block that we have already allocated. Signed-off-by: Dave Chinner <dchinner@redhat.com> Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com> Signed-off-by: Dave Chinner <david@fromorbit.com>
Diffstat (limited to 'fs/xfs/libxfs')
-rw-r--r--fs/xfs/libxfs/xfs_bmap.c24
1 files changed, 13 insertions, 11 deletions
diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
index 2760314fdf7f..a47670332326 100644
--- a/fs/xfs/libxfs/xfs_bmap.c
+++ b/fs/xfs/libxfs/xfs_bmap.c
@@ -673,7 +673,8 @@ xfs_bmap_extents_to_btree(
673 ASSERT(XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_EXTENTS); 673 ASSERT(XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_EXTENTS);
674 674
675 /* 675 /*
676 * Make space in the inode incore. 676 * Make space in the inode incore. This needs to be undone if we fail
677 * to expand the root.
677 */ 678 */
678 xfs_iroot_realloc(ip, 1, whichfork); 679 xfs_iroot_realloc(ip, 1, whichfork);
679 ifp->if_flags |= XFS_IFBROOT; 680 ifp->if_flags |= XFS_IFBROOT;
@@ -711,16 +712,15 @@ xfs_bmap_extents_to_btree(
711 args.minlen = args.maxlen = args.prod = 1; 712 args.minlen = args.maxlen = args.prod = 1;
712 args.wasdel = wasdel; 713 args.wasdel = wasdel;
713 *logflagsp = 0; 714 *logflagsp = 0;
714 if ((error = xfs_alloc_vextent(&args))) { 715 error = xfs_alloc_vextent(&args);
715 ASSERT(ifp->if_broot == NULL); 716 if (error)
716 goto err1; 717 goto out_root_realloc;
717 }
718 718
719 if (WARN_ON_ONCE(args.fsbno == NULLFSBLOCK)) { 719 if (WARN_ON_ONCE(args.fsbno == NULLFSBLOCK)) {
720 ASSERT(ifp->if_broot == NULL);
721 error = -ENOSPC; 720 error = -ENOSPC;
722 goto err1; 721 goto out_root_realloc;
723 } 722 }
723
724 /* 724 /*
725 * Allocation can't fail, the space was reserved. 725 * Allocation can't fail, the space was reserved.
726 */ 726 */
@@ -732,9 +732,10 @@ xfs_bmap_extents_to_btree(
732 xfs_trans_mod_dquot_byino(tp, ip, XFS_TRANS_DQ_BCOUNT, 1L); 732 xfs_trans_mod_dquot_byino(tp, ip, XFS_TRANS_DQ_BCOUNT, 1L);
733 abp = xfs_btree_get_bufl(mp, tp, args.fsbno, 0); 733 abp = xfs_btree_get_bufl(mp, tp, args.fsbno, 0);
734 if (!abp) { 734 if (!abp) {
735 error = -ENOSPC; 735 error = -EFSCORRUPTED;
736 goto err2; 736 goto out_unreserve_dquot;
737 } 737 }
738
738 /* 739 /*
739 * Fill in the child block. 740 * Fill in the child block.
740 */ 741 */
@@ -775,11 +776,12 @@ xfs_bmap_extents_to_btree(
775 *logflagsp = XFS_ILOG_CORE | xfs_ilog_fbroot(whichfork); 776 *logflagsp = XFS_ILOG_CORE | xfs_ilog_fbroot(whichfork);
776 return 0; 777 return 0;
777 778
778err2: 779out_unreserve_dquot:
779 xfs_trans_mod_dquot_byino(tp, ip, XFS_TRANS_DQ_BCOUNT, -1L); 780 xfs_trans_mod_dquot_byino(tp, ip, XFS_TRANS_DQ_BCOUNT, -1L);
780err1: 781out_root_realloc:
781 xfs_iroot_realloc(ip, -1, whichfork); 782 xfs_iroot_realloc(ip, -1, whichfork);
782 XFS_IFORK_FMT_SET(ip, whichfork, XFS_DINODE_FMT_EXTENTS); 783 XFS_IFORK_FMT_SET(ip, whichfork, XFS_DINODE_FMT_EXTENTS);
784 ASSERT(ifp->if_broot == NULL);
783 xfs_btree_del_cursor(cur, XFS_BTREE_ERROR); 785 xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
784 786
785 return error; 787 return error;