Portál AbcLinuxu, 25. dubna 2024 12:31

Jaderné noviny - 1. 9. 2016: Pohled do hlavy vývojářky Coccinelle

11. 9. 2016 | Redakce
Články - Jaderné noviny - 1. 9. 2016: Pohled do hlavy vývojářky Coccinelle  

Stav vydání jádra. Pohled do hlavy vývojářky Coccinelle.

Stav vydání jádra

Současný vývojový kernel je 4.8-rc4, vydaný 28. srpna. „Vše vypadá normálně, navíc to bylo klidnější než rc3, takže snad už jsme ve fázi, kdy se vše ‚uklidňuje‘. I když vzhledem k obvyklému časovému kolísání (správci rozkládají své žádosti o začlenění různě) je zatím těžké předvídat nějaký trend.“

Pro informace o známých regresích ve vydání 4.8 viz tento report.

Stabilní aktualizace: tento týden žádné nebyly vydány.

Pohled do hlavy vývojářky Coccinelle

Julia Lawallová, vývojářka Coccinelle, začala svou přednášku na Linux Security Summitu v Torontu konstatováním, že toho o bezpečnosti vlastně moc neví. Ale nástroj, který vyvinula, a patche, které tento nástroj generoval, zřejmě v jádře opravily v kernelu mnoho vzorců chyb, z nichž některé měly bezpečnostní důsledky. Lawallová ve svém příspěvku pro zhruba 90 účastníků vysvětlila, co se používáním nástroje naučila, a představila jeho schopnosti.

Principem Coccinelle je „najít jednou, opravit všude“. Jedná se o statický analyzátor, který hledá vzory v kódu v jazyce C. Takže jakmile někdo narazí na problém ve velkém kusu kódu, jde použít Coccinelle k nalezení dalších případů stejné chyby. Navíc je možné aplikovat transformace kódu k vytvoření patchů pro opravu ostatních výskytů chyby.

Sémantické patche

Coccinelle je uživatelsky skriptovatelná pomocí „sémantických patchů“, které jsou založeny na zápisu, který vývojáři znají. V ideálním případě, řekla Julia, se toho lidé k tomu, aby mohli tento nástroj používat, nebudou muset naučit moc. Od počátku bylo cílem, aby nástroj byl přístupný vývojářům v jazyce C.

Lawallová ukázala klasický případ využití Coccinelle. Commit Al Vira („wmi: (!x & y) znovu v akci“) ukazoval na opakující se problém s testy v podobě

if (!block->flags & ACPI_WMI_METHOD)

Správnou opravou je uzávorkování výrazu:

if (!(block->flags & ACPI_WMI_METHOD))

Problém je v tom, že operátor logické negace (!) se váže mnohem těsněji než bitová konjunkce (&), což vede k chybnému testu. Jak upozorňuje komentář, je jasné, že se tento problém objevuje s určitou frekvencí, ale použít grep k jeho nalezení je obtížné. V jádře se nachází příliš mnoho symbolů ! a & a test se může protáhnout na několik řádek, což může znesnadnit vyhledávání.

Jednoduchý sémantický patch Coccinelle ale najde (a opraví pomocí generovaného patche) problémy tohoto druhu:

@@
expression E;
constant C;
@@
- !E & C
+ !(E & C)

Lawallová poté ukázala příklad stejného problému (přes dva řádky) jinde v jádře a změny kódu vedoucí k opravě této chyby.

Historie

Coccinelle začala vznikat v roce 2004, kdy měla Lawallová volno. Bylo to zrovna v době vydání jádra 2.6, ale většina ovladačů tou dobou stále cílila na jádro 2.4. Lawallová chtěla automatizovat port těchto ovladačů na 2.6, ale ukázalo se, že „je to nereálné.“ Nicméně některé změny, které nazvala „vedlejší evoluce“, mohly být provedeny automaticky, jako například změny volacích funkcí, jako je přidávání nových parametrů.

K vyřešení problému vedlejší evoluce vznikl nástroj Coccinelle, angažovala se v tom čtveřice, v níž figurovala Lawallová, na univerzitě v Kodani mezi roky 2005 a 2007. První patche založené na výstupu Coccinelle byly k začlenění do jádra podány v roce 2007. Týkaly se míst, ve kterých bylo volání kmalloc() následováno memset(), a nahrazovaly tato volání jedním voláním kzalloc(). Následovala publikace dvou článků v letech 2008 a 2011.

V současné době se na vývoji Coccinelle podílí Lawallová společně s dalšími třemi lidmi z Inrie. „Nástroj je kompletně implementován v OCamlu, což může podstatně zmenšit základnu přispěvatelů,“ pokračovala za smíchu části publika. Na druhou stranu to redukuje počet patchů, které je potřeba zkontrolovat.

Nástroj měl na jádro docela velký dopad od roku 2007, kdy byly zveřejněny první patche. Aktuálně Coccinelle zmiňuje přes 4500 patchů v jádře, z toho 3000 pochází od více než 500 vývojářů mimo projekt Coccinelle samotný. V jádře je také 56 sémantických patchů, které se používají jako testy (dostupné skrze make coccicheck). Více sémantických patchů týkajících se Linuxu je možné najít na coccinellery.org.

Sémantické patche jsou v jádře určeny k zachycování různých typů chyb, které mohou proniknout na povrch. Existují testy pro obecné chyby v C (např. testování nezáporných číselných proměnných na hodnoty menší než nula, popř. dereference ukazatele nikam), obecné linuxové problémy (dvojité zámky nebo použití indexu, přes který se iteruje, mimo cyklus), chyby závislé na API (použití free() na oblasti alokované devm), modernizace API (použití kmemdup() namísto kmaloc() a memcpy()). Příspěvky nových sémantických patchů jsou vítány. Přispěvatelé mohou patch vyrobit sami nebo týmu ukázat vzor, na který má patch cílit.

Použití Coccinelle

Poté Lawallová ukázala některá složitější použití Coccinelle v jádře. Jako první to byla snaha převést ovladače k používání frameworku devm ke správě paměti, aby se zabránilo únikům paměti. Při zavedení (probe) ovladače se často alokuje paměť pomocí kzalloc() a potom se při odebrání (remove) uvolní. Přechod k devm_kzalloc() znamená, že paměť bude nově řídit knihovna devm.

Proces tvoří tři kroky. V prvé řadě je to nalezení názvů funkcí probe a remove. K tomu by šlo použít regulární výraz, což se však „jeví neatraktivní," řekla Lawallová. Ukazatele na funkce se vloží do struktury platform_driver atp., takže jejich jména jde extrahovat pomocí pravidel Coccinelle. Druhým krokem je nalezení definice funkce probe a transformace volání kzalloc() na devm_kzalloc(), přičemž volání kfree() jsou odstraněna ze všech větví, kde se ošetřují chyby. Posledním krokem je odstranění volání kfree() z funkce remove.

Lawallová na tomto sémantickém patchi pracovala v roce 2012 a nakonec podala 39 patchů, které vedly k provedení kýžených změn. Její sémantický patch nabízí více než 170 příležitostí k provedení této změny, ale není možné je aplikovat slepě. Je třeba se odpovědně dívat na generované patche, dodává. Celý proces trvá jen 30 sekund (za použití indexování GLIMPSE), takže problém hledání vzorců převádí na problém kontroly oprav.

Jiné použití Coccinelle je detekce volání na uživatelské úrovni, u kterých by mohlo dojít k blokování kvůli výpadku stránky (např. copy_*_user(), get_user()), pokud byla volána z oblasti chráněné zámkem. Blokování není v oblastech chráněných zámky povoleno, takže tato volání by se z nich neměla provádět. Patch jednoduše hlásil nálezy potenciálně inkriminovaných funkcí mezi spin_lock() (nebo variantami) a spin_unlock(). Běžně zpracovává Coccinelle funkce po jedné, ale při hledání vzorů ve funkcích nebo v souborech se dá použít i iterativně.

Jedna potvrzená chyba byla nalezena v copy_from_user(). Kontrola copy_to_user() našla problém, ale byl u něj komentář „FIXME“, což by mohlo naznačovat, že se k ní vývojář plánoval někdy v budoucnu vrátit. Když se tomu obecenstvo na přednášce zasmálo, řekla Lawallová: „Chápu vaši nedůvěru.“ Tato kontrola našla i falešně pozitivní nález. Poznamenala, že se sémantickými patchi nehodlá dosáhnout dokonalosti, jen se snaží najít něco zajímavého, takže nemá zájem komplikovat sémantické patche do té míry, aby eliminovala falešně pozitivní nálezy. Kontrola get_user() a put_user() našla od každého jeden případ, ale ještě nebyl čas je vyhodnotit.

Další práce spočívala v sémantickém patchi pro přidávání klíčového slova const k některým jaderným strukturám. Lawallová použila postup o čtyřech krocích, z nichž dva jsou manuální. Prvním z nich je identifikace struktur, které obsahují pouze ukazatele na funkce, pomocí Coccinelle. To jsou vhodní, ale ne jediní kandidáti na přidání const. Kromě toho je ochrana těchto struktur před přepsáním důležitá z toho důvodu, aby se zabránilo spuštění kódu v jádře. Ze seznamu takových struktur se některá vybere ručně a pomocí Coccinelle se pak ke každému jejímu použití přidá const. Posledním krokem je sestavit jádro pomocí GCC a ujistit se, že všechno funguje.

Tento proces vyústil v 115 patchů předložených v roce 2015. Lawallové nejde o to, zda dojde k jejich začlenění. Co si pamatuje, tak žádný z nich nebyl zamítnut, ale možná některé nebyly naopak přijaty. Detekce struktur se samými ukazateli na funkce je pomalá – spouští je přes noc – ale přidání const je okamžité.

Ponaučení

Lawallová se za tu dobu naučila některé věci, o které se chtěla podělit. Nejdůležitější je začít s něčím jednoduchým. Sémantické patche v jaderném stromu jsou komplexnější a ne zrovna reprezentativní – nejsou dobré pro první kroky s tímto nástrojem. Začněte s běžným případem, který může skončit falešně pozitivními chybami. Možná dostanete 100 výsledků, které povedou k nalezení dvou chyb. Samozřejmě je možné mít složitější systém, ale je to něco za něco.

Během procesu přechodu na devm bylo například těžké přijít na to, jak se ve funkci remove zbavit toho správného kfree(). Zvolila jednoduché řešení. Prostě předpokládala, že jaderní vývojáři by pro uvolňovanou proměnnou použili stejné jméno, jako pro tu, která se nachází ve funkci probe. Je to „zcela nebezpečné, ale funguje to docela dobře.“ Nejhorší případ by byla falešná negativa – neodstranění kfree(), které by mělo být odstraněno. Takže udělala test, ve kterém použila Coccinelle k odstranění všech volání kfree() ve funkci remove. Ukázalo se, že se jedná o stejnou sadu souborů jako v jejím pravidle, takže „pravděpodobně je pravidlo OK“ a „přímočará metoda byla dostatečně dobrá.“

Sémantické patche by se měly tvořit postupně. Pravidla pro přechod na devm vyústila v 171 souborů, jejichž analýza nějaký čas zabere. Kromě toho devm uvolňuje paměť, kterou spravuje v pořadí LIFO (last in first out), což může způsobit rozdíly. Takže Lawallová přepsala svůj sémantický patch, aby se předešlo jakýmkoli voláním funkcí, které vracejí hodnotu, před kzalloc() ve funkci probe nebo libovolným funkcím remove, kde se cokoliv volá po kfree(). To zmenšilo vzorek na 51 souborů.

Podobně kontrola na blokující funkce v oblasti chráněné zámky produkovala spoustu falešně pozitivních nálezů. Podmínky v kódu mohou způsobit, že status zámku bude při analýze zaznamenán nepřesně. Aby se tomu vyhnula, změnila test, aby vyhledával v místech, kde se blokující volání objevují po uvolnění zámku, a potom se podrobněji podívala na místa, kde se to nedělo.

Poslední rada, kterou předala, se týkala využití informací z jiných nástrojů. Proces přidávání const využíval GCC ke zjištění, zda se po změně dá jádro sestavit. Ale v ideálním případě by existoval způsob, jak uživatele varovat o potenciálně problematických případech. Za tímto účelem možná budete chtít získat informace o strukturách (jsou už některé z nich const, což by mohlo naznačit, že když budou všechny const, mohlo by to fungovat?) a o sestavení jádra s ohledem na příslušnou strukturu (jsou všechny instance struktur v souborech, které jsou obsaženy v sestavení ve stávající konfiguraci?). Tak by mohly být vybrány struktury, které by bylo snazší, nebo naopak složitější změnit a ověřit, aby bylo jasné, čím se zabývat dřív. Tento typ informací se dá shromáždit pomocí programů v Pythonu nebo OCamlu, což může uživatelům pomoci zaměřit se na vhodné kandidáty.

Na závěr Lawallová prohlásila, že tento nástroj je vhodný pro celou řadu různých problémů. Coccinelle je „kompromis mezi správností a úplností, ale v praxi je to užitečný nástroj.“ Vývojáři by udělali jedině dobře, pokud jej přidají do svého arzenálu.

Odkazy a zdroje

LWN.net

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

Jaderné noviny – přehled za březen 2024
Jaderné noviny – přehled za únor 2024
Jaderné noviny – přehled za leden 2024
Jaderné noviny – přehled za prosinec 2023
Jaderné noviny – přehled za listopad 2023

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