| From bd71fb3fea9945987053968f028a948997cba8cc Mon Sep 17 00:00:00 2001 |
| From: Joanne Koong <joannelkoong@gmail.com> |
| Date: Tue, 17 Mar 2026 13:39:35 -0700 |
| Subject: iomap: fix invalid folio access when i_blkbits differs from I/O granularity |
| |
| From: Joanne Koong <joannelkoong@gmail.com> |
| |
| commit bd71fb3fea9945987053968f028a948997cba8cc upstream. |
| |
| Commit aa35dd5cbc06 ("iomap: fix invalid folio access after |
| folio_end_read()") partially addressed invalid folio access for folios |
| without an ifs attached, but it did not handle the case where |
| 1 << inode->i_blkbits matches the folio size but is different from the |
| granularity used for the IO, which means IO can be submitted for less |
| than the full folio for the !ifs case. |
| |
| In this case, the condition: |
| |
| if (*bytes_submitted == folio_len) |
| ctx->cur_folio = NULL; |
| |
| in iomap_read_folio_iter() will not invalidate ctx->cur_folio, and |
| iomap_read_end() will still be called on the folio even though the IO |
| helper owns it and will finish the read on it. |
| |
| Fix this by unconditionally invalidating ctx->cur_folio for the !ifs |
| case. |
| |
| Reported-by: Johannes Thumshirn <johannes.thumshirn@wdc.com> |
| Tested-by: Johannes Thumshirn <johannes.thumshirn@wdc.com> |
| Link: https://lore.kernel.org/linux-fsdevel/b3dfe271-4e3d-4922-b618-e73731242bca@wdc.com/ |
| Fixes: b2f35ac4146d ("iomap: add caller-provided callbacks for read and readahead") |
| Cc: stable@vger.kernel.org |
| Signed-off-by: Joanne Koong <joannelkoong@gmail.com> |
| Link: https://patch.msgid.link/20260317203935.830549-1-joannelkoong@gmail.com |
| Reviewed-by: Christoph Hellwig <hch@lst.de> |
| Signed-off-by: Christian Brauner <brauner@kernel.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| fs/iomap/buffered-io.c | 15 ++++++++++----- |
| 1 file changed, 10 insertions(+), 5 deletions(-) |
| |
| --- a/fs/iomap/buffered-io.c |
| +++ b/fs/iomap/buffered-io.c |
| @@ -506,6 +506,7 @@ static int iomap_read_folio_iter(struct |
| loff_t length = iomap_length(iter); |
| struct folio *folio = ctx->cur_folio; |
| size_t folio_len = folio_size(folio); |
| + struct iomap_folio_state *ifs; |
| size_t poff, plen; |
| loff_t pos_diff; |
| int ret; |
| @@ -517,7 +518,7 @@ static int iomap_read_folio_iter(struct |
| return iomap_iter_advance(iter, length); |
| } |
| |
| - ifs_alloc(iter->inode, folio, iter->flags); |
| + ifs = ifs_alloc(iter->inode, folio, iter->flags); |
| |
| length = min_t(loff_t, length, folio_len - offset_in_folio(folio, pos)); |
| while (length) { |
| @@ -548,11 +549,15 @@ static int iomap_read_folio_iter(struct |
| |
| *bytes_submitted += plen; |
| /* |
| - * If the entire folio has been read in by the IO |
| - * helper, then the helper owns the folio and will end |
| - * the read on it. |
| + * Hand off folio ownership to the IO helper when: |
| + * 1) The entire folio has been submitted for IO, or |
| + * 2) There is no ifs attached to the folio |
| + * |
| + * Case (2) occurs when 1 << i_blkbits matches the folio |
| + * size but the underlying filesystem or block device |
| + * uses a smaller granularity for IO. |
| */ |
| - if (*bytes_submitted == folio_len) |
| + if (*bytes_submitted == folio_len || !ifs) |
| ctx->cur_folio = NULL; |
| } |
| |