Na bazie Clang dla języka C wprowadzono tryb sprawdzania granic buforów

Inżynierowie z firmy Apple ogłosili gotowość do testowania trybu „-fbounds-safety” dla kompilatora Clang, który zapewnia gwarancje bezpiecznego korzystania z buforów w kodzie napisanym w języku C. Tryb jest częścią forka LLVM, wspieranego przez firmę Apple dla projektu Swift. W przyszłości planowane jest stopniowe przenoszenie funkcjonalności „-fbounds-safety” do głównej bazy kodu LLVM/Clang.

Zauważono, że zaproponowany mechanizm ochrony jest już aktywnie stosowany w produktach Apple, takich jak jądro XNU, oprogramowanie układowe, biblioteki do obsługi dźwięku oraz dekodery obrazów. Włączenie trybu „-fbounds-safety” obniża wydajność aplikacji średnio o 5% (rozrzut od -1% do 29%), zwiększa rozmiar kodu o 9.1% (rozrzut od -1.4% do 38%) oraz spowalnia kompilację o 11%.

Użycie trybu „-fbounds-safety” do automatycznego wykrywania przekroczeń granic obszaru pamięci związanej z wskaźnikiem wymaga dodania do kodu specjalnych adnotacji i włączenia pliku nagłówkowego „ptrcheck.h”. Istotą zaproponowanej metody ochrony jest automatyczne dołączanie kontroli przestrzegania dozwolonych granic, dodawanych na podstawie ręcznie ustawionych adnotacji lub znanych przez kompilator rozmiarów.

W przeciwieństwie do używania w kodzie rozszerzonych wskaźników (wide pointer), które oprócz adresu zawierają informacje o górnej i dolnej granicy bufora, użycie trybu „-fbounds-safety” nie narusza ABI (Application Binary Interface), nie zmienia formatu eksportowanych wskaźników i nie wymaga przerabiania całego projektu naraz. W trybie „-fbounds-safety” rozszerzone wskaźniki stosowane są tylko w obszarach, które nie kolidują z ABI, a dla wskaźników mających wpływ na ABI stosowane są zwykłe wskaźniki z dołączonymi kontrolami, formowanymi na podstawie adnotacji zawierających informacje o granicach.

Adnotacje należy dołączać do wskaźników w polach struktur i parametrach funkcji wskazujących na tablice obiektów, a także do zmiennych globalnych z wskaźnikami. Dla wskaźników w zmiennych lokalnych adnotacje nie muszą być dodawane, ponieważ są automatycznie traktowane jako rozszerzone wskaźniki, które już zawierają informacje o dozwolonych granicach. Podpowiedzi dotyczące konstrukcji w kodzie, dla których wymagana jest dodatkowa adnotacja, są wyświetlane przez kompilator podczas uruchamiania z flagą „-fbounds-safety”.

Model zabezpieczeń oparty na „-fbounds-safety” można wdrażać stopniowo, plik po pliku, nie przerywając prac nad całym projektem. Dodawanie ochrony do projektu sprowadza się do wskazania adnotacji w określonym pliku z kodem, usunięcia ostrzeżeń kompilatora oraz przeprowadzenia testów działania programu, po czym etapy te powtarzają się dla następnego pliku. Kod z dodanymi adnotacjami pozostaje zgodny z normalnym kodem w C oraz kompilatorami, które nie obsługują „-fbounds-safety” (przy kompilacji innymi kompilatorami lub przy kompilacji bez flagi „-fbounds-safety” po prostu nie zostaną dodane dodatkowe kontrole granic).

Podczas działania programu, w przypadku wykrycia odwołania poza dozwolone granice, generowane jest wyjątek i program kończy swoje działanie. Awaryjne zakończenie także może wystąpić przy wskazywaniu niepoprawnych adnotacji, dlatego przy użyciu „-fbounds-safety” należy przeprowadzić dodatkowe testy działania programu.

W poniższym przykładzie do parametru „int *p” dodano adnotację „__counted_by(n)”, dodającą dodatkową kontrolę na dozwolone granice, działającą w czasie wykonywania. Jeśli spróbujesz skompilować kod w trybie „-fbounds-safety” bez wskazania tej adnotacji, kompilator wyświetli ostrzeżenie o braku informacji o granicach tablicy podczas przetwarzania wyrażenia „p[i] = 0”. #include void init_buf(int *__counted_by(n) p, int n) { for (int i = 0; i < n; ++i) p[i] = 0; // w trybie „-fbounds-safety” kompilator sam doda kontrolę, analogiczną do kodu „if (i = n) trap();” }

Dla lokalnych zmiennych z wskaźnikami kontrole są dołączane automatycznie, na przykład: void foo(int i){ char *buf = (char *)malloc(10); // dla wskaźnika buf zostaną zachowane informacje o granicach buf[i] = 0xff; // zostanie automatycznie dodana kontrola „if (buf + i = buf + 10) trap();” }

Tam, gdzie to możliwe, kompilator przeprowadza optymalizację i wyklucza dodanie zbędnego kodu, jeśli w kodzie już istnieją wymagane kontrole. Na przykład: for (size_t i = 0; i < count; ++i) { buf[i] = i; // kontrola „if (i = count) trap()” nie zostanie dodana, ponieważ powyżej już istnieje warunek „i < count” a i nie może być mniejsze od 0. }

Podstawowe adnotacje:

  • „__counted_by(N)” — określa rozmiar bufora w elementach docelowego typu.
  • „__sized_by(N)” — określa rozmiar bufora w bajtach.
  • » __ended_by(P)» — ustala górną granicę bufora.
  • «__null_terminated» — uwzględnia znak null jako koniec bufora.
  • «__single» — wiąże wskaźnik z jednym obiektem. Stosowane domyślnie dla wskaźników wpływających na ABI, chyba że wyraźnie oznaczono inaczej.
  • «__bidi_indexable» — rozszerzony wskaźnik z informacjami o górnych i dolnych granicach. Stosowane domyślnie dla wskaźników, które nie wpływają na ABI.
  • «__indexable» — rozszerzony wskaźnik z informacjami o górnej granicy.
  • «__unsafe_indexable» — wskaźnik bez sprawdzania granic (dla przenośności z niebezpiecznym kodem, na przykład do uzyskiwania wskaźników z zewnętrznego kodu).

Źródło: opennet.ru

Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS 🔥 Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS | ProHoster