From: sashiko-bot@kernel.org
To: "David Howells" <dhowells@redhat.com>
Cc: v9fs@lists.linux.dev, "Chuck Lever" <cel@kernel.org>,
"Jeff Layton" <jlayton@kernel.org>,
"Anna Schumaker" <anna@kernel.org>
Subject: Re: [PATCH v16 07/15] netfs: Explicitly pass tail padding to netfs_writeback_single()
Date: Sat, 07 Nov 2026 17:46:10 +0000 [thread overview]
Message-ID: <sashiko-outbox-203129@kernel.org> (raw)
In-Reply-To: <20261107160823.1465553-8-dhowells@redhat.com>
Thank you for your contribution! Sashiko AI review found 3 potential
issues to consider:
- [Medium] afs: race on i_size in afs_dir_writepages() triggers
WARN_ON_ONCE()
- [Medium] netfs: netfs_writeback_single() does not set wreq->i_size to
len
- [Medium] afs: i_size exceeding directory_size in writepages triggers
WARN_ON_ONCE()
--
commit 85f14f8d404627b37bc4c8b65ec7be7797cc5f30
Author: David Howells <dhowells@redhat.com>
netfs: Explicitly pass tail padding to netfs_writeback_single()
This commit changes netfs_writeback_single() to take an explicit content
length alongside the full DIO-rounded buffer iterator, and updates AFS
directory and symlink writeback to zero the buffer tail and pass both
lengths.
> diff --git a/fs/afs/dir.c b/fs/afs/dir.c
> index 75f8f70cfeff0..51c158d8f723f 100644
> --- a/fs/afs/dir.c
> +++ b/fs/afs/dir.c
[ ... ]
> @@ -2228,9 +2236,10 @@ static int afs_dir_writepages(struct address_space *mapping,
> }
>
> if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags)) {
> - iov_iter_folio_queue(&iter, ITER_SOURCE, dvnode->directory, 0, 0,
> - i_size_read(&dvnode->netfs.inode));
> - ret = netfs_writeback_single(mapping, wbc, &iter);
> + iov_iter_folio_queue(&iter, ITER_SOURCE, dvnode->directory,
> + 0, 0, dvnode->directory_size);
> + ret = netfs_writeback_single(mapping, wbc, &iter,
> + i_size_read(&dvnode->netfs.inode));
> if (ret == 1)
> ret = 0; /* Skipped write due to lock conflict. */
> }
[Severity: Medium]
Can a concurrent status update grow inode->i_size past
dvnode->directory_size here and trigger the WARN_ON_ONCE() in
netfs_writeback_single()?
RPC completion callbacks such as afs_do_lookup_success() call
afs_vnode_commit_status() -> afs_apply_status() without holding
dvnode->validate_lock:
CPU0 CPU1
afs_dir_writepages()
down_read(&dvnode->validate_lock)
test_bit(AFS_VNODE_DIR_VALID, ...)
iov_iter_folio_queue(...,
dvnode->directory_size)
afs_do_lookup_success()
...
afs_apply_status()
afs_invalidate_dir()
netfs_write_sizes()
i_size_read(&dvnode->netfs.inode)
netfs_writeback_single()
WARN_ON_ONCE(iov_iter_count(iter) < len) <-- triggers warning, -EIO
If a remote modification grows the directory beyond dvnode->directory_size
in that window, netfs_writeback_single() warns and fails writeback with
-EIO.
> diff --git a/fs/afs/symlink.c b/fs/afs/symlink.c
> index a1dbd552ddff0..c18e4f8064234 100644
> --- a/fs/afs/symlink.c
> +++ b/fs/afs/symlink.c
> @@ -228,6 +228,10 @@ int afs_readlink(struct dentry *dentry, char __user *buffer, int buflen)
> /*
> * Write the symlink contents to the cache as a single blob. We then throw
> * away the page we used to receive it.
> + *
> + * Note that the buffer tail is cleared under a shared lock on ->validate_lock
> + * - though hopefully there won't be racing writers since the only
> + * 'modification' is to be either read from the server or locally created.
> */
> int afs_symlink_writepages(struct address_space *mapping,
> struct writeback_control *wbc)
> @@ -247,9 +251,17 @@ int afs_symlink_writepages(struct address_space *mapping,
>
> if (vnode->directory &&
> atomic64_read(&vnode->cb_expires_at) != AFS_NO_CB_PROMISE) {
> + size_t i_size = i_size_read(&vnode->netfs.inode);
> +
> + if (i_size < vnode->directory_size) {
> + iov_iter_folio_queue(&iter, ITER_DEST, vnode->directory, 0, 0,
> + vnode->directory_size);
> + iov_iter_advance(&iter, i_size);
> + iov_iter_zero(vnode->directory_size - i_size, &iter);
> + }
> iov_iter_folio_queue(&iter, ITER_SOURCE, vnode->directory, 0, 0,
> - i_size_read(&vnode->netfs.inode));
> - ret = netfs_writeback_single(mapping, wbc, &iter);
> + vnode->directory_size);
> + ret = netfs_writeback_single(mapping, wbc, &iter, i_size);
> }
[Severity: Medium]
Can i_size exceed vnode->directory_size in afs_symlink_writepages() and
trigger WARN_ON_ONCE(iov_iter_count(iter) < len) in
netfs_writeback_single()?
In afs_do_read_symlink(), vnode->directory is allocated to PAGE_SIZE. If
netfs_read_single() fetches the symlink from the server and the returned
AFSFetchStatus reports status.size > PAGE_SIZE, afs_apply_status() updates
inode->i_size to status.size and sets the callback promise while
netfs_rreq_assess_single() marks the inode dirty. afs_do_read_symlink()
then returns -EFBIG without freeing vnode->directory or clearing the dirty
state. When afs_symlink_writepages() later runs, i_size is larger than
vnode->directory_size, causing netfs_writeback_single() to warn and return
-EIO (which also skips freeing vnode->directory).
> diff --git a/fs/netfs/write_issue.c b/fs/netfs/write_issue.c
> index b8237e1314d8a..e041023811ed0 100644
> --- a/fs/netfs/write_issue.c
> +++ b/fs/netfs/write_issue.c
> @@ -747,26 +747,33 @@ static int netfs_write_folio_single(struct netfs_io_request *wreq,
> * netfs_writeback_single - Write back a monolithic payload
> * @mapping: The mapping to write from
> * @wbc: Hints from the VM
> - * @iter: Data to write, must be ITER_FOLIOQ.
> + * @iter: Data buffer to write from, must be ITER_FOLIOQ.
> + * @len: Amount of data to write.
> *
> * Write a monolithic, non-pagecache object back to the server and/or
> * the cache.
> *
> + * The data buffer may be bigger than @len (as set in @iter's count), and if
> + * so, this function will use the extra as needed to provide padding into the
> + * cache to handle DIO rounding, so the caller should clear it.
> + *
> * Return: 0 if successful; 1 if skipped due to lock conflict and WB_SYNC_NONE;
> * or a negative error code.
> */
> int netfs_writeback_single(struct address_space *mapping,
> struct writeback_control *wbc,
> - struct iov_iter *iter)
> + struct iov_iter *iter, size_t len)
> {
> struct netfs_io_request *wreq;
> struct netfs_inode *ictx = netfs_inode(mapping->host);
> struct folio_queue *fq;
> - size_t size = iov_iter_count(iter);
> + size_t size = len;
> int ret;
>
> if (WARN_ON_ONCE(!iov_iter_is_folioq(iter)))
> return -EIO;
> + if (WARN_ON_ONCE(iov_iter_count(iter) < len))
> + return -EIO;
>
> if (!netfs_wb_begin(ictx, wbc->sync_mode == WB_SYNC_NONE)) {
> /* The VFS will have undirtied the inode. */
[Severity: Medium]
Should netfs_writeback_single() initialize wreq->i_size from len instead of
letting netfs_create_write_req() -> netfs_alloc_request() re-read
i_size_read(inode)?
If inode->i_size changes concurrently (via afs_apply_status() ->
netfs_write_sizes()) between the caller passing len and
netfs_alloc_request() reading wreq->i_size, wreq->i_size and len disagree.
If wreq->i_size < len and the loop advances to a folio with fpos >
wreq->i_size, netfs_write_folio_single() underflows the unsigned
wreq->i_size - fpos subtraction:
fs/netfs/write_issue.c:netfs_write_folio_single() {
...
flen = folio_size(folio);
if (flen > wreq->i_size - fpos) {
flen = wreq->i_size - fpos;
folio_zero_segment(folio, flen, fsize);
to_eof = true;
} else if (flen == wreq->i_size - fpos) {
to_eof = true;
}
...
}
Although the follow-up commit "netfs: Change netfs_writeback_single() to
not look inside the iterator" removes netfs_write_folio_single() and sets
wreq->len = len, it still leaves wreq->i_size initialized from
i_size_read(inode), which causes cachefiles_issue_write() to skip DIO tail
padding when start + len != wreq->i_size and triggers WARN_ON(len &
(cache->bsize - 1)) in cachefiles_collect_write().
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261107160823.1465553-1-dhowells@redhat.com?part=7
next prev parent reply other threads:[~2026-11-07 17:46 UTC|newest]
Thread overview: 31+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-07 16:08 [PATCH v16 00/15] netfs, iov_iter: Use a chain of bio_vec arrays instead of folio_queue David Howells
2026-11-07 16:08 ` [PATCH v16 01/15] Add a function to kmap one page of a multipage bio_vec David Howells
2026-11-07 16:15 ` sashiko-bot
2026-11-07 16:08 ` [PATCH v16 02/15] iov_iter: Add a segmented queue of bio_vec[] David Howells
2026-11-07 16:31 ` sashiko-bot
2026-11-07 16:08 ` [PATCH v16 03/15] netfs: Add some tools for managing bvecq chains David Howells
2026-11-07 16:46 ` sashiko-bot
2026-11-07 16:08 ` [PATCH v16 04/15] iov_iter: Add an API document for the bvecq structure David Howells
2026-11-07 16:47 ` sashiko-bot
2026-11-07 16:08 ` [PATCH v16 05/15] iov_iter: Add a bvecq kunit test David Howells
2026-11-07 17:01 ` sashiko-bot
2026-11-07 16:08 ` [PATCH v16 06/15] afs: Fix an inconsistency in the symlink code David Howells
2026-11-07 17:09 ` sashiko-bot
2026-11-07 16:08 ` [PATCH v16 07/15] netfs: Explicitly pass tail padding to netfs_writeback_single() David Howells
2026-11-07 17:46 ` sashiko-bot [this message]
2026-11-07 16:08 ` [PATCH v16 08/15] netfs: Change netfs_writeback_single() to not look inside the iterator David Howells
2026-11-07 18:09 ` sashiko-bot
2026-11-07 16:08 ` [PATCH v16 09/15] afs: Use a bvecq to hold dir content rather than folioq David Howells
2026-11-07 18:34 ` sashiko-bot
2026-11-07 16:08 ` [PATCH v16 10/15] cifs: Use a bvecq for buffering instead of a folioq David Howells
2026-11-07 18:51 ` sashiko-bot
2026-11-07 16:08 ` [PATCH v16 11/15] smbdirect: Support ITER_BVECQ in smbdirect_map_sges_from_iter() David Howells
2026-11-07 18:57 ` sashiko-bot
2026-11-07 16:08 ` [PATCH v16 12/15] netfs: Switch folioq to bvecq David Howells
2026-11-07 19:25 ` sashiko-bot
2026-11-07 16:08 ` [PATCH v16 13/15] smbdirect: Remove support for ITER_FOLIOQ from smbdirect_map_sges_from_iter() David Howells
2026-11-07 19:29 ` sashiko-bot
2026-11-07 16:08 ` [PATCH v16 14/15] iov_iter: Remove ITER_FOLIOQ David Howells
2026-11-07 19:31 ` sashiko-bot
2026-11-07 16:08 ` [PATCH v16 15/15] netfs: Remove folio_queue David Howells
2026-11-07 19:35 ` sashiko-bot
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=sashiko-outbox-203129@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=anna@kernel.org \
--cc=cel@kernel.org \
--cc=dhowells@redhat.com \
--cc=jlayton@kernel.org \
--cc=sashiko-reviews@lists.linux.dev \
--cc=v9fs@lists.linux.dev \
/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