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 12/15] netfs: Switch folioq to bvecq
Date: Sat, 07 Nov 2026 19:25:40 +0000 [thread overview]
Message-ID: <sashiko-outbox-203207@kernel.org> (raw)
In-Reply-To: <20261107160823.1465553-13-dhowells@redhat.com>
Thank you for your contribution! Sashiko AI review found 2 potential
issues to consider:
- [Critical] netfs: use-after-free on bvecq boundary in
netfs_read_to_pagecache()
- [High] netfs: use-after-free in netfs_reset_iter() on subrequest retry
--
commit 5744e4078be45f40cce041679e9567cd02a0f062
Author: David Howells <dhowells@redhat.com>
netfs: Switch folioq to bvecq
This commit switches netfslib's rolling_buffer and buffered read/writeback
paths from folio_queue to bvecq.
> diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c
> index 69e68ef0652a7..61b44af6774b7 100644
> --- a/fs/netfs/buffered_read.c
> +++ b/fs/netfs/buffered_read.c
> @@ -215,7 +215,7 @@ static void netfs_issue_read(struct netfs_io_request *rreq,
> * otherwise we set the deprecated PG_private_2.
> */
> static void netfs_mark_copy_to_cache(struct netfs_io_request *rreq,
> - struct folio_queue **fq,
> + struct bvecq **bq,
> unsigned int *offset,
> int *slot,
> size_t len,
> @@ -225,21 +225,21 @@ static void netfs_mark_copy_to_cache(struct netfs_io_request *rreq,
> struct folio *folio;
> size_t fsize, overlap;
>
> - if (!*fq)
> + if (!*bq)
> break;
> - if (*slot >= folioq_count(*fq)) {
> - *fq = (*fq)->next;
> + if (!bvecq_acquire_slot(*bq, *slot)) {
> + *bq = bvecq_next(*bq);
> *slot = 0;
> *offset = 0;
> continue;
> }
>
> /* Determine how much the subreq overlaps the folio, if at all. */
> - fsize = folioq_folio_size(*fq, *slot);
> + fsize = (*bq)->bv[*slot].bv_len;
> overlap = min(len, fsize - *offset);
>
> if (overlap > 0 && copy) {
> - folio = folioq_folio(*fq, *slot);
> + folio = bvec_folio(&(*bq)->bv[*slot]);
> if (netfs_using_pgpriv2(rreq)) {
> if (!folio_test_private_2(folio))
> folio_start_private_2(folio);
> @@ -275,7 +275,7 @@ static void netfs_read_to_pagecache(struct netfs_io_request *rreq)
> .cached_to[1] = ULLONG_MAX,
> };
> struct fscache_occupancy *occ = &_occ;
> - struct folio_queue *fq = rreq->buffer.tail;
> + struct bvecq *bq = rreq->buffer.tail;
> unsigned int offset = 0;
> ssize_t size = rreq->len;
> uoff_t start = rreq->start;
> @@ -408,10 +408,10 @@ static void netfs_read_to_pagecache(struct netfs_io_request *rreq)
> if (size <= 0)
> netfs_all_subreqs_queued(rreq);
>
> - if (fq) {
> + if (bq) {
> /* See if the cache indicated this should be cached. */
> copy = test_bit(NETFS_SREQ_COPY_TO_CACHE, &subreq->flags);
> - netfs_mark_copy_to_cache(rreq, &fq, &slot, &offset, slice, copy);
> + netfs_mark_copy_to_cache(rreq, &bq, &slot, &offset, slice, copy);
> }
>
> trace_netfs_sreq(subreq, netfs_sreq_trace_submit);
[Severity: Critical]
Can a subrequest ending at a bvecq boundary leave rreq->buffer.iter and bq
pointing at a spent bvecq that gets freed before the next subrequest steps
to bq->next?
In netfs_readahead(), rolling_buffer_bulk_load_from_ra() builds a multi-bvecq
chain (bq0 -> bq1 -> ...) upfront with refcount 1 on each segment. Unlike
iov_iter_folioq_advance(), which stepped to folioq->next as soon as slot hit
folioq_nr_slots(), iov_iter_bvecq_advance() only steps to bq->next at the top
of its do-while (by) loop:
lib/iov_iter.c:iov_iter_bvecq_advance() {
...
do {
if (slot >= bq->nr_slots) {
if (!bq->next)
break;
bq = bq->next;
slot = 0;
continue;
}
...
by -= len;
slot++;
} while (by);
}
netfs_mark_copy_to_cache() does the same in its while (len > 0) loop. When a
subrequest ends at the end of bq0, both rreq->buffer.iter.bvecq and bq are
left pointing at bq0 with slot == bq0->nr_slots, and the next subrequest's
subreq->io_iter copies rreq->buffer.iter before advancing:
CPU0 (issuer) CPU1 (collector)
netfs_read_to_pagecache()
subreq1 covers all of bq0
netfs_prepare_read_iterator(subreq1)
rolling_buffer_advance()
/* iter.bvecq == bq0,
bvecq_slot == bq0->nr_slots */
netfs_mark_copy_to_cache()
/* bq == bq0,
slot == bq0->nr_slots */
netfs_issue_read(subreq1)
netfs_maybe_bulk_drop_ra_refs()
netfs_read_unlock_folios()
rolling_buffer_delete_spent()
bq0->next = NULL;
bvecq_put(bq0); <-- frees bq0
subreq2 prepared for bq1:
netfs_prepare_read_iterator(subreq2)
netfs_limit_bvecq(&rreq->buffer.iter)
bvecq_next(bq0) <-- use-after-free, sees NULL
netfs_mark_copy_to_cache(&bq)
bvecq_next(bq0) <-- use-after-free, sees NULL
The same happens on a single CPU when subreq1 is NETFS_FILL_WITH_ZEROES and
completes synchronously inside netfs_issue_read(). Once bq0 is freed and its
next pointer is cleared, subreq2's iterator is truncated to 0 bytes and the
remaining folios in bq1 are never populated or unlocked.
> diff --git a/fs/netfs/rolling_buffer.c b/fs/netfs/rolling_buffer.c
> index 4347c99452d83..76cede046dcb7 100644
> --- a/fs/netfs/rolling_buffer.c
> +++ b/fs/netfs/rolling_buffer.c
[ ... ]
> @@ -205,44 +205,23 @@ ssize_t rolling_buffer_append(struct rolling_buffer *roll, struct folio *folio,
> * don't return the last buffer to keep the pointers independent, but return
> * NULL instead.
> */
> -struct folio_queue *rolling_buffer_delete_spent(struct rolling_buffer *roll)
> +struct bvecq *rolling_buffer_delete_spent(struct rolling_buffer *roll)
> {
> - struct folio_queue *spent = roll->tail, *next = READ_ONCE(spent->next);
> + struct bvecq *spent = roll->tail, *next = bvecq_next(spent);
>
> if (!next)
> return NULL;
> next->prev = NULL;
> - netfs_folioq_free(spent, netfs_trace_folioq_delete);
> roll->tail = next;
> + spent->next = NULL;
> + bvecq_put(spent);
> return next;
> }
[Severity: High]
Does freeing spent bvecq segments during collection cause a use-after-free in
netfs_reset_iter() when a subrequest spanning multiple bvecq segments is
retried?
subreq->io_iter is initialized from rreq->buffer.iter (or wreq->buffer.iter)
without taking a reference on its starting bvecq segment (bq0). If a
subrequest spans bq0 and bq1, transfers enough data to finish bq0, and then
fails with NETFS_SREQ_NEED_RETRY, both netfs_collect_read_results() and
netfs_collect_write_results() unlock the completed folios in bq0 before
entering the retry path:
fs/netfs/write_collect.c:netfs_collect_write_results() {
...
if (notes & NEED_UNLOCK) {
if (wreq->cleaned_to < wreq->collected_to)
netfs_writeback_unlock_folios(wreq, ¬es);
} else {
wreq->cleaned_to = wreq->collected_to;
}
// TODO: Discard encryption buffers
if (notes & NEED_RETRY)
goto need_retry;
...
}
netfs_writeback_unlock_folios() (and netfs_read_unlock_folios() on the read
side) calls rolling_buffer_delete_spent(), which clears bq0->next = NULL and
frees bq0. When netfs_retry_writes() or netfs_retry_reads() then calls
netfs_reset_iter(subreq) -> iov_iter_advance(&subreq->io_iter, ...),
iov_iter_bvecq_advance() dereferences the freed bq0 from subreq->io_iter.bvecq
and cannot advance to bq1 because bq0->next was cleared.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261107160823.1465553-1-dhowells@redhat.com?part=12
next prev parent reply other threads:[~2026-11-07 19:25 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
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 [this message]
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-203207@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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.