programmatūra · 5 min · 13.10.2026

Rust borrow checker 11 gadus pieņēma nedrošu kodu: Drop metode apiet mūža prasības

Rust kompilators 11 gadus pieņēma programmu, kas pilnīgi drošā kodā rada piekļuvi atmiņai pēc mainīgā dzīves beigām. Kļūdas pieteikums rust-lang/rust#164250 parādījās 13. oktobrī un dažās stundās savāca 602 punktus Hacker News. Minimālais piemērs aizņem apmēram desmit rindas un neizmanto nevienu unsafe bloku.

Rust galvenais solījums ir tieši tas, ko šī kļūda lauž: borrow checker nedrīkst ļaut atsaucei pārdzīvot datus, uz kuriem tā norāda. Tipa apzīmējums 'static parasti nozīmē, ka vērtība dzīvo visu programmas darbības laiku. Šajā piemērā kompilators tādu garantiju apsola, bet nepilda. Atsauce uz lokālu String aizbēg no sava bloka un tiek lasīta pēc tam, kad virkne jau iznīcināta.

Kas notiek kodā

Piemērā ir struktūra Q<T> ar tipa prasību T: 'static un paša rakstītu Drop metodi. Tā metode glabāto vērtību ieliek statiskā sarakstā Vec, kas dzīvo visu programmas laiku. Funkcijā main iekšējā blokā rodas īsdzīvojoša virkne l, tad rinda q.0 = Some(&l) ieraksta atsauci uz to struktūras laukā.

Šī piešķire ir kovarianta, tāpēc kompilators atsauci &l bez 'static ielaiž vietā, kur prasīts 'static. Kovariance nozīmē, ka tipu ar ilgāku mūžu drīkst lietot vietā, kur gaida īsāku. Šeit tas nostrādā otrādi un izlaiž cauri atsauci, kurai mūžs ir par īsu.

Kad bloks beidzas, l tiek iznīcināta, bet struktūras Drop metode nostrādā pēc tam un iebāž jau nederīgo atsauci statiskajā sarakstā. Vēlāks kods to nolasa ar downcast_ref un izdrukā svešu atmiņas saturu. Ar AddressSanitizer programma avarē ar paziņojumu stack-use-after-scope. Godbolt piemērs ar asan to parāda tiešraidē.

Pieteikuma autors Hacker News komentāros atzīmē, ka šis nav kāds eksotisks, grūti sasniedzams gadījums. Minimālais piemērs ir tik vienkāršs, ka to varētu uzrakstīt, pat nenojaušot par problēmu. Bez AddressSanitizer kļūda neuzrādās kā avārija. Programma vienkārši izdrukā datus no atmiņas, kas jau atbrīvota, tātad reālā kodā tā izskatītos pēc dīvainas, grūti atkārtojamas kļūdas.

Tāpēc svarīgs ir tieši Drop. Atmiņas apmaiņa pēc bloka beigām notiek tikai tāpēc, ka iznīcināšanas metode piekļūst atsaucei vēlāk nekā pieļauj mūžs. Bez paša rakstītas Drop metodes kompilators šādu atsauci nepieļautu dzīvot tik ilgi.

Kāpēc 11 gadus neviens nepamanīja

Kļūda ir atkārtojama uz pašreizējā nightly un stabilajām versijām vismaz līdz 1.85.0, bet komentāros to izseko līdz pat 2015. gadam. Tas nozīmē, ka caurums bijis klāt gandrīz tik ilgi, cik pati valoda ir stabila. Rust 1.0 iznāca 2015. gada maijā, tāpēc runa ir par visu stabilās valodas vēsturi. Daudzi komentētāji atzina, ka nesaprot, kā tik vienkāršu gadījumu varēja nepamanīt tik ilgi.

Vaina slēpjas variancē: noteikumos, kas nosaka, kad viena tipa atsauci drīkst lietot cita tipa vietā. Varianci Rust kodā gandrīz nekad neraksta ar roku. Tā izriet no tipu definīcijām automātiski, tāpēc šādas vietas gadiem paliek bez tieša testa pārklājuma. Viens no komentētājiem, kurš agrāk strādājis pie ar varianci saistīta linta, raksta, ka tieši tāpēc kļūdas tur var mierīgi nogulēt veselu desmitgadi. Šoreiz vēl bija vajadzīga arī sakritība ar Drop, kas nostrādā pēc bloka beigām. Divu reti pārbaudītu lietu krustojumā caurums arī palika neredzams.

Kāpēc tas ir P-high, nevis P-critical

Rust Types Team kļūdai piešķīra marķējumu I-unsound un prioritāti P-high. Komandas dalībnieks lcnr pieteikumā skaidro, kāpēc tā nav augstākā pakāpe P-critical.

„Šī kļūda ir P-high un būtu P-critical neatkarīgi no tā, vai kāds to sastapa reālā kodā. Tā ir „tikai” P-high tāpēc, ka tai ir 11 gadu. Kļūda, kas 11 gadus nav atrasta, nevar būt viegli trāpāma,” raksta lcnr.

Viņš piebilst, ka reālu atmiņas kļūmi šis caurums, visticamāk, nav izraisījis nejauši. Lai to izmantotu, vajag apzināti ļaunprātīgu Drop metodi, nevis parastu lietotāja kodu. Tātad vecs projekts, kas kompilējas tīri, visdrīzāk nav klusi bojāts. Praktiski izstrādātājam nav jāskrien pārrakstīt kodu, bet der zināt, ka drošs kods šeit tomēr spēj radīt dangling atsauci.

Ko Rust komanda dara tālāk

Pieteikums norāda uz vairākiem lieliem Types Team darbiem, kas tieši skar šādus caurumus. Pie tiem pieder jaunās paaudzes trait solver un region ierobežojumu pārstrāde. Abi maina to, kā kompilators spriež par tipiem un mūžiem. Tas dod stingrāku pamatu zem borrow checker.

Plašākais mērķis ir aprakstīts komandas 2026. gada ceļvedī ar nosaukumu Project Zero. Tas paredz izlabot visas zināmās tipu sistēmas nedrošības un izveidot pamatu, uz kura jaunu nedrošību risks ir mazāks. Kļūda numur 164250 publicēšanas brīdī joprojām ir atvērta.

Avoti

komentārisaruna

Komentāri

Šim rakstam vēl nav komentāru. Esi pirmais, kurš dalās ar savu viedokli.

Pievieno komentāru

Tavs e-pasts netiks publicēts. Obligātie lauki atzīmēti.

vēl no programmatūrasaistītie