Portál AbcLinuxu, 12. červenec 2020 15:06

Jaderné noviny – 26. 5. 2011: Co bude v Linuxu 3.0

6. 6. 2011 | Jirka Bourek
Články - Jaderné noviny – 26. 5. 2011: Co bude v Linuxu 3.0  

Aktuální verze jádra: 2.6.39. Citáty týdne: Rusty Russel, Valerie Aurora, Alan Cox a Matthew Garrett. Seccomp filtry: přístup odmítnut. Co přichází v $PŘÍŠTÍ_VERZE_JÁDRA, část první. Znáhodňování jaderných adres. Problém s přednačítáním.

Obsah

Aktuální verze jádra: 2.6.39

link

Jádro 2.6.39 je venku; přesně podle předpovědi bylo vydáno okamžitě po vydání Jaderných novin z minulého týdne (originálu, pozn. překl.) Linus naznačil trochu nejistoty ohledně toho, jestli by bylo vhodné ještě jedno -rc:

Nicméně protože za dva týdny budu na LinuxCon Japan, otázka byla, jestli to mám prostě vydat, nebo čekat tři další týdny, nebo mít hodně zmatené začleňovací okno, ve kterém bude přestávka.

Mezi význačné vlastnosti této verze patří IPset, subsystém pro řízení multimédií, pár nových plánovačů síťového toku, přepracování zátkování blokových zařízení, dlouho očekávané odstranění velkého jaderného zámku [Big Kernel Lock] a další. Více informací o této verzi vizte na stránce o 2.6.39 na KernelNewbies, ve shrnutí na LWN (1. část, 2. část a 3. část) a ve shrnutí od Thorstena Leemhuise na The H.

Stabilní aktualizace: 21. května vyšlo 2.6.38.7, následováno jádry 2.6.32.41 a 2.6.33.14 23. května. Každé obsahuje obvyklou řádku důležitých oprav.

Citáty týdne: Rusty Russel, Valerie Aurora, Alan Cox a Matthew Garrett

link

Před nějakým časem mě zděsilo chování jednoho hackera, kterého jsem hluboce respektoval, v soukromí: ošklivé a pokrytecké záležitosti. A měl jsem z toho vnitřní krizi: myslel jsem si, že se všichni snažíme udělat ze světa lepší místo. Nakonec jsem přišel s tím, že:

  1. Když je někdo skvělý hacker, neimplikuje to pro něj žádné morální nebo etické vlastnosti.
  2. Když je někdo skvělý kodér, neznamená to, že není magor.
  3. Když někdo pracuje na skvělém projektu, neznamená to, že pro to má stejnou motivaci jako já.

Pro mě to nebylo zjevné a zdá se, že pro jiné také ne.

-- Rusty Russel

Čím déle se dívám na argumenty pro to, proč pro vytvoření dobrého kódu musí být člověk hajzl, tím víc mám pocit, že se tu činí nějaká forma Stockholmského syndromu.

-- Valerie Aurora

Když máme takhle velkou změnu verze, mohli bychom zahodit většinu ze sběrnic MCU, EISA a ISA? Pořádné pročištění ovladačů má už teď zpoždění a kdyby někdo chtěl o půlnoci přivolávat z mrtvých nějaké plíživé stvůry, všechno to bude v gitu.

-- Alan Cox

UEFI je zkratka pro „Unified Extensible Firmware Interface“, kde „Firmware“ je staré africké slovo znamenající „Proč něco udělat dobře, když to můžete udělat tak blbě, že děti budou plakat a dospělí se třást“ a „UEI“ je keltsky „Propásli jsme DOS a tak jsme vám ho vypálili do ROM“

-- Matthew Garrett

Seccomp filtry: přístup odmítnut

link

napsal Jonathan Corbet, 25. května 2011

Článek z minulého týdne týkající se nápadu rozšířit „bezpečné zpracování“ jeho integrací s mechanismem perf/ftrace zmínil nepřekvapující fakt, že vývojáři existujícího mechanismu bezpečnostních modulů nebyli zcela nadšeni z toho, že by se měl objevit nový a zcela odlišný bezpečnostní framework. Od té doby diskuze o patchi pokračovala a opozice se objevila z úplně jiné strany: od vývojářů sledování a osazení sledovacími nástroji.

Peter Zijlstra začal novou diskuzi krátkou poznámkou: Silně oponuji tomu, aby se jádro perfu mixovalo s nějakým bezpečnostním voodoo (nebo mělo jinou aktivní roli, když jsme u toho.) Thomas Gleixner skočil do diskuze s detailnějším popisem námitek – podle jeho pohledu by přidání záležitostí spojených s bezpečnostní do sledovacího systému přidalo režii navíc, ztížilo v budoucnu změny a obecně by to míchalo úlohy, které se míchat nemají. Podle něj by bylo lepší ponechat seccomp jako samostatnou vlastnost, která bude filtrovací mechanismus sdílet až tehdy, když se vytvoří vhodná sada interních API.

Ingo Molnára, velkého podporovatele patche, to neodradilo; věří, že silněji integrované mechanismy vytvoří mocnější a užitečnější nástroj. Vzhledem k tomu, že bezpečnostní rozhodnutí se někde musí udělat tak jako tak, rád by viděl, kdyby se k tomu v nejvyšší možné míře využívala existující infrastruktura. Tento argument ale zjevně nepomůže; Peter odpověděl:

Ale přiznej si to, můžeš se tady hádat, až zmodráš, ale tglx i já dáme NAK každému a všem patchům, které by rozšířily perf/ftrace kamkoliv za pasivní roli pozorovatele.

Tak si věci stojí v době psaní tohoto článku. Rozšířený mechanismus bezpečného zpracování – který ve své původní podobě perf nepoužíval – prošvihne začleňovací okno a nemá žádnou jasnou cestu do hlavní řady. Vzhledem k tomu, že Linusovi se původní myšlenka také nelíbila, není jasné, jestli má vůbec nějakou budoucnost.

Co přichází v $PŘÍŠTÍ_VERZE_JÁDRA, část první

link

napsal Jonathan Corbet, 25. května 2011

V době psaní tohoto článku bylo do budoucí verze jádra přetaženo 5400 neslučovacích sad změn. Zpočátku se zdálo, že tento vývojový cyklus nebude mít velký počet vzrušujících nových vlastností, ale nějaké zajímavé přírůstky tu jsou. Mezi změnami viditelnými pro uživatele je následující:

Mezi změny viditelné pro vývojáře jádra patří:

Začleňovací okno pro tento vývojový cyklus pravděpodobně skončí 29. května těsně předtím, než Linus nastoupí do letadla do Japonska. Tou dobou se pravděpodobně dozvíme i to, jak se bude příští verze jmenovat; Linus dal jasně najevo, že si myslí, že čísla v 2.6.x jsou příliš vysoká a že je čas na změnu. Příští týden zde najdete závěr začleňovacího okna a konec napětí o číslu verze jádra.

Znáhodňování jaderných adres

link

napsal Jonathan Corbet, 24. května 2011

V minulých jaderných novinách se objevil krátký článek o skrývání jaderných adres před uživatelským prostorem. Toto skrývání se dostalo pod palbu od mnoha vývojářů, kteří říkají, že to rozbíjí programy (například perf), a přitom to neposkytuje žádné skutečné zabezpečení navíc. Nezdá se, že by tu byl konsenzus o tom, jestli je lepší, když útočníci neví, kde jádro uchovává své datové struktury. Ukazuje se, že zde může být ještě lepší způsob, než hodnoty ukazatelů prostě skrýt.

Není pochyb o tom, že mít přístup k rozvržení jaderné paměti je pro útočníky užitečné. Jak to řekl Dan Rosenberg:

Souhlasím s faktem, že kptr_restrict není kompletní bezpečnostní vlastnost. Nesouhlasím ale s tím, že by to bylo úplně zbytečné. V podstatě každý zveřejněný exploit jádra z minulých let využívá k zacílení útoku /proc/kallsyms nebo jiný únik adresy do uživatelského prostoru.

Skrývání jaderných adres má útočníka o tyto informace navíc připravit, což mu ztíží práci. Jedním velkým problémem tohoto přístupu je to, že většina systémů používá distribuční jádra. Získat potřebné informace o adresách z balíčku s jádrem od distributora není nijak výrazně náročné. Na takových systémech tedy rozvržení paměti jádra není žádným tajemstvím bez ohledu na to, jestli se hodnotám ukazatelů umožní prosáknout do uživatelského prostoru.

Když se tohle všechno diskutovalo, objevil se další nápad: proč při bootu nerozvrhnout jadernou paměť náhodně? Náhodné rozvržení adresového prostoru se používá pro zvýšení odolnosti proti útokům již dlouho, ale ne pro jádro. Vzhledem k tomu, že obraz jádra lze přestěhovat, není důvod, proč ho vždycky nahrát na stejné místo. Pokud pro sebe jádro vypočítá při každém bootu jiný offset, může tento offset odečíst od hodnot ukazatelů předtím, než je předá uživatelskému prostoru. Tyto ukazatele by pak mohly používat nástroje jako perf, ale už by nebyly nijak užitečné pro toho, kdo se pokouší přepsat jaderné datové struktury.

Dan znáhodnění adresového prostoru jádra zkoušel s určitým úspěchem; ukazuje se, že jednoduše přestěhovat jádro není tak těžké. Narazil přitom ale na pár potenciálních problémů. Prvním z nich je, že při bootování není k dispozici příliš mnoho entropie, takže vygenerovat dostatečně náhodnou základní adresu není tak jednoduché. Zdá se, že z real-time hodin a čítače časových značek [time stamp counter] lze získat dost bitů entropie, aby to útočníkovi později zabránilo jednoduše uhodnout základní adresu, ale skutečně náhodné číslo by bylo lepší.

Poté, na což upozornil Linus, jádro nelze stěhovat neomezeně. Pro umístění jádra je mnoho omezení kvůli zarovnání, takže podle Linuse je k dispozici maximálně 8-12 bitů náhodnosti. To znamená, že exploit by mohl najít správný offset maximálně po několika tisících pokusech. Vzhledem k tomu, že počítače umí dělat věci rychle, to správci systému nedává mnoho času zareagovat.

Jak ale upozornili ostatní, toto je pravděpodobně dostatečně náhodné. Neúspěšný pokus o zneužití chyby s velkou pravděpodobností způsobí oops; i když si ho správce nevšimne hned, časem by měl a ztrácí se tak možnost tiše převzít kontrolu nad strojem. Krom toho nepovedené pokusy mohou snadno systém zcela shodit (obzvláště když je nastaven příznak „panic on oops“, což platí pro mnoho RHEL systémů) nebo alespoň zanechat systém ve stavu, kde další pokusy zneužít chybu nebudou moci fungovat. Podle všeho je hodně užitečné donutit útočníka hádat.

Tato výhoda se nicméně ztrácí, když útočník nějak přijde na offset, který systém při bootu použil. Dan si všiml jednoho způsobu, jak to lze udělat: neprivilegovaná instrukce SIDT může najít tabulku popisovačů přerušení v systému. Z této pozice by se následně vypočítal offset. Tento problém lze vyřešit dynamickou alokací tabulky při bootu, ale za cenu hrabání se ve velmi záludném kódu pro počáteční fáze bootu. Dynamická alokace by nicméně měla i další výhody; pokud by se vložila do oblastí specifických pro jednotlivá CPU, systém by byl o něco málo škálovatelnější.

Problém lze tedy určitě vyřešit, ale není pochyb o tom, že budou i jiná místa, odkud bude útočník moci získat skutečné adresy v adresovém prostoru jádra. Jednoduše je příliš mnoho způsobů, jakým tato informace může do uživatelského prostoru prosáknout. Ucpat všechny tyto díry vypadá jako jeden z těch dlouhotrvajících úkolů, který nikdo nikdy neudělá. Mělo by nicméně být možné zacpat jich dost na to, aby útočníci nemohli počítat s tím, že vždycky přijdou na skutečné umístění jádra na běžícím systému. Je to trocha zabezpečení zatajováním, kterou stojí za to mít.

Problém s přednačítáním

link

napsal Jonathan Corbet, 24. května 2011

Postupem času většina vývojářů software zjistí, že snaha o mikrooptimalizace většinou nestojí za to, obzvlášť když chybí pevná data ukazující na specifický problém. Problémy s výkonností často nejsou tam, kde si myslíme, že jsou, špatně mířené pokusy věci vyladit k rychlejšímu běhu tedy mohou být naprosto neefektivní. Nebo mohou dokonce stav zhoršit. To je lekce, které se právě dostalo jaderným vývojářům.

Na úrovni jádra výkonnost často závisí na chování cache. Odkazování na paměť, které je potřeba vyřešit skutečným přístupem do paměti, je extrémně pomalé; dobrá výkonnost závisí na tom, že potřebná data budou po většinu času v cache CPU. Jádro se hodně snaží používat čerstvě nacachovanou paměť, když je to možné; spousta práce byla také věnována reorganizaci datových struktur tak, aby se pole, ke kterým se často přistupuje zároveň, nacházela ve stejné řádce v cache. Obecným pravidlem je, že tyto optimalizace výkonnost měřitelně zlepšily.

Práci s daty, která v cache nejsou, se často vyhnout nelze, ale někdy je možné pokusit se omezit její cenu. Pokud jádro ví, že bude v blízké budoucnosti přistupovat k paměti na konkrétním místě, může použít pro CPU specifickou instrukci pro přednačtení, která zahájí proces kopírování dat do cache. Tato instrukce je jadernému kódu k dispozici v podobě obecné funkce prefetch(); vývojáři ji používají výrazně. Vezměme například toto běžně používané makro z <linux/list.h>:

#define list_for_each(pos, head) \
    for (pos = (head)->next; prefetch(pos->next), pos != (head); \
        pos = pos->next)

Toto makro (a jeho varianty) se používá k procházení spojového seznamu. prefetch() se volá, aby načítání dalšího záznamu začalo už v době, kdy se zpracovává aktuální záznam. Při troše štěstí budou data v době, kdy začne další opakování smyčky, již načtená – přinejmenším budou alespoň na cestě. Spojové seznamy jsou známy tím, že jsou to pro cache nepřívětivé datové struktury, takže dává smysl, že taková optimalizace může pomoci věci zrychlit.

Až na to, že to není pravda – přinejmenším ne na x86.

Andi Kleen byl pravděpodobně první, kdo tuto optimalizaci zpochybnil, když se pokusil z operací se seznamy přednačítání odstranit loni v září. Jeho patch ale vygeneroval nějakou diskuzi a nakonec zmizel. Linus nedávno zkoušel nějaké profilování na jedné ze svých oblíbených pracovních zátěží (překlad jádra) a zjistil, že instrukce přednačítání jsou na vrcholu seznamu. Přednačítání stálo čas a tento čas nebyl splacen lepším chováním cache; jednoduché odstranění volání prefetch() věci zrychlilo.

Ingo Molnár, protože je Ingo, skočil do diskuze a provedl během hodiny týdenní výzkum. S použitím perfu a lehce poupraveného jádra byl schopen ověřit, že používání instrukcí přednačítání způsobilo ztrátu výkonnosti o přibližně 0.5 %. To rozhodně není výkonnostní regrese, která by si vyžádala první stránku novin a palcové titulky, ale rozhodně je tu prostor pro optimalizaci, která by věci mohla urychlit. Něco tu zjevně nefunguje tak, jak si lidé mysleli.

Linus na jeden problém upozornil hned zezačátku: jeho test obsahoval spoustu průchodů jednosměrně spojených hlist seznamů hashových tabulek. Tyto seznamy bývají krátké, takže tu není moc prostoru pro přednačítání; ve skutečnosti se většinou prefetch pokoušel použít nulový ukazatel, který znamená konec seznamu. Přednačítání nulového ukazatele vypadá hloupě, ale také je to nákladné: každá taková operace na x86 (a podle všeho i na ARM) narazí na chybějící hodnotu v TLB a způsobí zastavení pipeline [pipeline stall]. Ingo tento efekt změřil a dospěl k závěru, že každé přednačtení null stojí přibližně 20 cyklů procesoru.

Přednačítání nuly je zjevně špatný nápad. Bylo by pěkné, kdyby CPU jednoduše ignorovalo pokusy přednačíst nulový ukazatel, ale tak věci nestojí a – jak je to běžné – je potřeba to upravit v software. Ingo testoval s verzí prefetch(), která by instrukci přednačtení zadávala pouze pro nenulové ukazatele; tato verze se opravdu chovala lépe, ale stále měřitelně hůře, než když se přednačítání jednoduše vynechalo.

Návrháři CPU si jsou velmi dobře vědomi ceny za čekání na paměť; spoustu snahy věnovali tomu, aby se tato cena minimalizovala všude, kde je to možné. Mezi jinými věcmi tedy současná CPU mají svoji vlastní jednotku pro přednačítání paměti. Ta se pokouší předpovědět, která pozice v paměti bude příště zapotřebí, aby se její načtení zahájilo dříve. Jedna z věcí, které si Ingo během svých testů všiml, byla, že i bez operace přednačtení zadaných softwarově byl počet operací přednačtení na CPU přibližně stejný. Hardwarový přednačítač se tedy v daném čase činil – a co se týče rozhodnutí o tom, co načíst, fungoval lépe než software. Explicitní operace pro přednačtení podle všeho akorát rušily to, co se pokoušel dělat hardware.

Ingo své výsledky shrnul takto:

Závěrem tedy je: přednačítání je naprosto toxické, i když se vynechají NULL.

Okamžitým výsledkem je to, že pro 2.6.40 (ať už se bude jmenovat jakkoliv) byla volání prefetch() odstraněna z procházení spojových seznamů, hlist a seznamů sk_buff – přesně to, o co se pokoušel Andi Kleen v září. Je dost možné, že budou odstraněny i další operace přednačítání. prefetch() bude mít v jádře nadále své místo, ale jenom ve specifických situacích, kde bude možné zjevně dokázat, že to pomáhá výkonnosti. Stejně jako u dalších nízkoúrovňových optimalizací (na mysl přichází likely()) vhodit do kódu prefetch(), protože to vypadá jako dobrý nápad, není často správně.

Z této zkušenosti vychází ještě jedna lekce, a sice, že na číslech záleží. Andi měl pravdu, když se pokoušel tyto operace odstranit, ale neuspěl ve snaze patch začlenit. Proč věci nyní dopadly jinak má asi více důvodů, prvním z nich je fakt, že tentokrát o danou záležitost projevil zájem Linus. Je ale také pravda, že patche orientované na výkonnost musí být doprovázeny čísly, které ukazují, že se opravdu dosáhne požadovaného efektu; kdyby si Andi našel čas kvantifikovat dopad svých změn, měl by silnější pozici pro jejich začlenění.

Odkazy a zdroje

Kernel coverage at LWN.net: May 26, 2011

Další články z této rubriky

Jaderné noviny – přehled za červen 2020
Jaderné noviny – přehled za květen 2020
Jaderné noviny – přehled za duben 2020
Jaderné noviny – přehled za březen 2020
Jaderné noviny – přehled za únor 2020

ISSN 1214-1267, (c) 1999-2007 Stickfish s.r.o.