Skip to content

Commit c364b6d

Browse files
committed
xfs: fix bmv_count confusion w/ shared extents
In a bmapx call, bmv_count is the total size of the array, including the zeroth element that userspace uses to supply the search key. The output array starts at offset 1 so that we can set up the user for the next invocation. Since we now can split an extent into multiple bmap records due to shared/unshared status, we have to be careful that we don't overflow the output array. In the original patch f86f403 ("xfs: teach get_bmapx about shared extents and the CoW fork") I used cur_ext (the output index) to check for overflows, albeit with an off-by-one error. Since nexleft no longer describes the number of unfilled slots in the output, we can rip all that out and use cur_ext for the overflow check directly. Failure to do this causes heap corruption in bmapx callers such as xfs_io and xfs_scrub. xfs/328 can reproduce this problem. Reviewed-by: Eric Sandeen <sandeen@redhat.com> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
1 parent 2aa6ba7 commit c364b6d

File tree

1 file changed

+18
-10
lines changed

1 file changed

+18
-10
lines changed

fs/xfs/xfs_bmap_util.c

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -528,7 +528,6 @@ xfs_getbmap(
528528
xfs_bmbt_irec_t *map; /* buffer for user's data */
529529
xfs_mount_t *mp; /* file system mount point */
530530
int nex; /* # of user extents can do */
531-
int nexleft; /* # of user extents left */
532531
int subnex; /* # of bmapi's can do */
533532
int nmap; /* number of map entries */
534533
struct getbmapx *out; /* output structure */
@@ -686,19 +685,17 @@ xfs_getbmap(
686685
goto out_free_map;
687686
}
688687

689-
nexleft = nex;
690-
691688
do {
692-
nmap = (nexleft > subnex) ? subnex : nexleft;
689+
nmap = (nex> subnex) ? subnex : nex;
693690
error = xfs_bmapi_read(ip, XFS_BB_TO_FSBT(mp, bmv->bmv_offset),
694691
XFS_BB_TO_FSB(mp, bmv->bmv_length),
695692
map, &nmap, bmapi_flags);
696693
if (error)
697694
goto out_free_map;
698695
ASSERT(nmap <= subnex);
699696

700-
for (i = 0; i < nmap && nexleft && bmv->bmv_length &&
701-
cur_ext < bmv->bmv_count; i++) {
697+
for (i = 0; i < nmap && bmv->bmv_length &&
698+
cur_ext < bmv->bmv_count - 1; i++) {
702699
out[cur_ext].bmv_oflags = 0;
703700
if (map[i].br_state == XFS_EXT_UNWRITTEN)
704701
out[cur_ext].bmv_oflags |= BMV_OF_PREALLOC;
@@ -760,16 +757,27 @@ xfs_getbmap(
760757
continue;
761758
}
762759

760+
/*
761+
* In order to report shared extents accurately,
762+
* we report each distinct shared/unshared part
763+
* of a single bmbt record using multiple bmap
764+
* extents. To make that happen, we iterate the
765+
* same map array item multiple times, each
766+
* time trimming out the subextent that we just
767+
* reported.
768+
*
769+
* Because of this, we must check the out array
770+
* index (cur_ext) directly against bmv_count-1
771+
* to avoid overflows.
772+
*/
763773
if (inject_map.br_startblock != NULLFSBLOCK) {
764774
map[i] = inject_map;
765775
i--;
766-
} else
767-
nexleft--;
776+
}
768777
bmv->bmv_entries++;
769778
cur_ext++;
770779
}
771-
} while (nmap && nexleft && bmv->bmv_length &&
772-
cur_ext < bmv->bmv_count);
780+
} while (nmap && bmv->bmv_length && cur_ext < bmv->bmv_count - 1);
773781

774782
out_free_map:
775783
kmem_free(map);

0 commit comments

Comments
 (0)