Integralność przepływu sterowania

Zadbaj o dobrą organizację dzięki kolekcji Zapisuj i kategoryzuj treści zgodnie ze swoimi preferencjami.

Od 2016 r. około 86% wszystkich luk w zabezpieczeniach systemu Android jest związanych z bezpieczeństwem pamięci. Większość luk w zabezpieczeniach jest wykorzystywana przez osoby atakujące zmieniające normalny przepływ kontroli aplikacji w celu wykonania dowolnych złośliwych działań ze wszystkimi przywilejami wykorzystywanej aplikacji. Integralność przepływu sterowania (CFI) to mechanizm zabezpieczeń, który uniemożliwia wprowadzanie zmian w oryginalnym wykresie przepływu sterowania skompilowanego pliku binarnego, co znacznie utrudnia wykonywanie takich ataków.

W systemie Android 8.1 włączyliśmy implementację CFI przez LLVM w stosie mediów. W Androidzie 9 włączyliśmy CFI w większej liczbie komponentów, a także w jądrze. System CFI jest domyślnie włączony, ale musisz włączyć CFI jądra.

CFI LLVM wymaga kompilacji z optymalizacją czasu łącza (LTO) . LTO zachowuje reprezentację plików obiektowych w kodzie bitowym LLVM do czasu połączenia, co pozwala kompilatorowi lepiej zrozumieć, jakie optymalizacje można wykonać. Włączenie LTO zmniejsza rozmiar końcowego pliku binarnego i poprawia wydajność, ale wydłuża czas kompilacji. W testach na Androidzie połączenie LTO i CFI powoduje znikome obciążenie dla rozmiaru kodu i wydajności; w kilku przypadkach obie uległy poprawie.

Aby uzyskać więcej szczegółów technicznych na temat CFI i sposobu obsługi innych kontroli kontroli do przodu, zobacz dokumentację projektową LLVM .

Przykłady i źródło

CFI jest dostarczany przez kompilator i dodaje oprzyrządowanie do pliku binarnego w czasie kompilacji. Obsługujemy CFI w toolchainie Clang oraz system kompilacji Androida w AOSP.

CFI jest domyślnie włączone dla urządzeń Arm64 dla zestawu komponentów w /platform/build/target/product/cfi-common.mk . Jest również bezpośrednio włączony w zestawie plików makefile/blueprint komponentów multimedialnych, takich jak /platform/frameworks/av/media/libmedia/Android.bp i /platform/frameworks/av/cmds/stagefright/Android.mk .

Wdrażanie systemu CFI

CFI jest domyślnie włączone, jeśli używasz Clang i systemu kompilacji Android. Ponieważ CFI pomaga zapewnić bezpieczeństwo użytkownikom Androida, nie należy go wyłączać.

W rzeczywistości gorąco zachęcamy do włączenia CFI dla dodatkowych komponentów. Idealnymi kandydatami są uprzywilejowany kod natywny lub kod natywny przetwarzający dane wejściowe niezaufanych użytkowników. Jeśli używasz clang i systemu kompilacji Android, możesz włączyć CFI w nowych komponentach, dodając kilka wierszy do swoich plików makefile lub plików blueprint.

Obsługa CFI w plikach makefile

Aby włączyć CFI w pliku make, takim jak /platform/frameworks/av/cmds/stagefright/Android.mk , dodaj:

LOCAL_SANITIZE := cfi
# Optional features
LOCAL_SANITIZE_DIAG := cfi
LOCAL_SANITIZE_BLACKLIST := cfi_blacklist.txt
  • LOCAL_SANITIZE określa CFI jako środek odkażający podczas kompilacji.
  • LOCAL_SANITIZE_DIAG włącza tryb diagnostyczny dla CFI. Tryb diagnostyczny drukuje dodatkowe informacje debugowania w dzienniku podczas awarii, co jest przydatne podczas opracowywania i testowania kompilacji. Pamiętaj jednak o usunięciu trybu diagnostycznego w kompilacjach produkcyjnych.
  • LOCAL_SANITIZE_BLACKLIST umożliwia składnikom selektywne wyłączanie instrumentacji CFI dla poszczególnych funkcji lub plików źródłowych. Możesz użyć czarnej listy w ostateczności, aby rozwiązać wszelkie problemy, z którymi borykają się użytkownicy, które w przeciwnym razie mogłyby wystąpić. Aby uzyskać więcej informacji, zobacz Wyłączanie CFI .

Wspieranie CFI w plikach blueprint

Aby włączyć CFI w pliku projektu, takim jak /platform/frameworks/av/media/libmedia/Android.bp , dodaj:

   sanitize: {
        cfi: true,
        diag: {
            cfi: true,
        },
        blacklist: "cfi_blacklist.txt",
    },

Rozwiązywanie problemów

Jeśli włączasz CFI w nowych komponentach, możesz napotkać kilka problemów z błędami niezgodności typu funkcji i błędami niezgodności typu kodu zespołu .

Błędy niezgodności typu funkcji występują, ponieważ CFI ogranicza wywołania pośrednie tylko do przeskoków do funkcji, które mają ten sam typ dynamiczny, co typ statyczny użyty w wywołaniu. CFI ogranicza wirtualne i niewirtualne wywołania funkcji składowych do przeskoku tylko do obiektów, które są klasą pochodną typu statycznego obiektu użytego do wykonania wywołania. Oznacza to, że jeśli masz kod, który narusza którekolwiek z tych założeń, instrumentacja dodana przez CFI zostanie przerwana. Na przykład ślad stosu pokazuje SIGABRT, a logcat zawiera wiersz dotyczący integralności przepływu sterowania, który znajduje niezgodność.

Aby to naprawić, upewnij się, że wywoływana funkcja ma ten sam typ, który został zadeklarowany statycznie. Oto dwa przykładowe CL:

Innym możliwym problemem jest próba włączenia CFI w kodzie, który zawiera pośrednie wywołania asemblera. Ponieważ kod zestawu nie jest wpisany, powoduje to niezgodność typu.

Aby rozwiązać ten problem, utwórz otoki kodu natywnego dla każdego wywołania zestawu i nadaj otokom ten sam podpis funkcji, co wskaźnik wywołujący. Opakowanie może następnie bezpośrednio wywołać kod asemblera. Ponieważ oddziały bezpośrednie nie są oprzyrządowane przez CFI (nie można ich ponownie wskazać w czasie wykonywania, a więc nie stanowią zagrożenia dla bezpieczeństwa), to rozwiąże problem.

Jeśli jest zbyt wiele funkcji asemblera i nie można ich wszystkich naprawić, możesz również umieścić na czarnej liście wszystkie funkcje, które zawierają pośrednie wywołania asemblera. Nie jest to zalecane, ponieważ wyłącza kontrole CFI tych funkcji, otwierając w ten sposób powierzchnię ataku.

Wyłączanie CFI

Nie zaobserwowaliśmy żadnych narzutów na wydajność, więc nie powinieneś wyłączać CFI. Jeśli jednak istnieje wpływ na użytkownika, można selektywnie wyłączyć CFI dla poszczególnych funkcji lub plików źródłowych, dostarczając plik czarnej listy programu dezynfekującego w czasie kompilacji. Czarna lista nakazuje kompilatorowi wyłączenie instrumentacji CFI w określonych lokalizacjach.

System kompilacji Androida zapewnia obsługę czarnych list poszczególnych komponentów (umożliwiając wybór plików źródłowych lub poszczególnych funkcji, które nie otrzymają oprzyrządowania CFI) zarówno dla Make, jak i Soong. Aby uzyskać więcej informacji na temat formatu pliku czarnej listy, zapoznaj się z dokumentacją nadrzędną Clang .

Walidacja

Obecnie nie ma testu CTS specjalnie dla CFI. Zamiast tego upewnij się, że testy CTS przeszły pomyślnie z włączonym CFI lub bez niego, aby sprawdzić, czy CFI nie wpływa na urządzenie.