Linux kodola galvenajā zarā 2. septembrī nonāca ielāps, kas maina vienu vienīgu rindu failā security/Kconfig.hardening. Pēc tās kodola būvējums pēc noklusējuma vairs neieslēdz randstruct, ja būvēšanas mašīnā ir derīga Rust rīkkopa un arhitektūra Rust atbalsta.
Ielāpu uzrakstīja Mārks Brauns, Acked-by parakstu deva Rust for Linux uzturētājs Migels Ojeda, Kīss Kuks to ievilka savā hardening kokā. Linuss Torvalds izmaiņu pieņēma 2. septembrī plkst. 23:02 pēc UTC ar birku hardening-v7.3-rc2. Commit 2625480a1bf7 statistika ir viena pievienota rinda pret vienu izņemtu.
Viena rinda, kas attiecas tikai uz testa būvējumiem
Vecā rinda skanēja tā: default RANDSTRUCT_FULL if COMPILE_TEST && (GCC_PLUGINS || CC_HAS_RANDSTRUCT). Jaunā priekšā pieliek nosacījumu !(RUST_IS_AVAILABLE && HAVE_RUST).
Nākamā rinda tajā pašā izvēlnē kā bija, tā palikusi: default RANDSTRUCT_NONE. Parastā kodola konfigurācijā randstruct nekad nav bijis ieslēgts pēc noklusējuma. Nomainītais nosacījums nostrādā tikai tad, kad ieslēgts COMPILE_TEST, proti, būvējumos allmodconfig un allyesconfig, ko uzturētāji ar CI sistēmām laiž cauri koda pārklājuma dēļ. Virsraksti par drošības funkciju, ko Linux 7.3 atslēdz lietotājiem, mērķī netrāpa.
Ķibele bija šāda. allmodconfig ieslēdza randstruct, pēc tam CONFIG_RUST nosacījums depends on !RANDSTRUCT izslēdza Rust. Standarta pārklājuma būvējums tātad neaiztika nevienu Rust rindu, lai gan kodolā to jau ir desmitiem tūkstošu.
Kāpēc abas funkcijas nesatiek
randstruct sajauc lauku secību tajās struktūrās, kas sastāv tikai no funkciju rādītājiem vai ir atzīmētas ar __randomize_layout. Sēkla glabājas failā scripts/basic/randomize.seed un pārdzīvo make clean, lai ārējos moduļus varētu būvēt ar to pašu izkārtojumu. Uzbrucējam, kurš mērķē uz šādu struktūru, papildus vajag vēl vienu ievainojamību, kas atklāj konkrētā būvējuma izkārtojumu. Cena: nedaudz lielāks atmiņas patēriņš, mazliet lēnāks kods, sabojāta forensikas rīku darbība, jo Volatility vairs neatrod laukus tur, kur gaidīts.
Rust puses saites uz C struktūrām ģenerē bindgen. Ar GCC spraudni jaukšana notiek kompilatora iekšienē, tāpēc bindgen par to neko nezina un ģenerētais Rust kods lasītu laukus pēc nepareizām nobīdēm. Tieši tāpēc CONFIG_RUST nes divas rindas: depends on !GCC_PLUGIN_RANDSTRUCT un depends on !RANDSTRUCT.
Kconfig izvēlnē ir trīs varianti. RANDSTRUCT_NONE nedara neko. RANDSTRUCT_FULL sajauc laukus, cik vien iespējams. Moduļu gadījumā tas pievelk klāt MODVERSIONS, lai svešs modulis ar citu izkārtojumu neieslīdētu iekšā. RANDSTRUCT_PERFORMANCE jaukšanu ierobežo keša rindas ietvaros un prasa GCC spraudni, tātad ar Clang tas nav pieejams. Vienīgais variants, ko Brauna ielāps skar, ir RANDSTRUCT_FULL testa būvējumos.
Ielāpa aprakstā Brauns paskaidro, kāpēc tik netiešs nosacījums:
Apejam to, izslēdzot randstruct pēc noklusējuma, ja mums ir lietojama Rust rīkkopa un Rust atbalsts arhitektūrai. Cikliskas atkarības neļauj tieši atkarināt no !RUST. Tas nozīmē, ka varam nokļūt konfigurācijā, kurā izslēgts gan Rust, gan randstruct, bet cerams, ka biežāk iznākums būs gaidītais.
Īstais labojums vēstkopā guļ kopš 2024. gada
Ojeda 2024. gada 19. novembrī nosūtīja RFC ielāpu, kas mezglu atraisa Clang gadījumā. Clang struktūru jaukšanu prot pats no 16. versijas, bez GCC spraudņa, bindgen zem pārsega izmanto libclang un tāpēc pārmanto to pašu lauku secību. Ģenerētās saites sanāk pareizas. Kconfig izmaiņa ir tikpat īsa: depends on !RANDSTRUCT vietā depends on !RANDSTRUCT || CC_IS_CLANG.
Šā gada martā Andreass Hindborgs to nomērīja ar Rust null block draivera sēriju uz 120 slodzēm. Vidējais rezultāts ar ieslēgtu randstruct statistiski nozīmīgi nemainījās, atsevišķās konfigurācijās svārstības sasniedza aptuveni 10 % uz abām pusēm. Zem sava testa ziņojuma viņš uzlika Tested-by.
Mainline init/Kconfig šodien joprojām satur depends on !RANDSTRUCT bez Clang izņēmuma. Vienu iemeslu Ojeda nosauca pats: GCC spraudnis lauku secību jauc ar std::mt19937, Clang lieto Boba Dženkinsa algoritmu, turklāt Clang nesola, ka rezultāts paliks tas pats starp kompilatora versijām vai platformām. Ja GCC kādreiz iemācīsies jaukt struktūras bez spraudņa, abi rezultāti visdrīzāk atšķirsies.
Distribūcijas izvēli izdarīja agrāk
Fedora un RHEL kodola konfigurācijā, ko uztur kernel-ark, stāv CONFIG_RANDSTRUCT_NONE=y ar CONFIG_RUST=y. Rust draiveri iekšā, struktūru jaukšana ārā. Tā ir tā pati izvēle, ko ielāps tagad iemūrē arī testa būvējumu noklusējumā, tikai distribūcijas to izdarīja bez Kconfig palīdzības.
Praktiskā ietekme uz cilvēku, kurš pats būvē kodolu, ir neliela. CONFIG_RANDSTRUCT_FULL joprojām var ieslēgt ar roku, tikai tad būvējumā nebūs Rust. Kas mainās, ir tests: no 7.3-rc2 allmodconfig beidzot kompilē arī Rust kodu, tātad kļūdas tajā CI atradīs pirms laidiena, nevis pēc.
Phoronix šo izmaiņu aprakstīja kā drošības funkcijas atslēgšanu pēc noklusējuma. Precīzāk sakot, izvēle starp Rust draiveriem un randstruct kodola konfigurācijā stāv jau kopš 2022. gada. Kamēr Clang izņēmums nav pieņemts, to izšķir viena rinda init/Kconfig failā.
Avoti
- hardening: Default randstruct off with rust for better allmodconfig support
- Phoronix: Linux 7.3 Now Disabling RandStruct Security Feature By Default If Rust Support Present
- LKML: [PATCH v2] rust: allow Clang-native RANDSTRUCT configs
- LKML: Andreasa Hindborga testa rezultāti uz 120 slodzēm
- security/Kconfig.hardening mainline kokā
Komentāri
Šim rakstam vēl nav komentāru. Esi pirmais, kurš dalās ar savu viedokli.