Linux CIFS filesystem development
 help / color / mirror / Atom feed
* [BUG] [~6.6 Kernel] Corruption when retrying encrypted sync writes
@ 2026-02-18 18:00 Bharath SM
  2026-02-19 11:05 ` David Howells
  2026-02-27 10:25 ` Shyam Prasad N
  0 siblings, 2 replies; 7+ messages in thread
From: Bharath SM @ 2026-02-18 18:00 UTC (permalink / raw)
  To: David Howells, Shyam Prasad N, Shyam Prasad, Steve French, CIFS,
	Paulo Alcantara, Enzo Matsumiya, Henrique Carvalho, Bharath S M

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

We are noticing a data corruption issue in kernels based on stable
6.6.y. Especially, when a synchronous writes retried after a
connection reset.

Based on investigation so far, it looks like we are having issue in
the following code path:
When SMB3 encryption is enabled, partial-page buffered writes hit the
synchronous write path in cifs_write_end() when the folio is not
uptodate (!folio_test_uptodate(folio)), it calls cifs_write() directly
with the kmap()'d page cache buffer, bypassing the async writeback
path.
cifs_write() calls SMB2_write(), which places the write payload in
rq_iov[1], pointing directly at the page cache buffer. When
smb3_init_transform_rq() builds the encryption request, it shares
rq_iov by pointer (new->rq_iov = old->rq_iov), and crypt_message()
encrypts in-place via aead_request_set_crypt(req, sg, sg, ...). This
destroys the original page cache data. If the write gets -EAGAIN after
encryption (e.g., connection reset), cifs_write() re-sends the
now-ciphertext buffer as if it were plaintext, resulting in
double-encrypted garbage on the server. The server accepts it and
returns success.
Please let me know if you have seen this issue in the past, your
comments on the analysis and probable fixes.

Repro steps: Attached repro.zip with repro scripts and instructions:
1) Mount with SMB3 encryption enabled
2) Perform buffered writes in a loop (e.g., echo "known_pattern" >> file)
3) Kill the TCP connection during writes (ss -K dport 445) to force
retryable errors
4) Read the file back and compare against expected content

Issue can occur when all below conditions met in buffered writes:
1) SMB2 encryption is active
2) Sync write path: Writes reached SMB2_write via cifs_write
3) Retryable network error for writes: When EAGAIN or ECONABORTED
returned from  cifs_send_recv().

Here is a the sequence of operations leading to issue:
write(2) syscall
 └─ cifs_write_end()                          [file.c]
     └─ cifs_write()                          [file.c]
         │  iov[1].iov_base = write_data      ← page cache pointer enters iov[1]
         │
         └─ server->ops->sync_write()         [file.c]
             └─ smb2_sync_write()             [smb2ops.c:]
                 └─ SMB2_write()              [smb2pdu.c:]
                     │  rqst.rq_iov = iov     ← rqst points to iov[]
(with page cache in [1])
                     │  rqst.rq_nvec = n_vec+1  ← BUG: payload in
rq_iov, not rq_iter
                     │
                     └─ cifs_send_recv()      [transport.c:1305]
                         └─ compound_send_recv()  [transport.c:1071]
                             │
                             └─ smb_send_rqst()   [transport.c:427]
                                 │  if (flags & CIFS_TRANSFORM_REQ)  ←
YES for SMB3 encryption
                                 │
                                 └─ server->ops->init_transform_rq()
[smb2ops.c:~4398]
                                 │   = smb3_init_transform_rq()
                                 │     new->rq_iov = old->rq_iov     ←
SHARES pointer (not copied!)
                                 │     size =
iov_iter_count(old->rq_iter) = 0  ← empty, no copy
                                 │
                                 └─ __smb_send_rqst()  [transport.c:272]
                                     │  → crypt_message()  [smb2ops.c:~4280]
                                     │     → smb2_get_aead_req()
[smb2ops.c:~4196]
                                     │        sg = scatterwalk from rq_iov[0..n]
                                     │
aead_request_set_crypt(req, sg, sg, ...)
                                     │
   ^^^  ^^^
                                     │
src=dst → IN-PLACE encrypt
                                     │
                                     │   iov[1] (= page cache) is now
AES ciphertext
                                     │
                                     └─ kernel_sendmsg() / sock_sendmsg()
                                         → sends encrypted data on wire

         ← rc = -EAGAIN (connection dropped)

         is_replayable_error(rc) == true or cifs_write while loop detects EAGAIN
         goto replay_again                    ← loops back with corrupted iov[1]
             └─ SMB2_write() re-sends...
                 └─ smb3_init_transform_rq()  ← encrypts ciphertext AGAIN
                     └─ crypt_message()       ← double-encrypted garbage
                         └─ server writes it to disk  ←  CORRUPTION



Modifying SMB2_write function by adding payload to rq_iter seems to
help here. Need to further test.
With below fix, when rq_iter size > 0 code in smb3_init_transform_rq
allocates fresh pages, copies the data via copy_page_from_iter(), and
encrypts the copy instead of the original.
Please let me know your comments.


 rqst.rq_iov = iov;
-rqst.rq_nvec = n_vec + 1;
+rqst.rq_nvec = 1;
+iov_iter_kvec(&rqst.rq_iter, ITER_SOURCE, &iov[1], n_vec,
+              io_parms->length);
+rqst.rq_iter_size = io_parms->length;

[-- Attachment #2: repro.zip --]
[-- Type: application/x-zip-compressed, Size: 2484 bytes --]

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

end of thread, other threads:[~2026-03-09 10:39 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-02-18 18:00 [BUG] [~6.6 Kernel] Corruption when retrying encrypted sync writes Bharath SM
2026-02-19 11:05 ` David Howells
2026-02-19 11:14   ` Bharath SM
2026-02-27 10:25 ` Shyam Prasad N
2026-03-03 18:03   ` Bharath SM
2026-03-04  8:09     ` Shyam Prasad N
2026-03-09 10:39       ` Bharath SM

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