CVE-2026-92501: ext4: drain in-flight DIO before buffered write fallback

Published Sep 17, 2026
·
Updated

In the Linux kernel, the following vulnerability has been resolved:

ext4: drain in-flight DIO before buffered write fallback

generic/746 started failing intermittently on ext3 (no-extent inodes). The test triggers 'Page cache invalidation failure on direct I/O' warnings and subsequent fsync returns -EIO. Adding a 50ms delay between ext4bufferedwriteiter() and filemapwriteandwaitrange() in ext4diowriteiter() makes the race almost always reproducible.

On no-extent inodes, DIO writes to holes cannot use unwritten extents, so ext4iomapalloc() leaves mflags=0 and ext4mapblocks() returns 0. The iomap layer then returns -ENOTBLK, causing fallback to buffered I/O.

The fallback path in ext4diowriteiter() calls ext4bufferedwriteiter() which dirties pages, then does flush and invalidate. However, there's an unprotected window between ext4bufferedwriteiter() returning (with inode lock released) and the subsequent flush+invalidate.

Concurrent async DIO completions from other threads can run kiocbinvalidatepostdirectwrite() during this window. If pages have been re-dirtied, post-invalidation finds dirty pages and triggers the warning, setting -EIO in the error sequence.

Consider a file with two 4k extents: [hole][written]. Thread A does DIO to the written extent, while thread B does DIO spanning both:

kworker A (4k DIO, allocated block) kworker B (8k DIO, fallback) ----------------------------------- ---------------------------- inodelockshared() inodelockshared() iomapdiorw(): iomapdiorw(): kiocbinvalidatepages -> clean iomapbegin -> -ENOTBLK submitbio (async) dio->size = 0 inodeunlockshared() inodeunlockshared()

[bio pending in block layer] / fallback: lock released / ext4bufferedwriteiter() inodelock(exclusive) genericperformwrite() -> dirty pages [0, 8k] inodeunlock(exclusive)

/ pages dirty, no lock / [bio completes] filemapwriteandwaitrange() iomapdiocomplete() -> flush dirty pages kiocbinvalidatepostdirectwrite() invalidatemappingpages() invalidateinodepages2range() -> finds dirty page! -> diowarnstalepagecache() -> errseqset(-EIO)

This issue can be triggered through normal I/O paths, not just intentionally overlapping DIO writes from userspace. For example, generic/746 uses a loop device where multiple kworkers issue concurrent I/O to the backing file. Additionally, when blocksize < foliosize, non-overlapping DIO writes that share a large folio can also trigger the race.

Add inodediowait() in ext4bufferedwriteiter() before ext4writechecks() to drain all in-flight DIO. This ensures that all DIO clears existing pages before submitting IO (via kiocbinvalidatepages()), all BIO waits for all DIO to complete (via inodediowait()), and ext4writechecks() observes the inode size after all completed DIO so that ext4blockzeroeof() does not race with in-flight DIO, thus eliminating the race.

Affected Software

1 affected component
Linux Linux kernel

Event History

Sep 17, 2026
CVE Published
via MITRE·04:10 PM
Data Sourced
via MITRE·04:10 PM
Description

Frequently Asked Questions

1

Which systems are most likely to encounter this issue?

The described failure occurs on ext3-style no-extent inodes when direct I/O writes target holes and fall back to buffered I/O. It requires concurrency with asynchronous direct-I/O completions from other threads during the fallback path.

2

What operational symptoms indicate that the issue may be occurring?

Kernel logs may contain “Page cache invalidation failure on direct I/O” warnings. Subsequent fsync operations can return -EIO after the error is recorded in the inode error sequence.

3

Does a direct I/O write always trigger the affected fallback path?

No. The fallback described occurs when a no-extent inode cannot allocate an unwritten extent for a direct-I/O write to a hole, causing the iomap layer to return -ENOTBLK and use buffered I/O instead.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203