CVE-2026-89772 in Linux
Tóm tắt
Bởi VulDB • 11/09/2026
Trong kernel Linux, các lỗ hổng sau đây đã được khắc phục:
btrfs: bảo vệ chống ghi (write-protect) đối với folios trong quá trình writeback dữ liệu
commit 095be159f3eb ("btrfs: thống nhất việc xóa cờ dirty của folio") đã thay thế lời gọi `folio_clear_dirty_for_io()` trong `extent_write_cache_pages()` bằng một phép kiểm tra đơn giản là `folio_test_dirty()`. Ngoài việc xóa cờ dirty, `folio_clear_dirty_for_io()` còn gọi đến `folio_mkclean()`, nhằm bảo vệ chống ghi các PTE (Page Table Entry) chia sẻ ánh xạ folio. Lưu ý rằng chúng ta vẫn gọi `folio_clear_dirty_for_io()` sau đó trong `submit_one_sector()` khi xóa trạng thái dirty trên sector cuối cùng của folio (sector duy nhất đối với trường hợp non-subpage). Tuy nhiên, chúng ta đã mất đi lời gọi sớm này trong `extent_write_cache_pages()`.
Nếu không có sự bảo vệ chống ghi bổ sung, một tiến trình có file được mmap hóa có thể sửa đổi một sector trong khi nó đang được sử dụng bởi writeback theo cách mong đợi một folio ổn định (tính checksum, nén, sao chép, v.v...) mà không gây ra fault, điều này biểu hiện dưới dạng một vài lỗi cụ thể.
1. Đối với các folios lớn hoặc kích thước sector subpage, có khả năng submit một bio không bao phủ toàn bộ folio. Khi điều này xảy ra, chúng ta sẽ có một bio đang xử lý (in flight) cho một folio mà chúng ta *chưa* gọi `folio_clear_dirty_for_io()`. Nếu một task với PTE mmap hóa hiện tại ghi dữ liệu (không gây fault..) trong khoảng thời gian này, nó có thể dẫn đến hỏng hóc dữ liệu. Nếu lệnh ghi xảy ra khi quá trình tính checksum hoặc chính việc ghi đang diễn ra, điều này có thể dẫn đến checksum không hợp lệ và sau đó là các báo cáo về lỗi corrupt khi đọc lại. Nếu lệnh ghi xảy ra sau khi quá trình tính checksum/ghi đã hoàn tất nhưng trước khi cờ dirty của sector cuối cùng được xóa, thì dữ liệu ghi sẽ tồn tại trong page cache nhưng không ảnh hưởng đến việc theo dõi trạng thái dirty và sẽ bị mất đi khi folio hoàn thành việc submit toàn bộ và bit dirty được xóa. Điều này dẫn đến việc mất dữ liệu ghi ngay cả khi `fsync()` được gọi.
2. Đối với các lệnh gửi (submissions) zoned, được thực hiện theo lô riêng biệt khỏi vòng lặp chính `extent_writepage()`, chúng ta cũng có nguy cơ vi phạm checksum (csum violations). Các phép ghi zoned bị giới hạn ở `max_zone_append_size` và không căn chỉnh với folios, do đó một lệnh gửi có thể trải dài qua hai folio. Folio đầu tiên được xử lý trong `extent_write_cache_pages()` sẽ gọi `extent_write_locked_range()`, điều này sẽ submit phạm vi một phần của folio tiếp theo, trong khi phần còn lại của folio đó vẫn có thể đang ở trạng thái dirty. Vì vậy, việc xóa cờ dirty trên các sector đã submit không gọi đến `folio_clear_dirty_for_io()` và chúng ta gặp phải vấn đề tương tự. Do `extent_write_cache_pages()` bỏ qua các folios được submit theo lô này (chúng đã được đánh dấu cho writeback từ quá trình submit bởi folio trước đó), chúng ta cần thêm sự bảo vệ chống ghi trong `lock_delalloc_folios()`.
3. Đối với các extents nội tuyến (inline extents), điều này sẽ gây rủi ro âm thầm làm mất dữ liệu ghi xảy ra sau/khi chúng ta sao chép extent nội tuyến nhưng trước khi xóa cờ dirty trên folio.
4. Đối với các folio vượt quá EOF, mmap có thể can thiệp vào các byte được điền bằng 0 phía sau EOF và khiến chúng bị lưu trữ vĩnh viễn, trong tương lai các lỗi (faults) sẽ nhìn thấy sai lệch thay vì giá trị 0.
5. Cuối cùng, đối với các extents đã nén, chúng ta có nguy cơ sửa đổi các folio trong khi đang xử lý việc nén dữ liệu, điều này sẽ dẫn đến dữ liệu nén bị hỏng. Cụ thể, trong `run_delalloc_compressed()`, chúng ta lên lịch công việc để thực hiện `compress_file_range()` theo từng khối kích thước BTRFS_COMPRESSION_CHUNK_SIZE (512K), các khối này sẽ gọi `btrfs_folio_clamp_clear_dirty()` trên phạm vi tương ứng. Đối với trường hợp non-subpage, điều này luôn xóa toàn bộ folio một cách an toàn. Đối với subpage, chúng ta cũng có nguy cơ thực hiện việc xóa từng phần (partial clear). Cụ thể, hãy tưởng tượng một folio 2M được chia thành các khối 512K để xử lý; công việc nén có thể bắt đầu trên một khối trước khi tất cả các worker `compress_file_range()` của các khối khác tiến đủ xa để hoàn tất việc xóa toàn bộ bitmap dirty của folio và đạt đến lệnh gọi `folio_clear_dirty_for_io()`. Các folios lớn ở rìa của phạm vi submit cũng tương tự, có nguy cơ chỉ được xóa một phần. Lỗ hổng cụ thể này đã được giới thiệu bởi patch thứ hai trong cùng chuỗi: commit a4ef54dbb576 ("btrfs: make extent_range_clear_dirty_for_io() to handle sector size < page size cases")
Chúng ta không thể đơn giản khôi phục lời gọi đến `folio_clear ---truncated---
If you want to get best quality of vulnerability data, you may have to visit VulDB.