From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=100.103.45.18 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1794061484; b=0P7dozZJsr2YVnF5TVKkwEYsTSBC71uZ4GiGUmye53MvVu0xCC0q3+joutZIgd7pQTQb yLoAbqdOOSHrrkTsvBEF2nExIHwCL/T/8yT28PONv7Uxo1/ftIf3yWKIufUdmJHEI/g9V KTY3GM1xuCOUcAQs8AoOuJJFzLuWxwy3qJXqZcv/oQJEZJmAW9KNe5g3fjPLjvJae5OxI LiIhwH/x3nIbQIFpd6oTwmkFlPHE+7DVHdD9xtZ6wKctT0TlXEFeNP9Dli1Mok9GijGiM J1Fw8AmESfa65TsWj3d/VEUmn7KOjuWbmr9GqYydNUdt+PpIX3G6MLBr2fWcAnP6LVA== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1794061484; h=Received:DKIM-Signature:From:To:Cc:Subject:Date:Message-ID:X-Mailer: In-Reply-To:References:User-Agent:X-stable:X-Patchwork-Hint: MIME-Version:Content-Transfer-Encoding; bh=DR2OchJSccSP0sW4sDwpOyB6hgjkGgdlMLYxLJby3gM=; b=lHlRLbQSLlUcWacVbsBIu9Tm1McrPgtTkVNbOFnu6ABOx8DR67SbiZctkqi1DP+Lq6Go 3Y7p1C8zI0LNpiVIyNfk88e7YevbIl1A6OXmAd8Rm3tAWxEXB4qE+ydHVPoARvFr7lwXZ ctlqKGXb49E1G3sdHXi2fqXdAsj06P0tzl08fuXy0Hi5wZEQWTXrKKtkXqtb2mTXYAdB5 gzAffr6RF2N7nfcDY3YmhEIN8BNXJ9ZCDA1+8YagJa872WZ7UfUfwRDwtI1KHwqnG0587 os8yGv5Rs+tigi07Dj/s4zdQfXqe3cC9Ojiv3yMFo6+QnbAdJWhmvOlIv9Kbs1II9oQ== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass header.d=linuxfoundation.org header.i=@linuxfoundation.org header.a=rsa-sha256 header.s=korg header.b=Ebs8QarI; arc=none smtp.remote-ip=100.103.45.18 Received: from smtp.kernel.org (quasi.space.kernel.org [100.103.45.18]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (prime256v1)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6D7BF606A326; Sat, 7 Nov 2026 14:24:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linuxfoundation.org header.i=@linuxfoundation.org header.a=rsa-sha256 header.s=korg header.b=Ebs8QarI Received: by smtp.kernel.org (Postfix) with ESMTPSA id 2055F1F0089F; Sat, 7 Nov 2026 14:24:44 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linuxfoundation.org; s=korg; t=1794061484; bh=DR2OchJSccSP0sW4sDwpOyB6hgjkGgdlMLYxLJby3gM=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=Ebs8QarIfrw6gISSM+7FeDh0XB9opFHfh1nMQuDZxwX9QYjOTXupvtHLOB/OE29iM oQjUqgzMsIeitcsW4fsf2qTVCvQiU2TpcYniklHiZg8bURhmMT9IEMb/KxyZpPqlRH DfcXIdyFzhBHevun7Az46DJp2ETCrzJTtKlzpCcA= From: Greg Kroah-Hartman To: stable@vger.kernel.org Cc: Greg Kroah-Hartman , patches@lists.linux.dev, David Howells , Namjae Jeon , Paulo Alcantara , Christian Brauner , Matthew Wilcox , Ronnie Sahlberg , Shyam Prasad N , Tom Talpey , Bharath SM , Sasha Levin Subject: [PATCH 6.12 0543/1536] smb: client: drain and invalidate before server-side copy/clone Date: Sat, 7 Nov 2026 08:42:39 -0500 Message-ID: <20261107135012.874427098@linuxfoundation.org> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261107134957.653319350@linuxfoundation.org> References: <20261107134957.653319350@linuxfoundation.org> User-Agent: quilt/0.69 X-stable: review X-Patchwork-Hint: ignore X-Mailing-List: stable@vger.kernel.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Transfer-Encoding: 8bit 6.12-stable review patch. If anyone has any objections, please let me know. ------------------ From: Paulo Alcantara [ Upstream commit c9b1194e8ec99d95db25db45edd2fbf690f043ab ] cifs_file_copychunk_range() and the clone (FICLONE) path of cifs_remap_file_range() do not serialise the destination page cache against the server-side copy the way the other server-side range operations (smb3_zero_range(), smb3_punch_hole(), smb3_collapse_range() and smb3_insert_range()) do. cifs_file_copychunk_range() invalidates the destination range with filemap_invalidate_inode(), which takes and drops the mapping's invalidate_lock internally, so the lock is no longer held when the copychunk ioctl is issued. It also never drains in-flight netfs I/O on the target. The clone path never takes the invalidate_lock at all (only i_rwsem), uses a bare truncate_inode_pages_range() and likewise does not drain outstanding I/O. As a result an asynchronous destination writeback can complete after the server-side copy/clone has run and reinstate stale data over the region just written by the server, corrupting the file. This is the same class of corruption as commit d7d2adcd022b ("smb/client: flush dirty data before punching a hole") and has been seen randomly in generic/363 against Windows Server. Fix both paths to follow the established ordering: hold the target mapping's invalidate_lock across the flush and invalidation of the destination and the server ioctl, and call netfs_wait_for_outstanding_io() on the target to drain in-flight writes before the ioctl is issued. Only the target inode's invalidate_lock is required, as the source is merely flushed and not invalidated; i_rwsem (already held via lock_two_nondirectories()) is acquired before the invalidate_lock, matching the VFS lock ordering. Since filemap_invalidate_inode() takes that same lock internally, replace it with its own unmap/flush/invalidate steps instead of calling it, and return early on a zero-length copy to avoid a range underflow. Fixes: 8101d6e112e2 ("cifs: Fix copy offload to flush destination region") Fixes: c54fc3a4f375 ("cifs: Fix flushing, invalidation and file size with FICLONE") Reviewed-by: David Howells Reviewed-by: Namjae Jeon Signed-off-by: Paulo Alcantara Cc: Christian Brauner Cc: Matthew Wilcox Cc: Ronnie Sahlberg Cc: Shyam Prasad N Cc: Tom Talpey Cc: Bharath SM Cc: stable@vger.kernel.org [ sashal: Kept the tree's existing min_t(loff_t, ..., i_size_read()) truncate lines in cifs_remap_file_range() and added only the netfs_wait_for_outstanding_io() call after them. ] Signed-off-by: Sasha Levin --- fs/smb/client/cifsfs.c | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c index b845010829c5b..65bf11f606ed4 100644 --- a/fs/smb/client/cifsfs.c +++ b/fs/smb/client/cifsfs.c @@ -1326,6 +1326,7 @@ static loff_t cifs_remap_file_range(struct file *src_file, loff_t off, * server could even support copy of range where source = target */ lock_two_nondirectories(target_inode, src_inode); + filemap_invalidate_lock(target_inode->i_mapping); if (len == 0) { loff_t src_size = i_size_read(src_inode); @@ -1387,6 +1388,7 @@ static loff_t cifs_remap_file_range(struct file *src_file, loff_t off, truncate_inode_pages_range(&target_inode->i_data, min_t(loff_t, fstart, i_size_read(target_inode)), fend); + netfs_wait_for_outstanding_io(target_inode); fscache_invalidate(cifs_inode_cookie(target_inode), NULL, i_size_read(target_inode), 0); @@ -1422,6 +1424,7 @@ static loff_t cifs_remap_file_range(struct file *src_file, loff_t off, that target is updated on the server */ CIFS_I(target_inode)->time = 0; unlock: + filemap_invalidate_unlock(target_inode->i_mapping); /* although unlocking in the reverse order from locking is not strictly necessary here it is a little cleaner to be consistent */ unlock_two_nondirectories(src_inode, target_inode); @@ -1445,6 +1448,9 @@ ssize_t cifs_file_copychunk_range(unsigned int xid, struct cifs_tcon *target_tcon; ssize_t rc; + if (len == 0) + return 0; + cifs_dbg(FYI, "copychunk range\n"); if (!src_file->private_data || !dst_file->private_data) { @@ -1474,6 +1480,7 @@ ssize_t cifs_file_copychunk_range(unsigned int xid, * server could even support copy of range where source = target */ lock_two_nondirectories(target_inode, src_inode); + filemap_invalidate_lock(target_inode->i_mapping); cifs_dbg(FYI, "about to flush pages\n"); @@ -1499,11 +1506,24 @@ ssize_t cifs_file_copychunk_range(unsigned int xid, * Start at the old EOF when extending so the folio straddling it, which * may hold data written past EOF through an mmap, is dropped too. */ - rc = filemap_invalidate_inode(target_inode, true, - min(destoff, i_size_read(target_inode)), - destoff + len - 1); - if (rc) - goto unlock; + if (target_inode->i_mapping->nrpages) { + loff_t fstart = min(destoff, i_size_read(target_inode)); + loff_t fend = destoff + len - 1; + + unmap_mapping_pages(target_inode->i_mapping, + fstart >> PAGE_SHIFT, + (fend >> PAGE_SHIFT) - + (fstart >> PAGE_SHIFT) + 1, + false); + rc = filemap_write_and_wait_range(target_inode->i_mapping, + fstart, fend); + if (rc) + goto unlock; + invalidate_inode_pages2_range(target_inode->i_mapping, + fstart >> PAGE_SHIFT, + fend >> PAGE_SHIFT); + } + netfs_wait_for_outstanding_io(target_inode); fscache_invalidate(cifs_inode_cookie(target_inode), NULL, i_size_read(target_inode), 0); @@ -1531,6 +1551,7 @@ ssize_t cifs_file_copychunk_range(unsigned int xid, CIFS_I(target_inode)->time = 0; unlock: + filemap_invalidate_unlock(target_inode->i_mapping); /* although unlocking in the reverse order from locking is not * strictly necessary here it is a little cleaner to be consistent */ -- 2.53.0