CVE-2025-38365
Təsvir
Linux nüvəsində aşağıdakı zəiflik aradan qaldırılmışdır: btrfs: ad dəyişdirmələri və kataloq loqlanması arasındakı yarış vəziyyətinin (race condition) düzəldilməsi Ad dəyişdirmə (rename) ilə kataloq inode-unun loqlanması arasında bir yarış vəziyyəti mövcuddur ki, əgər bu baş verərsə və ad dəyişdirmə tamamlanmamış sistem çöksə və ya enerji kəsilsə, fayl sistemi növbəti dəfə mount edildikdə, loqun təkrar oxunması (log replay) kodu adı dəyişdirilən faylı siləcəkdir. Bu, hadisələrə səbəb olan addımların ardıcıllığının təhlili ilə daha yaxşı izah olunur. İlkin şərtləri nəzərdən keçirək: 1) Biz N tranzaksiyasındayıq; 2) Keçmiş tranzaksiyada (< N) yaradılmış A və B kataloqlarımız var; 3) İki sərt keçidə (hardlink) malik olan fayla uyğun gələn X inode-u var; onlardan biri A kataloqunda ("A/foo_link1"), digəri isə B kataloqundadır ("B/foo_link2"). Hər iki sərt keçid keçmiş tranzaksiyada (< N) saxlanılmışdır; 4) Tək sərt keçidi olan və A kataloqunda yerləşən fayla uyğun gələn Y inode-u var, onu "A/bar" adlandıraq. Bu fayl da keçmiş tranzaksiyada (< N) saxlanılmışdır. Faylin itirilməsinə səbəb olan addımlar aşağıdakılardır və onların hamısı N tranzaksiyası altında baş verir: 1) "A/foo_link1" keçidi silinir, beləliklə, btrfs_unlink() -> btrfs_record_unlink_dir() vasitəsilə X inode-unun last_unlink_trans sahəsi N olaraq yenilənir; 2) Tapşırıq A, Y inode-unu "A/bar"-dan "A/baz"-a adlandırmaq məqsədilə ad dəyişdirmə prosesini başladır, beləliklə biz btrfs_rename() funksiyasına daxil oluruq; 3) Tapşırıq A, btrfs_insert_inode_ref() çağıraraq Y inode-u üçün yeni BTRFS_INODE_REF_KEY daxil edir; 4) Ad dəyişdirmə eyni kataloqda baş verdiyi üçün A kataloqunun inode-unun last_unlink_trans sahəsini cari tranzaksiya ID-si ilə təyin etmirik, yəni btrfs_record_unlink_dir() çağırmırıq; 5) Daha sonra Tapşırıq A, __btrfs_unlink_inode() çağırarkən A kataloqundan girişləri (BTRFS_DIR_ITEM_KEY və BTRFS_DIR_INDEX_KEY elementləri) silir (əslində dir index elementi gecikdirilmiş element kimi əlavə edilir, lakin effekt eynidir); 6) İndi, Tapşırıq A, btrfs_add_link() çağıraraq A kataloquna yeni "A/baz" girişini əlavə etməzdən əvvəl, başqa bir tapşırıq olan Tapşırıq B, X inode-unu loqlayır; 7) Tapşırıq B, X inode-unun fsync prosesini başladır və X inode-unu loqladıqdan sonra, btrfs_log_inode_parent() funksiyasında btrfs_log_all_parents() çağırır, çünki X inode-unun 1-ci addımda təyin edilmiş N dəyərində last_unlink_trans qiyməti var; 8) btrfs_log_all_parents() funksiyasında commit root istifadə edərək X inode-unun bütün valideyn kataloqlarını axtarırıq, beləliklə A və B kataloqlarını tapırıq və onları loqlayırıq. Lakin A kataloqunu loqlayarkən, artıq Y inode-u üçün heç bir dir index elementi yoxdur — nə köhnə "A/bar" adı, nə də yeni "A/baz" adı, çünki ad dəyişdirmə köhnə adı silib, lakin hələ yeni adı daxil etməyib (Tapşırıq A bunu etmək üçün hələ btrfs_add_link() çağırmayıb). Qeyd edin ki, A kataloqunun loqlanması tranzaksiya commit-inə geri dönmür, çünki onun last_unlink_trans dəyəri cari tranzaksiya ID-sindən daha kiçikdir (baxın addım 4); 9) Tapşırıq B, A və B kataloqlarının loqlanmasını bitirir və loq ağacını saxlamaq üçün btrfs_sync_log() çağırdığı btrfs_sync_file() funksiyasına geri qayıdır; 10) Tapşırıq B loq ağacını uğurla saxlayır, btrfs_sync_log() uğurla tamamlanır və enerji kəsintisi baş verir. Y inode-u üçün heç bir kataloq girişi olmayan loq ağacımız var, buna görə də loq replay kodu Y inode-una aid "A/bar" girişini subvolume ağacından silir, çünki o, loq ağacında mövcud deyil və loq ağacı öz indeksi üçün həlledicidir (biz "A/bar"-a uyğun gələn dentry üçün indeks diapazonunu əhatə edən BTRFS_DIR_LOG_INDEX_KEY elementini loqlamışıq). Y inode-u üçün başqa heç bir sərt keçid olmadığı və loq replay kodu "A/bar" adını sildiyi üçün fayl itirilir. Əgər Tapşırıq B loqu yalnız Tapşırıq A, Y inode-u üçün yeni adı ("A/bar") loqda yeniləyəcək btrfs_log_new_name() funksiyasını çağırdıqdan sonra sinxronizasiya etsəydi, bu problem baş verməzdi. Köhnə kataloq girişini silməzdən əvvəl ad dəyişdirmələri zamanı loq root-unu pinləmək və sonda pinləməni ləğv etməklə bu problem həll edilmişdir.
Necə düzəltmək olar
- linux linux_kernel6.1.143, 6.6.96, 6.12.36, 6.15.5 və ya daha yeni versiya
Təsirlənən məhsullar
| Məhsul | Təsirlənən versiyalar | Düzəldildiyi versiya |
|---|---|---|
| linux linux_kernel | 5.18 ≤ v < 6.1.143, 6.2 ≤ v < 6.6.96, 6.7 ≤ v < 6.12.36, 6.13 ≤ v < 6.15.5, 6.16 | 6.1.143, 6.6.96, 6.12.36, 6.15.5 |
| debian debian_linux | 11.0 | — |
Versiyanızı yoxlayın
Məhsulu seçin və istifadə etdiyiniz versiyanı yazın; versiyanın təsirlənən aralıqda olub-olmadığını görün.
Attack Surface Management
Domenlərinizə bağlı aktivləri, onlarda işləyən texnologiyaları və məlum zəiflikləri bir siyahıda görün.