diff options
author | Darrick J. Wong <darrick.wong@oracle.com> | 2017-02-02 02:56:11 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2017-02-04 03:47:13 -0500 |
commit | b5b4d4a9141e15ea8d887d88d9763cf190955907 (patch) | |
tree | 969e195e3ae8268d6e177a10c7a70c2aad0bb0b4 /fs | |
parent | 5d44dd54bd57c6275d82d8912730c794fc8ec8ab (diff) |
xfs: fix bmv_count confusion w/ shared extents
commit c364b6d0b6cda1cd5d9ab689489adda3e82529aa upstream.
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 f86f403794b ("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>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/xfs/xfs_bmap_util.c | 28 |
1 files changed, 18 insertions, 10 deletions
diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c index 0670a8bd5818..efb8ccd6bbf2 100644 --- a/fs/xfs/xfs_bmap_util.c +++ b/fs/xfs/xfs_bmap_util.c | |||
@@ -528,7 +528,6 @@ xfs_getbmap( | |||
528 | xfs_bmbt_irec_t *map; /* buffer for user's data */ | 528 | xfs_bmbt_irec_t *map; /* buffer for user's data */ |
529 | xfs_mount_t *mp; /* file system mount point */ | 529 | xfs_mount_t *mp; /* file system mount point */ |
530 | int nex; /* # of user extents can do */ | 530 | int nex; /* # of user extents can do */ |
531 | int nexleft; /* # of user extents left */ | ||
532 | int subnex; /* # of bmapi's can do */ | 531 | int subnex; /* # of bmapi's can do */ |
533 | int nmap; /* number of map entries */ | 532 | int nmap; /* number of map entries */ |
534 | struct getbmapx *out; /* output structure */ | 533 | struct getbmapx *out; /* output structure */ |
@@ -686,10 +685,8 @@ xfs_getbmap( | |||
686 | goto out_free_map; | 685 | goto out_free_map; |
687 | } | 686 | } |
688 | 687 | ||
689 | nexleft = nex; | ||
690 | |||
691 | do { | 688 | do { |
692 | nmap = (nexleft > subnex) ? subnex : nexleft; | 689 | nmap = (nex> subnex) ? subnex : nex; |
693 | error = xfs_bmapi_read(ip, XFS_BB_TO_FSBT(mp, bmv->bmv_offset), | 690 | error = xfs_bmapi_read(ip, XFS_BB_TO_FSBT(mp, bmv->bmv_offset), |
694 | XFS_BB_TO_FSB(mp, bmv->bmv_length), | 691 | XFS_BB_TO_FSB(mp, bmv->bmv_length), |
695 | map, &nmap, bmapi_flags); | 692 | map, &nmap, bmapi_flags); |
@@ -697,8 +694,8 @@ xfs_getbmap( | |||
697 | goto out_free_map; | 694 | goto out_free_map; |
698 | ASSERT(nmap <= subnex); | 695 | ASSERT(nmap <= subnex); |
699 | 696 | ||
700 | for (i = 0; i < nmap && nexleft && bmv->bmv_length && | 697 | for (i = 0; i < nmap && bmv->bmv_length && |
701 | cur_ext < bmv->bmv_count; i++) { | 698 | cur_ext < bmv->bmv_count - 1; i++) { |
702 | out[cur_ext].bmv_oflags = 0; | 699 | out[cur_ext].bmv_oflags = 0; |
703 | if (map[i].br_state == XFS_EXT_UNWRITTEN) | 700 | if (map[i].br_state == XFS_EXT_UNWRITTEN) |
704 | out[cur_ext].bmv_oflags |= BMV_OF_PREALLOC; | 701 | out[cur_ext].bmv_oflags |= BMV_OF_PREALLOC; |
@@ -760,16 +757,27 @@ xfs_getbmap( | |||
760 | continue; | 757 | continue; |
761 | } | 758 | } |
762 | 759 | ||
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 | */ | ||
763 | if (inject_map.br_startblock != NULLFSBLOCK) { | 773 | if (inject_map.br_startblock != NULLFSBLOCK) { |
764 | map[i] = inject_map; | 774 | map[i] = inject_map; |
765 | i--; | 775 | i--; |
766 | } else | 776 | } |
767 | nexleft--; | ||
768 | bmv->bmv_entries++; | 777 | bmv->bmv_entries++; |
769 | cur_ext++; | 778 | cur_ext++; |
770 | } | 779 | } |
771 | } while (nmap && nexleft && bmv->bmv_length && | 780 | } while (nmap && bmv->bmv_length && cur_ext < bmv->bmv_count - 1); |
772 | cur_ext < bmv->bmv_count); | ||
773 | 781 | ||
774 | out_free_map: | 782 | out_free_map: |
775 | kmem_free(map); | 783 | kmem_free(map); |