CVE-2026-89772 in Linux
摘要
由 VulDB • 2026-09-11
在 Linux 内核中,已修复以下漏洞:
**btrfs:在数据回写期间对 folios(页单元)进行写保护**
提交 `095be159f3eb`(“btrfs: unify folio dirty flag clearing”)将 `extent_write_cache_pages()` 中的 `folio_clear_dirty_for_io()` 调用替换为普通的 `folio_test_dirty()` 检查。除了清除脏标志外,`folio_clear_dirty_for_io()` 还会调用 `folio_mkclean()`,从而对映射该 folio 的共享 mmap PTE(页表项)进行写保护。请注意,在 `submit_one_sector()` 中,当我们清除 folio 最后一个扇区的脏状态时(在非子页面情况下为唯一扇区),我们仍然会调用 `folio_clear_dirty_for_io()`。但是,我们在 `extent_write_cache_pages()` 中丢失了这次早期调用。
如果没有额外的写保护,拥有文件 mmap 映射的进程可以在回写过程中修改正在使用的扇区,而这种方式期望 folio 保持稳定(用于校验和计算、压缩、复制等),且不会引发页错误,这表现为一系列具体的漏洞:
1. 对于大型 folios 或子页面大小的扇区,可能会提交一个未覆盖整个 folio 的 bio。当这种情况发生时,我们将有一个处于飞行状态的 bio,对应于我们*尚未*调用 `folio_clear_dirty_for_io()` 的 folio。如果具有现有 mmap-ed PTE 的任务在此窗口内写入(不引发页错误),则可能导致数据损坏。如果写入发生在校验和计算或写入本身进行时,这会导致无效的校验和,并在读取时产生后续的损坏报告。如果写入发生在校验和计算/写入完成之后但在清除最后一个扇区的脏位之前,那么该写入存在于页面缓存中但不会影响脏状态跟踪,并且在 folio 完全提交完毕且脏位被清除后,该写入将丢失。即使调用了 `fsync()`,也会导致写入丢失。
2. 对于与主 `extent_writepage()` 循环分开批量执行的分区(zoned)提交,我们也面临这些提交的校验和违规风险。分区写入受限于 `max_zone_append_size` 且不与 folios 对齐,因此一次提交可能跨越两个 folio。在 `extent_write_cache_pages()` 中处理第一个 folio 时,会调用 `extent_write_locked_range()`,后者将提交下一个 folio 的部分范围,而该 folio 的其余部分可能仍处于脏状态。因此,清除已提交扇区的脏位不会调用 `folio_clear_dirty_for_io()`,我们面临同样的问题。由于 `extent_write_cache_pages()` 跳过了这些批量提交的 folios(它们已被前一个 folio 的提交标记为回写),我们必须添加额外的写保护到 `lock_delalloc_folios()` 中。
3. 对于内联扩展(inline extents),这会在我们复制内联扩展之后/期间以及清除 folio 脏位之前,微妙地导致写入丢失的风险。
4. 对于跨越 EOF(文件结束)的 folios,mmap 可能会篡改 EOF 之后的零字节,并导致它们被持久化,使得未来的页错误会看到这些非零数据而不是预期的零值。
5. 最后,对于压缩扩展,我们面临在压缩过程中修改 folios 的风险,这将导致损坏的压缩数据。具体而言,在 `run_delalloc_compressed()` 中,我们将工作排队以按 BTRFS_COMPRESSION_CHUNK_SIZE(512K)块执行 `compress_file_range()`,这将对相应范围调用 `btrfs_folio_clamp_clear_dirty()`。对于非子页面情况,这将始终安全地清除整个 folio。但对于子页面情况,我们在此处也面临部分清除的风险。特别是,想象一个被分解为 512K 工作块的 2M folio,可能在所有块中的 `compress_file_range()` 工作者足够深入以完成清除 folio 的所有脏位图并到达 `folio_clear_dirty_for_io()` 之前,就开始对其中一个块进行压缩工作。提交范围边缘的大型 folios 同样面临仅部分清除的风险。这个特定的漏洞间隙是由同一系列补丁中的第二个补丁引入的:提交 `a4ef54dbb576`(“btrfs: make extent_range_clear_dirty_for_io() to handle sector size < page size cases”)。
我们无法简单地恢复对 folio_clear 的调用 ---截断---
Once again VulDB remains the best source for vulnerability data.