From: Joanne Koong <joannelkoong(a)gmail.com>
[ Upstream commit 7aa6bc3e8766990824f66ca76c19596ce10daf3e ]
iomap_adjust_read_range() assumes that the position and length passed in
are block-aligned. This is not always the case however, as shown in the
syzbot generated case for erofs. This causes too many bytes to be
skipped for uptodate blocks, which results in returning the incorrect
position and length to read in. If all the blocks are uptodate, this
underflows length and returns a position beyond the folio.
Fix the calculation to also take into account the block offset when
calculating how many bytes can be skipped for uptodate blocks.
Signed-off-by: Joanne Koong <joannelkoong(a)gmail.com>
Tested-by: syzbot(a)syzkaller.appspotmail.com
Reviewed-by: Brian Foster <bfoster(a)redhat.com>
Reviewed-by: Christoph Hellwig <hch(a)lst.de>
Signed-off-by: Christian Brauner <brauner(a)kernel.org>
Signed-off-by: Sasha Levin <sashal(a)kernel.org>
---
LLM Generated explanations, may be completely bogus:
Now I have all the information needed for a comprehensive analysis. Let
me compile my findings.
---
## COMPREHENSIVE ANALYSIS: iomap_adjust_read_range() Bug Fix
### 1. COMMIT MESSAGE ANALYSIS
**Subject:** `iomap: adjust read range correctly for non-block-aligned
positions`
**Key Points from Message:**
- The function `iomap_adjust_read_range()` **assumes block-aligned
positions**, but this assumption is violated
- Bug was discovered via **syzbot testing with erofs** filesystem
- Causes "too many bytes to be skipped for uptodate blocks"
- Results in **incorrect position and length to read**
- If all blocks are uptodate: **underflows length** and **returns
position beyond the folio**
**Tags Analysis:**
- ✅ `Tested-by: syzbot(a)syzkaller.appspotmail.com` - Automated testing
confirmed
- ✅ `Reviewed-by: Brian Foster <bfoster(a)redhat.com>` - Red Hat
filesystem expert
- ✅ `Reviewed-by: Christoph Hellwig <hch(a)lst.de>` - Major iomap author
- ✅ `Signed-off-by: Christian Brauner <brauner(a)kernel.org>` - VFS
maintainer
- ❌ **No `Cc: stable(a)vger.kernel.org`** tag
- ❌ **No `Fixes:` tag** pointing to original buggy commit
### 2. CODE CHANGE ANALYSIS
**Bug Location:** `fs/iomap/buffered-io.c`, function
`iomap_adjust_read_range()`
**The Buggy Code (lines 246-253 before fix):**
```c
for (i = first; i <= last; i++) {
if (!ifs_block_is_uptodate(ifs, i))
break;
*pos += block_size; // Bug: assumes pos is block-aligned
poff += block_size;
plen -= block_size;
first++;
}
```
**Technical Root Cause:**
When `*pos` has a sub-block offset (e.g., `pos = 512` in a 1024-byte
block):
- `first = poff >> block_bits` computes the block index correctly (block
0)
- But the loop skips a full `block_size` per block, ignoring the offset
**Example demonstrating the bug:**
- Block size: 1024 bytes
- Position: 512 (half-way into block 0)
- Block 0 is uptodate
**Old buggy calculation:**
- Skip full 1024 bytes → `pos = 1536`, overshoots by 512 bytes
- `plen -= 1024` → underflows if `plen < 1024`
**Fixed calculation:**
```c
blocks_skipped = i - first;
if (blocks_skipped) {
unsigned long block_offset = *pos & (block_size - 1); // = 512
unsigned bytes_skipped =
(blocks_skipped << block_bits) - block_offset; // = 1024 -
512 = 512
*pos += bytes_skipped; // Correct: pos = 1024
poff += bytes_skipped;
plen -= bytes_skipped;
}
```
**Consequences of the Bug:**
1. **Integer underflow**: `plen` becomes huge (wraps around as unsigned)
2. **Position beyond folio**: `*pos` can point past the folio boundary
3. **Incorrect read ranges**: Wrong data read, potential corruption
4. **Potential crashes**: Invalid memory access from bad ranges
### 3. CLASSIFICATION
- **Type:** BUG FIX (arithmetic/logic error causing incorrect
calculations)
- **NOT:** Feature addition, cleanup, or optimization
- **Severity:** HIGH - affects filesystem data integrity
- **Scope:** Core iomap buffered I/O infrastructure
### 4. SCOPE AND RISK ASSESSMENT
**Change Size:**
- 13 insertions, 6 deletions
- Single file: `fs/iomap/buffered-io.c`
- Localized to one function
**Risk Level:** LOW
- Fix is mathematically straightforward
- Does not change control flow significantly
- Well-reviewed by iomap experts
- Tested by syzbot
**Affected Subsystem:** `fs/iomap/` - mature, widely-used infrastructure
**Affected Filesystems:** Multiple major filesystems use iomap:
- **XFS** - Primary enterprise Linux filesystem
- **GFS2** - Cluster filesystem
- **erofs** - Read-only filesystem (Android)
- **zonefs** - Zoned storage filesystem
- **fuse** - User-space filesystems
### 5. USER IMPACT
**Who is affected?**
- ALL users of XFS, GFS2, erofs, zonefs, and fuse filesystems
- Particularly affects systems with:
- Block sizes smaller than page size
- Partial folio reads/writes at non-block-aligned offsets
**How severe?**
- Can cause **incorrect data reads**
- Potential **data corruption** in read paths
- Possible **kernel crashes** from invalid memory access
- **Data integrity** issues for critical filesystems
**Triggered by:** syzbot (automated fuzzing) - confirms this is a real
triggerable bug
### 6. STABILITY INDICATORS
- ✅ `Tested-by: syzbot` - Confirmed fix works
- ✅ Two expert reviewers (Brian Foster, Christoph Hellwig)
- ✅ Merged by VFS maintainer (Christian Brauner)
- ⚠️ Relatively new commit (September 2025)
### 7. DEPENDENCY CHECK
**Bug Origin:** Commit `9dc55f1389f95` ("iomap: add support for sub-
pagesize buffered I/O without buffer heads") from **v4.19-rc1** (2018)
**Dependencies for backport:**
- The fix is self-contained
- May need minor adaptation for older kernels:
- v5.x: Uses `struct iomap_page` and pages (not folios)
- v6.x: Uses `struct iomap_folio_state` and folios
- Core logic remains the same across versions
**Related commits:**
- `8e3c15ee0d292` - Minor loop syntax change (`for` → `while`), NOT a
dependency
- `9d875e0eef8ec` - Related fix for unaligned END offsets (separate
issue)
### 8. HISTORICAL CONTEXT
The bug has existed for **~6 years** (since v4.19). It was latent
because:
- Most callers pass block-aligned positions
- erofs triggered an edge case with non-aligned positions
- syzbot's extensive testing exposed it
The original code assumed callers would always pass block-aligned
positions, which is a reasonable assumption violated in specific edge
cases.
---
## BACKPORT RECOMMENDATION
### Arguments FOR backporting:
1. **Fixes a real bug** discovered by automated testing (syzbot)
2. **Affects critical filesystems** (XFS, GFS2) used in production
3. **Small, surgical fix** - 13 lines added, 6 removed
4. **Low regression risk** - mathematically correct fix
5. **Expert-reviewed** by iomap maintainers
6. **Bug existed since v4.19** - all stable trees are affected
7. **Can cause data integrity issues** - serious for a filesystem bug
### Arguments AGAINST backporting:
1. **No `Cc: stable` tag** - maintainers didn't explicitly request
backport
2. **No `Fixes:` tag** - doesn't identify the buggy commit
3. **Relatively new commit** - hasn't had extensive mainline testing yet
4. **Needs adaptation** for older kernels (page vs folio APIs)
### Conclusion:
Despite the missing stable tags, this commit should be backported
because:
- It fixes a **real, triggerable bug** found by syzbot
- The bug can cause **incorrect data reads and potential corruption**
- It affects **major filesystems** (XFS, GFS2) used in production
environments
- The fix is **small, contained, and well-reviewed**
- The **risk of NOT fixing** (data corruption) outweighs the risk of the
fix (minimal)
The absence of `Cc: stable` may be an oversight, as this clearly meets
stable criteria.
**YES**
fs/iomap/buffered-io.c | 19 +++++++++++++------
1 file changed, 13 insertions(+), 6 deletions(-)
diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c
index 8b847a1e27f13..1c95a0a7b302d 100644
--- a/fs/iomap/buffered-io.c
+++ b/fs/iomap/buffered-io.c
@@ -240,17 +240,24 @@ static void iomap_adjust_read_range(struct inode *inode, struct folio *folio,
* to avoid reading in already uptodate ranges.
*/
if (ifs) {
- unsigned int i;
+ unsigned int i, blocks_skipped;
/* move forward for each leading block marked uptodate */
- for (i = first; i <= last; i++) {
+ for (i = first; i <= last; i++)
if (!ifs_block_is_uptodate(ifs, i))
break;
- *pos += block_size;
- poff += block_size;
- plen -= block_size;
- first++;
+
+ blocks_skipped = i - first;
+ if (blocks_skipped) {
+ unsigned long block_offset = *pos & (block_size - 1);
+ unsigned bytes_skipped =
+ (blocks_skipped << block_bits) - block_offset;
+
+ *pos += bytes_skipped;
+ poff += bytes_skipped;
+ plen -= bytes_skipped;
}
+ first = i;
/* truncate len if we find any trailing uptodate block(s) */
while (++i <= last) {
--
2.51.0
If a DIO read or an unbuffered read request extends beyond the EOF, the
server will return a short read and a status code indicating that EOF was
hit, which gets translated to -ENODATA. Note that the client does not cap
the request at i_size, but asks for the amount requested in case there's a
race on the server with a third party.
Now, on the client side, the request will get split into multiple
subrequests if rsize is smaller than the full request size. A subrequest
that starts before or at the EOF and returns short data up to the EOF will
be correctly handled, with the NETFS_SREQ_HIT_EOF flag being set,
indicating to netfslib that we can't read more.
If a subrequest, however, starts after the EOF and not at it, HIT_EOF will
not be flagged, its error will be set to -ENODATA and it will be abandoned.
This will cause the request as a whole to fail with -ENODATA.
Fix this by setting NETFS_SREQ_HIT_EOF on any subrequest that lies beyond
the EOF marker.
This can be reproduced by mounting with "cache=none,sign,vers=1.0" and
doing a read of a file that's significantly bigger than the size of the
file (e.g. attempting to read 64KiB from a 16KiB file).
Fixes: a68c74865f51 ("cifs: Fix SMB1 readv/writev callback in the same way as SMB2/3")
Signed-off-by: David Howells <dhowells(a)redhat.com>
cc: Steve French <sfrench(a)samba.org>
cc: Paulo Alcantara <pc(a)manguebit.org>
cc: Shyam Prasad N <sprasad(a)microsoft.com>
cc: linux-cifs(a)vger.kernel.org
cc: netfs(a)lists.linux.dev
cc: linux-fsdevel(a)vger.kernel.org
---
fs/smb/client/cifssmb.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c
index 645831708e1b..1871d2c1a8e0 100644
--- a/fs/smb/client/cifssmb.c
+++ b/fs/smb/client/cifssmb.c
@@ -1395,7 +1395,7 @@ cifs_readv_callback(struct mid_q_entry *mid)
} else {
size_t trans = rdata->subreq.transferred + rdata->got_bytes;
if (trans < rdata->subreq.len &&
- rdata->subreq.start + trans == ictx->remote_i_size) {
+ rdata->subreq.start + trans >= ictx->remote_i_size) {
rdata->result = 0;
__set_bit(NETFS_SREQ_HIT_EOF, &rdata->subreq.flags);
} else if (rdata->got_bytes > 0) {
Hi there,
While running performance benchmarks for the 5.15.196 LTS tags , it was
observed that several regressions across different benchmarks is being
introduced when compared to the previous 5.15.193 kernel tag. Running an
automated bisect on both of them narrowed down the culprit commit to:
- 5666bcc3c00f7 Revert "cpuidle: menu: Avoid discarding useful
information" for 5.15
Regressions on 5.15.196 include:
-9.3% : Phoronix pts/sqlite using 2 processes on OnPrem X6-2
-6.3% : Phoronix system/sqlite on OnPrem X6-2
-18% : rds-stress -M 1 (readonly rdma-mode) metrics with 1 depth & 1
thread & 1M buffer size on OnPrem X6-2
-4 -> -8% : rds-stress -M 2 (writeonly rdma-mode) metrics with 1 depth &
1 thread & 1M buffer size on OnPrem X6-2
Up to -30% : Some Netpipe metrics on OnPrem X5-2
The culprit commits' messages mention that these reverts were done due
to performance regressions introduced in Intel Jasper Lake systems but
this revert is causing issues in other systems unfortunately. I wanted
to know the maintainers' opinion on how we should proceed in order to
fix this. If we reapply it'll bring back the previous regressions on
Jasper Lake systems and if we don't revert it then it's stuck with
current regressions. If this problem has been reported before and a fix
is in the works then please let me know I shall follow developments to
that mail thread.
Thanks & Regards,
Harshvardhan
When fsl_edma_alloc_chan_resources() fails after clk_prepare_enable(),
the error paths only free IRQs and destroy the TCD pool, but forget to
call clk_disable_unprepare(). This causes the channel clock to remain
enabled, leaking power and resources.
Fix it by disabling the channel clock in the error unwind path.
Fixes: d8d4355861d8 ("dmaengine: fsl-edma: add i.MX8ULP edma support")
Cc: stable(a)vger.kernel.org
Suggested-by: Frank Li <Frank.Li(a)nxp.com>
Signed-off-by: Zhen Ni <zhen.ni(a)easystack.cn>
---
Changes in v2:
- Remove FSL_EDMA_DRV_HAS_CHCLK check
Changes in v3:
- Remove cleanup
Changes in v4:
- Re-send as a new thread
---
drivers/dma/fsl-edma-common.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/dma/fsl-edma-common.c b/drivers/dma/fsl-edma-common.c
index 4976d7dde080..11655dcc4d6c 100644
--- a/drivers/dma/fsl-edma-common.c
+++ b/drivers/dma/fsl-edma-common.c
@@ -852,6 +852,7 @@ int fsl_edma_alloc_chan_resources(struct dma_chan *chan)
free_irq(fsl_chan->txirq, fsl_chan);
err_txirq:
dma_pool_destroy(fsl_chan->tcd_pool);
+ clk_disable_unprepare(fsl_chan->clk);
return ret;
}
--
2.20.1