From: Frank Sorenson <sorenson@redhat.com>
To: linux-cifs@vger.kernel.org, pc@manguebit.org, stfrench@microsoft.com
Subject: [PATCH v3] cifs: prevent readdir from changing file size due to stale directory metadata
Date: Tue, 21 Jul 2026 12:39:18 -0500 [thread overview]
Message-ID: <20260721173919.1762653-1-sorenson@redhat.com> (raw)
Windows Server's directory enumeration metadata lags behind the actual
file state immediately after a local modification. If a concurrent
readdir() runs while this lag exists, it overwrites the correct locally-
cached i_size with the stale value from the server's directory listing.
A subsequent stat() within the actimeo window returns the wrong size.
This manifests in two ways:
- After write+close: stat() returns 0 for a file just written
- After rename: stat() on the renamed file returns 0
Both are the same root cause. The bug does not reproduce against Samba
(no directory metadata lag) or with actimeo=0 (bypasses the attribute
cache).
The existing is_size_safe_to_change() check only blocked stale readdir
size updates while an active RW lease was held. It does not protect
the window after the last writable handle closes, which is when the
race occurs.
The fix adds cifsInodeInfo->time_last_write, stamped at writable close
and at truncate. is_size_safe_to_change() blocks readdir from updating
i_size within acregmax jiffies of that timestamp. When a size update is
blocked and the server value differs from the cached one, the attribute
cache is invalidated, forcing a fresh QUERY_INFO on the next stat() that
returns the authoritative size from the server's open-file table rather
than the stale directory enumeration.
time_last_write is refreshed at the actual server close in
smb2_deferred_work_close() and in the cifs_close_deferred_file*() drain
paths (triggered by lease/oplock breaks and tcon teardown) to ensure the
protection window is anchored to the real close time when closetimeo > 0.
Memory ordering: time_last_write uses smp_store_release() at all write
sites. For the close path, the spinlock release in _cifsFileInfo_put()
forms the store-release that pairs with is_inode_writable()'s
spin_lock() (load-acquire), guaranteeing that the subsequent
smp_load_acquire() on time_last_write observes any concurrent close.
The setattr path uses smp_store_release() directly, relying on
acquire-release semantics; store propagation (nanoseconds) is negligible
relative to acregmax (seconds).
Testing
-------
Reproduces against Windows Server 2022 with SMB 3.1.1 with multiple
concurrent threads; does not reproduce against Samba or with actimeo=0.
A reproducer exercising concurrent write+close+stat and rename+stat with
concurrent readdir was run for 50,000+ iterations without hitting the bug.
A reproducer is available at https://github.com/fsorenson/cifs_cache_race_repro/
v3: replace CIFS_INO_LOCK serialization approach with time_last_write
tracking; single patch now covers both observed symptoms
v2: fix malformed patch
Frank Sorenson (1):
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/file.c | 64 ++++++++++++++++++++++++++++++++++++++++++++----
fs/smb/client/inode.c | 4 +++
fs/smb/client/misc.c | 21 +++++++++++++---
5 files changed, 83 insertions(+), 8 deletions(-)
--
2.55.0
next reply other threads:[~2026-07-21 17:39 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-21 17:39 Frank Sorenson [this message]
2026-07-21 17:39 ` [PATCH] cifs: prevent readdir from changing file size due to stale directory metadata 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=20260721173919.1762653-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