Linux CIFS filesystem development
 help / color / mirror / Atom feed
* Request to backport data corruption fix to stable
@ 2025-11-14 11:12 Shyam Prasad N
  2025-11-14 15:00 ` Sasha Levin
  0 siblings, 1 reply; 4+ messages in thread
From: Shyam Prasad N @ 2025-11-14 11:12 UTC (permalink / raw)
  To: Greg KH, sashal
  Cc: David Howells, Bharath SM, Steve French, CIFS, Enzo Matsumiya

[-- Attachment #1: Type: text/plain, Size: 825 bytes --]

Hi Greg/Sasha,

Over the last few months, a few users have reported a data corruption
with Linux SMB kernel client filesystem. This is one such report:
https://lore.kernel.org/linux-cifs/36fb31bf2c854cdc930a3415f5551dcd@izw-berlin.de/

The issue is now well understood. Attached is a fix for this issue.
I've made sure that the fix is stopping the data corruption and also
not regressing other write patterns.

The regression seems to have been introduced during a refactoring of
this code path during the v6.3 and continued to exist till v6.9,
before the code was refactored again with netfs helper library
integration in v6.10.

I request you to include this change in all stable trees for
v6.3..v6.9. I've done my testing on stable-6.6. Please let me know if
you want this tested on any other kernels.

-- 
Regards,
Shyam

[-- Attachment #2: 0001-cifs-stop-writeback-extension-when-change-of-size-is.patch --]
[-- Type: application/octet-stream, Size: 3440 bytes --]

From c4494f7ad7c0dd46b67dc67058507278e56c9311 Mon Sep 17 00:00:00 2001
From: Shyam Prasad N <sprasad@microsoft.com>
Date: Fri, 14 Nov 2025 14:30:55 +0530
Subject: [PATCH] cifs: stop writeback extension when change of size is
 detected

cifs_extend_writeback can pick up a folio on an extending write which
has been dirtied, but we have aclamp on the writeback to an i_size
local variable, which can cause short writes, yet mark the page as clean.
This can cause a data corruption.

As an example, consider this scenario:
1. First write to the file happens offset 0 len 5k.
2. Writeback starts for the range (0-5k).
3. Writeback locks page 1 in cifs_writepages_begin. But does not lock
page 2 yet.
4. Page 2 is now written to by the next write, which extends the file
by another 5k. Page 2 and 3 are now marked dirty.
5. Now we reach cifs_extend_writeback, where we extend to include the
next folio (even if it should be partially written). We will mark page
2 for writeback.
6. But after exiting cifs_extend_writeback, we will clamp the
writeback to i_size, which was 5k when it started. So we write only 1k
bytes in page 2.
7. We still will now mark page 2 as flushed and mark it clean. So
remaining contents of page 2 will not be written to the server (hence
the hole in that gap, unless that range gets overwritten).

With this patch, we will make sure not extend the writeback anymore
when a change in the file size is detected.

This fix also changes the error handling of cifs_extend_writeback when
a folio get fails. We will now stop the extension when a folio get fails.

Cc: stable@kernel.org # v6.3~v6.9
Signed-off-by: Shyam Prasad N <sprasad@microsoft.com>
Acked-by: David Howells <dhowells@redhat.com>
Reported-by: Mark A Whiting <whitingm@opentext.com>
Signed-off-by: Shyam Prasad N <sprasad@microsoft.com>
---
 fs/smb/client/file.c | 18 +++++++++++++++---
 1 file changed, 15 insertions(+), 3 deletions(-)

diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c
index 7a2b81fbd9cfd..cc2ba2b18c8d4 100644
--- a/fs/smb/client/file.c
+++ b/fs/smb/client/file.c
@@ -2747,8 +2747,10 @@ static void cifs_extend_writeback(struct address_space *mapping,
 				  loff_t start,
 				  int max_pages,
 				  loff_t max_len,
-				  size_t *_len)
+				  size_t *_len,
+				  unsigned long long i_size)
 {
+	struct inode *inode = mapping->host;
 	struct folio_batch batch;
 	struct folio *folio;
 	unsigned int nr_pages;
@@ -2779,7 +2781,7 @@ static void cifs_extend_writeback(struct address_space *mapping,
 
 			if (!folio_try_get(folio)) {
 				xas_reset(xas);
-				continue;
+				break;
 			}
 			nr_pages = folio_nr_pages(folio);
 			if (nr_pages > max_pages) {
@@ -2799,6 +2801,15 @@ static void cifs_extend_writeback(struct address_space *mapping,
 				xas_reset(xas);
 				break;
 			}
+
+			/* if file size is changing, stop extending */
+			if (i_size_read(inode) != i_size) {
+				folio_unlock(folio);
+				folio_put(folio);
+				xas_reset(xas);
+				break;
+			}
+
 			if (!folio_test_dirty(folio) ||
 			    folio_test_writeback(folio)) {
 				folio_unlock(folio);
@@ -2930,7 +2941,8 @@ static ssize_t cifs_write_back_from_locked_folio(struct address_space *mapping,
 
 			if (max_pages > 0)
 				cifs_extend_writeback(mapping, xas, &count, start,
-						      max_pages, max_len, &len);
+						      max_pages, max_len, &len,
+						      i_size);
 		}
 	}
 	len = min_t(unsigned long long, len, i_size - start);
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2025-11-15 13:34 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-11-14 11:12 Request to backport data corruption fix to stable Shyam Prasad N
2025-11-14 15:00 ` Sasha Levin
2025-11-14 18:35   ` Steve French
2025-11-15 13:34     ` Sasha Levin

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox