{"id":21694,"date":"2026-09-12T04:31:32","date_gmt":"2026-09-11T20:31:32","guid":{"rendered":"https:\/\/www.54iter.cn\/?vuln=cve-2026-89772"},"modified":"2026-09-12T04:31:32","modified_gmt":"2026-09-11T20:31:32","slug":"cve-2026-89772","status":"publish","type":"vuln","link":"https:\/\/www.54iter.cn\/?vuln=cve-2026-89772","title":{"rendered":"CVE-2026-89772 &#8211; In the Linux kernel, the following vulnerability has been resolved:\n\nbtrfs: write-protect"},"content":{"rendered":"<p>In the Linux kernel, the following vulnerability has been resolved:<\/p>\n<p>btrfs: write-protect folios during data writeback<\/p>\n<p>commit 095be159f3eb (&#8220;btrfs: unify folio dirty flag clearing&#8221;) replaced<br \/>\nthe folio_clear_dirty_for_io() call in extent_write_cache_pages() with a<br \/>\nplain folio_test_dirty() check. Besides clearing the dirty flag,<br \/>\nfolio_clear_dirty_for_io() also calls folio_mkclean(), which write-protects<br \/>\nthe shared mmap PTEs mapping the folio. Note that we still do call<br \/>\nfolio_clear_dirty_for_io() later in submit_one_sector() when we clear<br \/>\ndirty on the last sector of the folio (the only sector for non-subpage<br \/>\ncases). But we lost this early call in extent_write_cache_pages().<\/p>\n<p>Without the extra write-protection, a process with the file mmap-ed can<br \/>\nmodify a sector while it is being used by writeback in a way that<br \/>\nexpects a stable folio (checksumming, compressing, copying, etc&#8230;)<br \/>\nwithout faulting, which manifests as a handful of concrete bugs.<\/p>\n<p>1. For large folios or subpage sectorsize, it is possible to submit a bio<br \/>\nwhich does not cover the whole folio. When this happens, we will have a<br \/>\nbio in flight for a folio that we have *not* called<br \/>\nfolio_clear_dirty_for_io() on. If a task with an existing mmap-ed PTE<br \/>\nwrites (without faulting..) in this window, it can result in<br \/>\ncorruptions. If the write arrives while the checksumming or writing itself<br \/>\nis underway, this can result in an invalid checksum and later corruption<br \/>\nreports on read. If the write arrives after checksumming\/writing is done<br \/>\nbut before the last sector dirty is cleared, then the write is present<br \/>\nin page cache but doesn&#8217;t affect the dirty tracking and will be lost<br \/>\nwhen the folio is fully finished being submitted and the dirty bit<br \/>\nis cleared. This results in losing the write even if fsync() is called.<\/p>\n<p>2. For zoned submissions which are done in batch separate from the main<br \/>\nextent_writepage() loop, we also risk csum violations for those<br \/>\nsubmissions. Zoned writes are clamped to max_zone_append_size and are<br \/>\nnot aligned with folios, so a submission can span two folios. The first<br \/>\nfolio being processed in extent_write_cache_pages() will call<br \/>\nextent_write_locked_range() which will submit the partial range of the<br \/>\nnext folio, while the rest of that folio could still be dirty. So<br \/>\nclearing dirty on the submitted sectors doesn&#8217;t call<br \/>\nfolio_clear_dirty_for_io() and we have the same issue. Since<br \/>\nextent_write_cache_pages() skips these batch submitted folios (they are<br \/>\nalready marked for writeback from submission by the preceding folio), we<br \/>\nmust add the extra write protection in lock_delalloc_folios().<\/p>\n<p>3. For inline extents this will subtly risk losing writes that happen<br \/>\nafter\/while we copy the inline extent but before we clear dirty on<br \/>\nthe folio.<\/p>\n<p>4. For folios spanning EOF, mmap could tamper with the zeroed bytes past<br \/>\nEOF and cause them to be persisted where future faults would improperly<br \/>\nsee them instead of zeros.<\/p>\n<p>5. Finally, for compressed extents, we risk modifying the folios while we<br \/>\nwork on compressing them which will result in corrupted compressed data.<br \/>\nSpecifically, in run_delalloc_compressed() we queue up work to do<br \/>\ncompress_file_range() in BTRFS_COMPRESSION_CHUNK_SIZE (512K) chunks which<br \/>\nwill call btrfs_folio_clamp_clear_dirty() on the range. For non-subpage,<br \/>\nthis will always clear the whole folio, safely. For subpage, we risk a<br \/>\npartial clear here as well. In particular, imagine a 2M folio broken up<br \/>\ninto 512K chunks of work which might start compression work on one chunk<br \/>\nbefore all the chunks compress_file_range() workers have gotten far<br \/>\nenough to finish clearing all the dirty bitmaps of the folio and getting<br \/>\nto folio_clear_dirty_for_io(). Large folios on the edges of submission<br \/>\nranges are similarly at risk to be only partly cleared.<br \/>\nThis particular gap was introduced by a second patch in the same series:<br \/>\ncommit a4ef54dbb576 (&#8220;btrfs: make extent_range_clear_dirty_for_io() to handle sector size &lt; page size cases&quot;)<\/p>\n<p>We cannot simply restore the call to folio_clear<br \/>\n&#8212;truncated&#8212;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In the Linux kernel, the following vulnerability has been resolved:<\/p>\n<p>btrfs: write-protect folios during data writeback<\/p>\n<p>commit 095be159f3eb (&#8220;btrfs: unify folio dirty flag clearing&#8221;) replaced<br \/>\nthe foli<\/p>\n","protected":false},"template":"","meta":[],"categories":[],"tags":[],"class_list":["post-21694","vuln","type-vuln","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/www.54iter.cn\/index.php?rest_route=\/wp\/v2\/vuln\/21694","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.54iter.cn\/index.php?rest_route=\/wp\/v2\/vuln"}],"about":[{"href":"https:\/\/www.54iter.cn\/index.php?rest_route=\/wp\/v2\/types\/vuln"}],"wp:attachment":[{"href":"https:\/\/www.54iter.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=21694"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.54iter.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=21694"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.54iter.cn\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=21694"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}