Inžinieri spoločnosti Apple oznámili pripravenosť na testovanie režimu „-fbounds-safety“ pre kompilátor Clang, ktorý poskytuje záruky bezpečnej práce s vyrovnávacími pamäťami v kóde v jazyku C. Režim je zahrnutý vo fork-u LLVM, ktorý je podporovaný spoločnosťou Apple pre projekt Swift. Ďalej je naplánované postupné prenesenie funkčnosti „-fbounds-safety“ do základnej kódu LLVM/Clang.
Je zaznamenané, že navrhnutý mechanizmus ochrany sa už aktívne používa v produktoch Apple, ako je jadro XNU, firmvéry, knižnice na prácu so zvukom a dekodéry obrázkov. Zapnutie režimu „-fbounds-safety“ znižuje výkon aplikácií v priemere o 5% (rozpätie od -1% do 29%), zvyšuje veľkosť kódu o 9.1% (rozpätie od -1,4% do 38%) a spomaľuje kompiláciu o 11%.
Používanie režimu „-fbounds-safety“ na automatické zistenie prekročenia hraníc pamäťovej oblasti súvisiacej s ukazovateľom vyžaduje pridanie špeciálnych anotácií do kódu a zahrnutie hlavičkového súboru „ptrcheck.h“. Podstata navrhnutého metódy ochrany spočíva v automatickom pripojení kontrol dodržiavania platných hraníc, ktoré sú pridané na základe manuálne nastavených anotácií alebo známych veľkostí pre kompilátor.
Na rozdiel od používania rozšírených ukazovateľov (wide pointer), v ktorých sú okrem adresy aj informácie o hornej a dolnej hranici vyrovnávacej pamäte, používanie režimu „-fbounds-safety“ nenarušuje ABI (Application Binary Interface), nemení formát exportovaných ukazovateľov a nevyžaduje úpravu pre celý projekt naraz. V režime „-fbounds-safety“ sa rozšírené ukazovatele používajú iba v oblastiach, ktoré sa neprekrývajú s ABI, a pre ukazovatele ovplyvňujúce ABI sa používajú bežné ukazovatele s pridaním kontrol vytvorených na základe anotácií s informáciami o hraniciach.
Anotácie je potrebné pripojiť k ukazovateľom v poliach štruktúr a parametroch funkcií, ktoré odkazujú na pole objektov, ako aj k globálnym premenným s ukazovateľmi. Pre ukazovatele v lokálnych premenných nie je potrebné pridávať anotácie, keďže sa automaticky spracovávajú ako rozšírené ukazovatele, ktoré už obsahujú informácie o platných hraniciach. Nápovedy o konštrukciách v kóde, pre ktoré sa vyžaduje pridanie anotácií, sa zobrazia kompilátorom pri spustení s príznakom „-fbounds-safety“.
Model ochrany na báze „-fbounds-safety“ je možné implementovať postupne, súbor po súbore, bez prerušenia vývoja celého projektu. Pridanie ochrany do projektu spočíva v uvedení anotácií v určenom súbore s kódom, odstránení varovaní kompilátora a vykonaní testovania funkčnosti programu, pričom tieto fázy sa opakujú pre nasledujúci súbor. Kód s pridanými anotáciami zostáva kompatibilný s normálnym C kódom a kompilátormi, ktoré nepodporujú „-fbounds-safety“ (pri kompilácii inými kompilátormi alebo pri kompilácii bez príznaku „-fbounds-safety“ nebudú jednoducho pridané ďalšie kontroly hraníc).
Počas chodu programu, ak sa zistí prístup mimo povolených hraníc, generuje sa výnimka a program sa ukončí. Núdzové ukončenie môže nastať aj pri uvedení nesprávnych anotácií, preto je pri použití „-fbounds-safety“ potrebné vykonávať dodatočné testovanie funkčnosti programu.
V nižšie uvedenom príklade je do parametra „int *p“ vložená anotácia „__counted_by(n)“, ktorá pridáva dodatočnú kontrolu na povolené hranice, ktorá funguje počas vykonávania. Ak sa pokúsite skompilovať kód v režime „-fbounds-safety“ bez uvedenia tejto anotácie, kompilátor vyhodí varovanie o nedostatku informácií o hraniciach poľa pri spracovaní výrazu „p[i] = 0“. #include void init_buf(int *__counted_by(n) p, int n) { for (int i = 0; i < n; ++i) p[i] = 0; // v režime „-fbounds-safety“ kompilátor sám vloží kontrolu, podobnú kódu „if (i = n) trap();“ }
Pre lokálne premenné s ukazovateľmi sa kontroly automaticky pripoja, napríklad: void foo(int i){ char *buf = (char *)malloc(10); // pre ukazovateľ buf budú uložené informácie o hraniciach buf[i] = 0xff; // bude automaticky pridaná kontrola „if (buf + i = buf + 10) trap();“ }
Kde je to možné, kompilátor vykonáva optimalizáciu a vylučuje pridanie nadbytočného kódu, ak už v kóde existujú potrebné kontroly. Napríklad: for (size_t i = 0; i < count; ++i) { buf[i] = i; // kontrola „if (i = count) trap()“ nebude pridaná, pretože vyššie už existuje podmienka „i < count“ a i nemôže byť menšie ako 0. }
Hlavné anotácie:
- „__counted_by(N)“ — určuje veľkosť bufferu v prvkoch cieľového typu.
- „__sized_by(N)“ — určuje veľkosť bufferu v bajtoch.
- » __ended_by(P)» — nastavuje hornú hranicu vyrovnávacej pamäte.
- «__null_terminated» — považuje nulový znak za koniec vyrovnávacej pamäte.
- «__single» — viaže ukazovateľ k jednému objektu. Používa sa predvolene pre ukazovatele, ktoré ovplyvňujú ABI, pokiaľ nie je explicitne nastavená anotácia.
- «__bidi_indexable» — rozšírený ukazovateľ s informáciami o horných a dolných hraniciach. Používa sa predvolene pre ukazovatele, ktoré neovplyvňujú ABI.
- «__indexable» — rozšírený ukazovateľ s informáciami o hornej hranici.
- «__unsafe_indexable» — ukazovateľ bez kontroly hraníc (pre prenositeľnosť s nechráněným kódom, napríklad na získanie ukazovateľov z vonkajšieho kódu).
Zdroj: opennet.ru
