Szczegóły techniczne dotyczące integralności przepływu sterowania
„Dostępność ogromnej liczby wskaźników funkcji w jądrze zwiększa popularność tego wzorca ataku. Nawet jeśli napastnicy nie mogą wstrzyknąć własnego kodu wykonywalnego, w celu uzupełnienia exploita można wykonać dowolne części istniejącego kodu jądra.
LLVMCFI próbuje złagodzić te ataki, ograniczając prawidłowe cele wywołań i wymuszając panikę jądra w przypadku wykrycia naruszenia CFI. Przed każdą gałęzią pośrednią dodawana jest kontrola, aby potwierdzić, że adres docelowy wskazuje na prawidłową funkcję z poprawnym podpisem. Zapobiega to przeskakiwaniu gałęzi pośredniej do dowolnej lokalizacji kodu, a nawet ogranicza funkcje, które można wywołać. Osoba atakująca nadal będzie mogła zmienić wskaźnik funkcji, jeśli błąd umożliwi dostęp. Jednak CFI LLVM ogranicza 55% połączeń pośrednich do maksymalnie 5 możliwych celów i 80% do maksymalnie 20 celów. Aby określić wszystkie prawidłowe cele wywołań dla każdej gałęzi pośredniej, kompilator musi zobaczyć cały kod jądra na raz.
Korzystanie z LTO (Optymalizacja czasu łącza) sprawia, że jest to możliwe. CFI LLVM wymaga użycia LTO, gdzie kompilator tworzy kod bitowy specyficzny dla LLVM dla wszystkich C jednostki kompilacji, a linker obsługujący LTO używa zaplecza LLVM do łączenia kodu bitowego i kompilowania go kod natywny.
Oprócz umożliwienia korzystania z CFI, LTO osiąga lepszą wydajność w czasie wykonywania poprzez analizę całego programu i optymalizację między modułami.
Cienki LTO prawie dogonił poprawę wydajności LTO. W trybie ThinLTO, podobnie jak w przypadku zwykłego LTO, Szczęk emituje kod bitowy LLVM po fazie kompilacji. Kod bitowy ThinLTO jest wzbogacony o zwarte podsumowanie modułu. Na etapie łączenia odczytywane są tylko podsumowania i łączone w połączony indeks podsumowujący, który zawiera indeks lokalizacji funkcji na potrzeby późniejszego importowania funkcji między modułami. Następnie przeprowadzana jest szybka i skuteczna analiza całego programu na podstawie połączonego indeksu podsumowującego. ThinLTO pozwala na wielowątkowy proces łączenia, co skutkuje skróceniem czasu kompilacji.
Ze względu na to, że CFI przerywa wykonywanie programu po napotkaniu pewnych klas błędów, klasyfikuje się go również jako narzędzie do wyszukiwania błędów, jak wspomniano wcześniej, gdy jest używane w trybie zezwalającym. Zezwalający CFI pokaże naruszenia CFI w dzienniku jądra, bez wymuszania paniki jądra. Jądra rdzenia 4.9 (urządzenia Pixel 3 generacji) i 4.14 (urządzenia Pixel 4 generacji) miały kilka typów funkcji niedopasowania skutkujące naruszeniami CFI, które zostały naprawione przez Google w zestawach poprawek dostępnych w jądrze/common repozytoria.
Jednak ze względu na naturę ekosystemu Androida te rozbieżności prawdopodobnie można znaleźć również w kodzie producenta SoC (w tym przypadku Qualcomm) lub OEM (OnePlus). W jądrze Kirisakura dla OnePlus 8 Pro naprawiono kilka naruszeń CFI w kodzie Qualcomm odmiennym od jądra 4.19 (przykład: 1, 2, 3).
Uruchomienie jądra w permisywnym CFI ujawniło naruszenia CFI również w kodzie związanym ze sterownikami OnePlus (odpowiednie zatwierdzenia można znaleźć Tutaj I Tutaj). Jądro Kirisakura dla OnePlus 8 Pro działa z wymuszonym CFI, chroniąc użytkowników przed tego rodzaju atakami polegającymi na ponownym użyciu kodu.
Czytaj więcej
Entuzjasta majsterkowania (czyli zbieracz starych części do PC). Skanda, będąca zagorzałym użytkownikiem Androida od czasów Eclaira, lubi także śledzić najnowsze trendy rozwojowe w świecie komputerów jednopłytkowych.