Linux CIFS filesystem development
 help / color / mirror / Atom feed
From: Frank Sorenson <sorenson@redhat.com>
To: linux-cifs@vger.kernel.org, pc@manguebit.org, stfrench@microsoft.com
Subject: [PATCH v2 0/2] cifs: fix attribute cache corruption from concurrent directory operations
Date: Mon, 20 Jul 2026 11:35:39 -0500	[thread overview]
Message-ID: <20260720163541.1428872-1-sorenson@redhat.com> (raw)

This series fixes two distinct but related race conditions in the
cifs/smb3 client where directory operations (lease breaks and readdir)
can corrupt the attribute cache of recently modified files, causing
subsequent stat() calls to return incorrect file sizes or fail with EIO.

Both issues stem from the fundamental problem that directory-level
metadata -- whether from a lease break notification or a readdir
enumeration -- is not strictly synchronized with the authoritative state
of open or recently closed files.

Both bugs require as few as 2 concurrent threads performing directory
and file operations simultaneously.  They only reproduce against Windows
Server (where directory lease breaks occur and directory enumeration
metadata lags behind file state); they do not reproduce against Samba.
The workaround for both is to mount with actimeo=0, which forces every
stat() to query the server directly rather than trusting the cache.


Patch 1: cifs: serialize readdir with directory cache invalidation from lease breaks
-------------------------------------------------------------------------------------
When a directory lease break occurs while readdir is actively traversing
the directory cache, the lease break handler calls cifs_revalidate_mapping()
-> cifs_zap_mapping() while holding CIFS_INO_LOCK, racing with
cifs_readdir() which traverses the same cache without that lock.  The
resulting corruption causes subsequent stat() calls to return wrong file
sizes or EIO errors.

Fix: acquire CIFS_INO_LOCK at the start of cifs_readdir() so that lease
break cache invalidation and readdir traversal are mutually exclusive.


Patch 2: cifs: prevent readdir from changing file size due to stale directory metadata
---------------------------------------------------------------------------------------
After writing to a file and closing it, concurrent readdir() can fetch
stale directory metadata from the server (EndOfFile=0 for a recently
written file) and overwrite the correct cached i_size.  The race window
is between cifsFileInfo_put() removing the handle from openFileList
(after which is_inode_writable() returns false) and stat() being called.
The existing is_size_safe_to_change() check only blocks this when an
active RW lease was held -- not after the last writable handle is closed.

Fix: track the time of the last writable close or truncate in a new
cifsInodeInfo->time_last_write field.  If readdir attempts to change
i_size within acregmax jiffies of that timestamp, the update is
suppressed.  When the suppressed size differs from the cached value,
cifs_i->time is set to zero, forcing the next stat() to issue a fresh
QUERY_INFO RPC.  QUERY_INFO returns the authoritative size from the
server's open-file table rather than stale directory enumeration metadata,
which is the same path taken by actimeo=0.


Testing
-------
Both bugs reproduce against Windows Server 2022 with SMB 3.1.1 and
at least 2 concurrent threads.  A reproducer program exercising
concurrent rename+readdir (bug 1) and write+close+stat with concurrent
readdir (bug 2) was run for 400000 iterations with both patches applied
without hitting either bug.

A reproducer is available at https://github.com/fsorenson/cifs_cache_race_repro/


v2: fix malformed patch 2


Frank Sorenson (2):
  cifs: serialize readdir with directory cache invalidation from lease breaks
  cifs: prevent readdir from changing file size due to stale directory metadata

 fs/smb/client/cifsfs.c   |  1 +
 fs/smb/client/cifsglob.h |  1 +
 fs/smb/client/cifsproto.h |  1 +
 fs/smb/client/file.c     | 24 +++++++++++++++++++++---
 fs/smb/client/inode.c    |  6 +++++-
 fs/smb/client/readdir.c  | 12 ++++++++++++
 6 files changed, 45 insertions(+), 4 deletions(-)

--
2.55.0


             reply	other threads:[~2026-07-20 16:35 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-20 16:35 Frank Sorenson [this message]
2026-07-20 16:35 ` [PATCH v2 1/2] cifs: serialize readdir with directory cache invalidation from lease breaks Frank Sorenson
2026-07-20 16:35 ` [PATCH v2 2/2] cifs: prevent readdir from changing file size due to stale directory metadata Frank Sorenson
2026-07-20 17:19 ` [PATCH v2 0/2] cifs: fix attribute cache corruption from concurrent directory operations Frank Sorenson

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260720163541.1428872-1-sorenson@redhat.com \
    --to=sorenson@redhat.com \
    --cc=linux-cifs@vger.kernel.org \
    --cc=pc@manguebit.org \
    --cc=stfrench@microsoft.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox