Skip to content

Commit 9af8669

Browse files
bsberndkdave
authored andcommitted
btrfs: file_remove_privs needs an exclusive lock in direct io write
This was noticed by Miklos that file_remove_privs might call into notify_change(), which requires to hold an exclusive lock. The problem exists in FUSE and btrfs. We can fix it without any additional helpers from VFS, in case the privileges would need to be dropped, change the lock type to be exclusive and redo the loop. Fixes: e9adabb ("btrfs: use shared lock for direct writes within EOF") CC: Miklos Szeredi <miklos@szeredi.hu> CC: stable@vger.kernel.org # 5.15+ Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Bernd Schubert <bschubert@ddn.com> Reviewed-by: David Sterba <dsterba@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
1 parent 06ed093 commit 9af8669

1 file changed

Lines changed: 14 additions & 2 deletions

File tree

fs/btrfs/file.c

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1466,15 +1466,27 @@ static ssize_t btrfs_direct_write(struct kiocb *iocb, struct iov_iter *from)
14661466
if (iocb->ki_flags & IOCB_NOWAIT)
14671467
ilock_flags |= BTRFS_ILOCK_TRY;
14681468

1469-
/* If the write DIO is within EOF, use a shared lock */
1470-
if (iocb->ki_pos + iov_iter_count(from) <= i_size_read(inode))
1469+
/*
1470+
* If the write DIO is within EOF, use a shared lock and also only if
1471+
* security bits will likely not be dropped by file_remove_privs() called
1472+
* from btrfs_write_check(). Either will need to be rechecked after the
1473+
* lock was acquired.
1474+
*/
1475+
if (iocb->ki_pos + iov_iter_count(from) <= i_size_read(inode) && IS_NOSEC(inode))
14711476
ilock_flags |= BTRFS_ILOCK_SHARED;
14721477

14731478
relock:
14741479
err = btrfs_inode_lock(BTRFS_I(inode), ilock_flags);
14751480
if (err < 0)
14761481
return err;
14771482

1483+
/* Shared lock cannot be used with security bits set. */
1484+
if ((ilock_flags & BTRFS_ILOCK_SHARED) && !IS_NOSEC(inode)) {
1485+
btrfs_inode_unlock(BTRFS_I(inode), ilock_flags);
1486+
ilock_flags &= ~BTRFS_ILOCK_SHARED;
1487+
goto relock;
1488+
}
1489+
14781490
err = generic_write_checks(iocb, from);
14791491
if (err <= 0) {
14801492
btrfs_inode_unlock(BTRFS_I(inode), ilock_flags);

0 commit comments

Comments
 (0)