Safe Rust-ийн үнэмлэхүй хамгаалалт ганхав уу? Borrow Checker-ээс 11 жилийн турш нуугдсан ноцтой "цоорхой" илэрлээ
Rust хэлийг хөгжүүлэгчдийн нийгэмлэг хайрлаж, томоохон технологийн аваргууд үйлдлийн систем, дэд бүтцийн түвшинд өргөнөөр нэвтрүүлж буйн ганц гол шалтгаан бий: Хог цэвэрлэгч (Garbage Collector) ашиглахгүйгээр санах ойн бүрэн аюулгүй байдлыг (Memory Safety) хангах.
Rust-ийн үндсэн дүрэм тун энгийн: хэрэв та unsafe блок ашиглаагүй л бол санах ойн Use-After-Free, давхар чөлөөлөлт (double free) эсвэл Data Race-тэй хэзээ ч нүүр тулахгүй. Гэвч сүүлийн хэдэн цагийн дотор rust-lang албан ёсны репозиторт нээгдсэн нэгэн Issue (#164250) уг төмөр мэт дүрмийг сууриар нь доргиож, олон улсын инженерийн нийгэмлэгийг цочирдуулаад байна.
11 жилийн турш нуугдсан "Санах ойн цоорхой"
GitHub дээр ил гарсан уг ноцтой дутагдал (unsoundness bug) нь бүр Rust 1.0 албан ёсны хувилбар гарахаас өмнө буюу бараг 11 жилийн өмнөөс эхлэлтэй компиляторын код дотор чимээгүй оршсоор иржээ.
Хамгийн аюултай нь: 100% цэвэр "Safe Rust" кодоор ямар ч unsafe хаалт ашиглалгүйгээр санах ойн хугацаа (lifetime)-г хуурч, стекээс аль хэдийн чөлөөлөгдсөн утсыг (pointer) статик санах ойд шилжүүлэн үлдээж, улмаар Use-After-Free үүсгэх бүрэн боломжтойг баталсан байна. AddressSanitizer-ээр уг кодыг ажиллуулахад шууд stack-use-after-scope алдаа заан систем сүйрч (crash) байгаа төдийгүй, халдагчийн талаас Program Counter (PC)-ийг удирдан өөрийн кодоо ажиллуулах (arbitrary code execution) POC (Proof-of-Concept) хувилбарууд нийтлэгдэж эхлэв.
Алдаа чухам хаана үүсэв? (Covariance ба Drop-ийн аюултай уулзвар)
Асуудлын үндэс нь хэлний ерөнхий онол биш, харин Rust компилятор (rustc)-ийн ковариант талбарт утга оноох (covariant field assignment) болон объектын амьдралын мөчлөг дуусах үеийн Drop трэйтийн логик шалгалтын зөрчил дээр үүсжээ.
Хялбаршуулсан бүтцийг харвал:
- Хөгжүүлэгч бүтэц дотроо
'staticхугацаа шаардсан өгөгдлийн төрөл тодорхойлно (жишээ ньstruct Q<T: 'static>(Option<T>);). - Тухайн бүтцэд зориулан тусгайлан бичсэн
Dropхэрэгжүүлэлт (custom Drop implementation) дотор санах ойг глобал төлөв рүү (жишээ нь статикMutex<Vec<...>>) хуулдаг логик оруулна. - Улмаар компиляторын төрлийн шалгуурыг хуурч, түр зуурын, амьдрах хугацаа нь дуусаж буй локал хувьсагчийн заагчийг бүтцийн талбарт оноож өгөх боломж олддог.
Энгийн үед Rust-ийн Borrow Checker нь "богино хугацаатай reference урт хугацаатай бүтэц рүү орж болохгүй" гэж алдаа заан татгалзах (reject) ёстой. Гэвч rustc-ийн одоогийн lifetime solver нь тусгайлан бичигдсэн Drop логик болон ковариант талбарын шилжилт хоёрын хоорондох нарийн түвэгтэй уялдааг дутуу үнэлж, кодыг амжилттай компиляци хийчихэж байна. Үр дүнд нь уг хувьсагч стекээс устаж санах ой нь чөлөөлөгдсөний дараа ч статик хадгалагч дотор түүний "үхсэн" заагч амьд үлдэж, түүнийг дуудах үед урьдчилан таамаглах аргагүй санах ойн гажуудал үүсэж байна.
Төрлийн систем нурав уу, компилятор уу?
Энэ мэдээ тармагц дэлхийн өнцөг булан бүрийн хөгжүүлэгчид Hacker News болон Reddit дээр "Rust-ийн аюулгүй байдлын баталгаа үлгэр байсан уу?" хэмээн ширүүн маргалдаж эхэллээ.
Гэвч мэргэжлийн хэлний дизайнерууд болон Rust Core багийн гишүүдийн тайлбарлаж буйгаар сандрах шалтгаан тийм ч их биш юм:
- Энэ бол Rust-ийн төрлийн системийн (type system) зарчмын алдаа биш: Rust-ийн семантик дүрмээр бол уг кодыг Borrow Checker анхнаасаа л татгалзах (reject) ёстой байсан.
- Энэ бол хэрэгжүүлэлтийн (implementation) алдаа: Компилятор өөрийнхөө дүрэм журмыг гүйцэтгэхдээ зарим нэг нарийн тохиолдлыг алгасчихсан гэсэн үг.
Өөрөөр хэлбэл, Rust хэл өөрөө эвдрээгүй, харин компилятор дахь шүүлтүүрийн нэг нүх онгорхой байсныг олж илрүүлсэн тул компиляторын баг уг кодыг "компиляци хийхээс шууд татгалздаг" нөхөөс (patch)-ийг бэлтгэж байна.
Дэд үеийн Borrow Checker: Polonius-ийн шаардлага
Энэхүү олон жилийн настай "цоорхой" нь Rust-ийн одоогийн хуучин NLL (Non-Lexical Lifetimes) шийдвэрлэгчийн түвэгтэй хязгаарлалтуудтай шууд холбоотой. Rust баг сүүлийн жилүүдэд Borrow Checker-ийг математик логик, формал дүрмийн үндсэн дээр шинэчлэн бүтээх Polonius төслийг хөгжүүлж буй бөгөөд саяхан Polonius Alpha хувилбарыг Nightly сувагт туршиж эхэлсэн билээ.
Ийм гэнэтийн ноцтой дутагдлууд илэрч байгаа нь хэлний хөгжүүлэгчдэд Polonius төслийг аль болох түргэн бүрэн тогтворжуулж (stabilize), алдаа мадаггүй математик загварчлалд шилжих хэрэгтэйг сануулсан хүчтэй дохио боллоо.
DEVX дүгнэлт: Та юу анхаарах ёстой вэ?
Энгийн production систем хөгжүүлж буй хөгжүүлэгчдийн хувьд өдөр тутмын кодонд ийм этгээд хэлбэрийн загвар (custom Drop дотор static mutex рүү хуулдаг бүтэц) бичигдэх магадлал маш бага. Тиймээс таны одоо ажиллаж буй сервисийн аюулгүй байдал шууд маргааш нурна гэж айх хэрэггүй.
Гэвч энэ үйл явдал бидэнд хоёр чухал сургамжийг өгч байна:
- Үнэмлэхүй аюулгүй систем гэж үгүй: Хэдийгээр хэлний түвшинд 100% аюулгүй гэж амласан ч компилятор өөрөө хэдэн зуун мянган мөр С/C++ болон Rust кодоос бүтсэн программ тул хүн төрөлхтний гаргасан алдаанаас ангид байж чадахгүй.
- Rust-ийн шинэчлэлтүүдийг анхааралтай дагах:
rustc-ийн дараагийн patch хувилбарууд гармагц өөрсдийн build орчноо яаралтай шинэчлэхэд бэлэн байх нь чухал юм.
Эх сурвалж: rust-lang/rust GitHub Issue #164250 & Hacker News Discussion
Сэтгэгдэл
Ачаалж байна...