Skip to content

Commit f5f0ed8

Browse files
Christoph Hellwigcmaiolino
authored andcommitted
xfs: don't call remap_verify_area with sb write protection held
The XFS_IOC_EXCHANGE_RANGE ioctl with the XFS_EXCHANGE_RANGE_TO_EOF flag operates on a range bounded by the end of the file. This means the actual amount of blocks exchanged is derived from the inode size, which is only stable with the IOLOCK (i_rwsem) held. Do that, it currently calls remap_verify_area from inside the sb write protection which nests outside the IOLOCK. But this makes fsnotify_file_area_perm which is called from remap_verify_area unhappy when the kernel is built with lockdep and the recently added CONFIG_FANOTIFY_ACCESS_PERMISSIONS option. Fix this by always calling remap_verify_area before taking the write protection, and passing a 0 size to remap_verify_area similar to the FICLONE/FICLONERANGE ioctls when they are asked to clone until the file end. (Note: the size argument gets passed to fsnotify_file_area_perm, but then isn't actually used there). Fixes: 9a64d9b ("xfs: introduce new file range exchange ioctl") Cc: <[email protected]> # v6.10 Signed-off-by: Christoph Hellwig <[email protected]> Reviewed-by: Darrick J. Wong <[email protected]> Signed-off-by: Carlos Maiolino <[email protected]>
1 parent 89841b2 commit f5f0ed8

File tree

1 file changed

+27
-44
lines changed

1 file changed

+27
-44
lines changed

fs/xfs/xfs_exchrange.c

Lines changed: 27 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -329,22 +329,6 @@ xfs_exchrange_mappings(
329329
* successfully but before locks are dropped.
330330
*/
331331

332-
/* Verify that we have security clearance to perform this operation. */
333-
static int
334-
xfs_exchange_range_verify_area(
335-
struct xfs_exchrange *fxr)
336-
{
337-
int ret;
338-
339-
ret = remap_verify_area(fxr->file1, fxr->file1_offset, fxr->length,
340-
true);
341-
if (ret)
342-
return ret;
343-
344-
return remap_verify_area(fxr->file2, fxr->file2_offset, fxr->length,
345-
true);
346-
}
347-
348332
/*
349333
* Performs necessary checks before doing a range exchange, having stabilized
350334
* mutable inode attributes via i_rwsem.
@@ -355,11 +339,13 @@ xfs_exchange_range_checks(
355339
unsigned int alloc_unit)
356340
{
357341
struct inode *inode1 = file_inode(fxr->file1);
342+
loff_t size1 = i_size_read(inode1);
358343
struct inode *inode2 = file_inode(fxr->file2);
344+
loff_t size2 = i_size_read(inode2);
359345
uint64_t allocmask = alloc_unit - 1;
360346
int64_t test_len;
361347
uint64_t blen;
362-
loff_t size1, size2, tmp;
348+
loff_t tmp;
363349
int error;
364350

365351
/* Don't touch certain kinds of inodes */
@@ -368,24 +354,25 @@ xfs_exchange_range_checks(
368354
if (IS_SWAPFILE(inode1) || IS_SWAPFILE(inode2))
369355
return -ETXTBSY;
370356

371-
size1 = i_size_read(inode1);
372-
size2 = i_size_read(inode2);
373-
374357
/* Ranges cannot start after EOF. */
375358
if (fxr->file1_offset > size1 || fxr->file2_offset > size2)
376359
return -EINVAL;
377360

378-
/*
379-
* If the caller said to exchange to EOF, we set the length of the
380-
* request large enough to cover everything to the end of both files.
381-
*/
382361
if (fxr->flags & XFS_EXCHANGE_RANGE_TO_EOF) {
362+
/*
363+
* If the caller said to exchange to EOF, we set the length of
364+
* the request large enough to cover everything to the end of
365+
* both files.
366+
*/
383367
fxr->length = max_t(int64_t, size1 - fxr->file1_offset,
384368
size2 - fxr->file2_offset);
385-
386-
error = xfs_exchange_range_verify_area(fxr);
387-
if (error)
388-
return error;
369+
} else {
370+
/*
371+
* Otherwise we require both ranges to end within EOF.
372+
*/
373+
if (fxr->file1_offset + fxr->length > size1 ||
374+
fxr->file2_offset + fxr->length > size2)
375+
return -EINVAL;
389376
}
390377

391378
/*
@@ -401,15 +388,6 @@ xfs_exchange_range_checks(
401388
check_add_overflow(fxr->file2_offset, fxr->length, &tmp))
402389
return -EINVAL;
403390

404-
/*
405-
* We require both ranges to end within EOF, unless we're exchanging
406-
* to EOF.
407-
*/
408-
if (!(fxr->flags & XFS_EXCHANGE_RANGE_TO_EOF) &&
409-
(fxr->file1_offset + fxr->length > size1 ||
410-
fxr->file2_offset + fxr->length > size2))
411-
return -EINVAL;
412-
413391
/*
414392
* Make sure we don't hit any file size limits. If we hit any size
415393
* limits such that test_length was adjusted, we abort the whole
@@ -747,6 +725,7 @@ xfs_exchange_range(
747725
{
748726
struct inode *inode1 = file_inode(fxr->file1);
749727
struct inode *inode2 = file_inode(fxr->file2);
728+
loff_t check_len = fxr->length;
750729
int ret;
751730

752731
BUILD_BUG_ON(XFS_EXCHANGE_RANGE_ALL_FLAGS &
@@ -779,14 +758,18 @@ xfs_exchange_range(
779758
return -EBADF;
780759

781760
/*
782-
* If we're not exchanging to EOF, we can check the areas before
783-
* stabilizing both files' i_size.
761+
* If we're exchanging to EOF we can't calculate the length until taking
762+
* the iolock. Pass a 0 length to remap_verify_area similar to the
763+
* FICLONE and FICLONERANGE ioctls that support cloning to EOF as well.
784764
*/
785-
if (!(fxr->flags & XFS_EXCHANGE_RANGE_TO_EOF)) {
786-
ret = xfs_exchange_range_verify_area(fxr);
787-
if (ret)
788-
return ret;
789-
}
765+
if (fxr->flags & XFS_EXCHANGE_RANGE_TO_EOF)
766+
check_len = 0;
767+
ret = remap_verify_area(fxr->file1, fxr->file1_offset, check_len, true);
768+
if (ret)
769+
return ret;
770+
ret = remap_verify_area(fxr->file2, fxr->file2_offset, check_len, true);
771+
if (ret)
772+
return ret;
790773

791774
/* Update cmtime if the fd/inode don't forbid it. */
792775
if (!(fxr->file1->f_mode & FMODE_NOCMTIME) && !IS_NOCMTIME(inode1))

0 commit comments

Comments
 (0)