Inženýři společnosti Apple oznámili připravenost k testování režimu „-fbounds-safety“ pro kompilátor Clang, který poskytuje záruky bezpečné práce s buffery v kódu napsaném v jazyce C. Režim je součástí forku LLVM, který Apple podporuje pro projekt Swift. V budoucnu je plánováno postupné převedení funkčnosti „-fbounds-safety“ do hlavní kódové základny LLVM/Clang.
Bylo poznamenáno, že navržený mechanismus ochrany se již aktivně používá v produktech Apple, jako je jádro XNU, firmware, knihovny pro práci se zvukem a dekodéry obrazů. Aktivace režimu „-fbounds-safety“ snižuje výkon aplikací v průměru o 5 % (rozpětí od -1 % do 29 %), zvyšuje velikost kódu o 9,1 % (rozpětí od -1,4 % do 38 %) a zpomaluje kompilaci o 11 %.
Použití režimu „-fbounds-safety“ pro automatické odhalování přetečení paměti související s ukazatelem vyžaduje přidání speciálních anotací do kódu a zahrnutí hlavičkového souboru „ptrcheck.h“. Podstatou navrhované metody ochrany je automatické připojení kontrol pro dodržování povolených hranic, které jsou přidávány na základě ručně stanovených anotací nebo známých velikostí kompilátoru.
Na rozdíl od použití v kódu rozšířených ukazatelů (wide pointer), které kromě adresy obsahují také informace o horní a dolní hranici bufferu, použití režimu „-fbounds-safety“ nenarušuje ABI (Application Binary Interface), nemění formát exportovaných ukazatelů a nevyžaduje přepracování celého projektu. V režimu „-fbounds-safety“ se rozšířené ukazatele používají pouze v oblastech, které se nepřekrývají s ABI, zatímco pro ukazatele ovlivňující ABI se používají běžné ukazatele s nahrazením kontrol, které jsou vytvářeny na základě anotací s informacemi o hranicích.
Anotace je třeba připojit k ukazatelům v polích struktur a parametrech funkcí, které odkazují na pole objektů, stejně jako k globálním proměnným s ukazateli. U ukazatelů v místních proměnných není třeba anotace přidávat, protože jsou automaticky zpracovávány jako rozšířené ukazatele, které již obsahují informace o povolených hranicích. Nápovědy o konstrukcích v kódu, pro které je třeba přidat anotace, zobrazí kompilátor při spuštění s příznakem „-fbounds-safety“.
Model ochrany na základě „-fbounds-safety“ lze zavádět postupně, soubor po souboru, aniž by bylo nutné přerušovat vývoj celého projektu. Přidání ochrany do projektu spočívá v uvedení anotací v určitém souboru s kódem, odstranění varování kompilátoru a provedení testování programu, přičemž tyto kroky se opakují pro další soubor. Kód s přidanými anotacemi zůstává kompatibilní s běžným C-kódem a kompilátory, které nepodporují „-fbounds-safety“ (při sestavování jinými kompilátory nebo při sestavování bez přepínače „-fbounds-safety“ prostě nebudou přidány další kontroly hranic).
Během běhu programu, v případě zjištění přístupu mimo povolené hranice, je vygenerována výjimka a program ukončuje své provádění. K havarijnímu ukončení může také dojít při uvedení nesprávných anotací, proto je při použití „-fbounds-safety“ nezbytné provést dodatečné testování programu.
V níže uvedeném příkladu byla do parametru „int *p“ vložena anotace „__counted_by(n)“, která přidává dodatečnou kontrolu na povolené hranice, která funguje během běhu. Pokud se pokusíte zkompilovat kód v režimu „-fbounds-safety“ bez uvedení této anotace, kompilátor zobrazí varování o nedostatku informací o hranicích pole při zpracování 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žimu „-fbounds-safety“ kompilátor sám doplní kontrolu, podobnou kódu „if (i = n) trap();“ }
Pro lokální proměnné s ukazateli se kontroly přidávají automaticky, například: void foo(int i){ char *buf = (char *)malloc(10); // pro ukazatel buf budou zachovány informace o hranicích buf[i] = 0xff; // Bude automaticky přidána kontrola „if (buf + i = buf + 10) trap();“ }
Kde je to možné, kompilátor provádí optimalizaci a vylučuje přidání nadbytečného kódu, pokud již v kódu existují potřebné kontroly. Například: for (size_t i = 0; i < count; ++i) { buf[i] = i; // Kontrola „if (i = count) trap()“ nebude přidána, protože výše již existuje podmínka „i < count“ a i nemůže být menší než 0. }
Hlavní anotace:
- „__counted_by(N)“ — určuje velikost vyrovnávací paměti v prvcích cílového typu.
- „__sized_by(N)“ — určuje velikost vyrovnávací paměti v bytech.
- » __ended_by(P)» — nastavuje horní hranici pro vyrovnávací paměť.
- «__null_terminated» — bere nulový znak jako konec vyrovnávací paměti.
- «__single» — připojuje ukazatel k jednomu objektu. Používá se ve výchozím nastavení pro ukazatele ovlivňující ABI, pokud není explicitně uvedena anotace.
- «__bidi_indexable» — rozšířený ukazatel s informacemi o horní a dolní hranici. Používá se ve výchozím nastavení pro ukazatele, které neovlivňují ABI.
- «__indexable» — rozšířený ukazatel s informacemi o horní hranici.
- «__unsafe_indexable» — ukazatel bez kontroly hranic (pro přenositelnost s nezabezpečeným kódem, například pro získání ukazatelů z externího kódu).
Zdroj: opennet.ru
