CVE-2025-21932
Təsvir
Linux kernel-də aşağıdakı zəiflik aradan qaldırılmışdır: mm: yaddaş çatışmazlığı səbəbindən birləşdirmə uğursuz olduqda vma_modify() funksiyasını dayandırın vma_modify() funksiyasının qalan hissəsi, birləşdirmə cəhdindən sonra vmg vəziyyətinin dəyişməz qalmasına əsaslanır. Adətən belə olur, lakin birləşdirmə cəhdinin göstərilən diapazonun birləşdirilə bilməz olması səbəbindən deyil, birləşdirməni təsdiqləmək zamanı yaranan yaddaş çatışmazlığı (out of memory) xətası səbəbindən uğursuz olduğu nadir halda bu fərziyyə yanlış olur. Bu, vmg->start və end dəyərlərinin dəyişdirilməsinə gətirib çıxarır və buna görə də VMA-nı bölmək üçün növbəti cəhdlər yanlış start/end dəyərləri ilə həyata keçirilir. Şükür ki, reallıqda buna rast gəlmək praktiki olaraq qeyri-mümkündür, çünki bu, maple tree düyünün öncədən ayrılmasına dair xəta tələb edirdi ki, bu da "uğursuz olmaq üçün çox kiçik" olduğu üçün ehtimal olunmur, yəni kernel uğurlu olana qədər sadəcə resursların geri qaytarılmasını (reclaim) təkrar etməyə davam edərdi. Lakin bu ssenari nəzəri cəhətdən mümkün qalır və burada etdiklərimiz yanlışdır, ona görə də bunu düzəltməliyik. Ən təhlükəsiz variant, bu ssenari baş verdikdə əməliyyatdan sadəcə imtina etməkdir. Əgər birləşdirmək üçün yaddaş ayıra bilmiriksə, onda bölmək üçün də yaddaş ayıra bilmərik (bəlkə daha çox!). Bunun baş verdiyi hər hansı bir ssenari çox kəskin (çox güman ki, kritik) yaddaş təzyiqi altında olardı, buna görə də erkən imtina etmək daha yaxşıdır. Beləliklə, bu ssenaridə əməliyyatı dayandırmağın məqsədəuyğun olduğuna şübhə yoxdur. Lakin ümumiyyətlə, mümkünsə, birləşdirmə cəhdindən sonra VMG vəziyyətinin sabit olduğunu heç vaxt fərz etməməliyik, çünki birləşdirmə əməliyyatları VMG sahələrini yeniləyir. Nəticə etibarilə, start və end dəyərlərini lokal dəyişənlərdə saxlayaraq bunu əlavə olaraq aydınlaşdırırıq. Problem ilk dəfə syzkaller və Brad Spengler (siyahıdankənar müzakirə vasitəsilə) tərəfindən bildirilmişdir və hər iki halda bu, vma_merge_existing_range() funksiyasında aşağıdakı assert-in tetiklənməsi kimi özünü göstərmişdir: VM_WARN_ON_VMG(start >= end, vmg); Görünür, bunun baş verdiyi ən azı bir ssenari, birdən çox VMA üzrə madvise() səbəbiylə birləşdirmənin cəhd edildiyi haldır və bu belə görünür: start end |<------>| |----------|------| | vma | next | |----------|------| madvise_walk_vmas() çağırıldıqda, biz əvvəlcə yuxarıdakı vma-nı tapırıq (vma daxilində ofsetdə olduğumuz üçün prev-in vma-ya bərabər olduğunu müəyyən edirik) və sonra dövrəyə daxil oluruq. Biz madvise() etdiyimiz diapazonun bir hissəsini təşkil edən vma-nın sonunu 'tmp'-ni bu dəyərə təyin etməklə müəyyən edirik: /* Burada vma->vm_start <= start < (end|vma->vm_end) */ tmp = vma->vm_end; Sonra visit() vasitəsilə madvise() əməliyyatını çağırırıq və əməliyyatın bir hissəsi kimi prev-in vma-ya işarə etməsini təmin edirik: /* Burada vma->vm_start <= start < tmp <= (end|vma->vm_end). */ error = visit(vma, &prev, start, tmp, arg); Burada visit() funksiya göstəricisi bu nümunədə madvise_vma_behavior()-dır. syzkaller hesabatlarında müşahidə edildiyi kimi, sonda madvise_update_vma() çağırılır, o isə ardıcıl olaraq vma_modify_flags_name() və vma_modify() funksiyalarını çağırır. Sonra, vma_modify() funksiyasında birləşdirməni cəhd edirik: merged = vma_merge_existing_range(vmg); if (merged) return merged; Biz bunu vmg->start və end dəyərlərini start və tmp olaraq təyin edərək çağırırıq: start tmp |<--->| |----------|------| | vma | next | |----------|------| Özümüzü sağa birləşdirmə ssenarisində tapırıq, lakin ortanı silə bilmədiyimiz halda (biz vma daxilində ofsetdəyik). Burada vmg->start və end dəyərlərinin bəlkə də intuitiv olmayan dəyərlərə təyin edildiyi xüsusi bir halımız var - biz orta VMA-nı kiçiltməyi və növbətini genişləndirməyi planlaşdırmışdıq. Bu o deməkdir ki, vmg->start və end ... vma->vm_start və start olaraq təyin edilir. İndi commit_merge() uğursuz olur və vmg->start, end bu şəkildə qalır. Bu o deməkdir ki, biz vma_modify()-in qalan hissəsinə vmg->start və end (burada start' və end' kimi qeyd olunub) aşağıdakı kimi təyin edilmiş halda qayıdırıq: start' end' |<-->| |----------|------| | vma | next | |----------|------| Beləliklə, biz indi səhvən buna uyğun olaraq bölməyə çalışırıq. Təəssüf ki, budur...
Necə düzəltmək olar
- linux linux_kernel6.12.19, 6.13.7 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 | 6.12 ≤ v < 6.12.19, 6.13 ≤ v < 6.13.7, 6.14 | 6.12.19, 6.13.7 |
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.