UndefinedBehaviorSanitizer

UndefinitedBehaviorSanitizer (UBSan) wykonuje instrumentację w czasie kompilacji, aby sprawdzić różne typy niezdefiniowanego zachowania. Chociaż UBSan jest w stanie wykryć wiele niezdefiniowanych błędów w zachowaniu , Android obsługuje:

  • wyrównanie
  • bool
  • miedza
  • wyliczenie
  • przelew-odlew-pływak
  • float-dzielenie-przez-zero
  • liczba całkowita, dzielenie przez zero
  • atrybut inny niż null
  • zero
  • powrót
  • zwraca atrybut-nonnull
  • podstawa zmiany
  • wykładnik przesunięcia
  • Przepełnienie liczby całkowitej ze znakiem
  • nieosiągalny
  • Przepełnienie liczby całkowitej bez znaku
  • związany z vla

unsigned-integer-overflow, chociaż nie jest to technicznie niezdefiniowane zachowanie, jest zawarte w narzędziu dezynfekującym i używane w wielu modułach Androida, w tym w komponentach serwera multimediów, w celu wyeliminowania wszelkich ukrytych luk w zabezpieczeniach związanych z przepełnieniem liczb całkowitych.

Realizacja

W systemie kompilacji Androida możesz włączyć UBSan globalnie lub lokalnie. Aby włączyć UBSan globalnie, ustaw SANITIZE_TARGET w Android.mk. Aby włączyć UBSan na poziomie poszczególnych modułów, ustaw LOCAL_SANITIZE i określ niezdefiniowane zachowania, których chcesz szukać w Android.mk. Na przykład:

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_CFLAGS := -std=c11 -Wall -Werror -O0

LOCAL_SRC_FILES:= sanitizer-status.c

LOCAL_MODULE:= sanitizer-status

LOCAL_SANITIZE := alignment bounds null unreachable integer
LOCAL_SANITIZE_DIAG := alignment bounds null unreachable integer

include $(BUILD_EXECUTABLE)

I równoważna konfiguracja blueprintu (Android.bp):

cc_binary {

    cflags: [
        "-std=c11",
        "-Wall",
        "-Werror",
        "-O0",
    ],

    srcs: ["sanitizer-status.c"],

    name: "sanitizer-status",

    sanitize: {
        misc_undefined: [
            "alignment",
            "bounds",
            "null",
            "unreachable",
            "integer",
        ],
        diag: {
            misc_undefined: [
                "alignment",
                "bounds",
                "null",
                "unreachable",
                "integer",
            ],
        },
    },

}

Skróty UBSan

Android ma także dwa skróty, integer i default-ub , umożliwiające jednoczesne włączenie zestawu środków dezynfekujących. integer umożliwia integer-divide-by-zero , signed-integer-overflow i unsigned-integer-overflow . default-ub umożliwia sprawdzanie, które powoduje minimalne problemy z wydajnością kompilatora: bool, integer-divide-by-zero, return, returns-nonnull-attribute, shift-exponent, unreachable and vla-bound . Klasy integer sanitizer można używać z SANITIZE_TARGET i LOCAL_SANITIZE, natomiast default-ub można używać tylko z SANITIZE_TARGET.

Lepsze raportowanie błędów

Domyślna implementacja UBSan systemu Android wywołuje określoną funkcję w przypadku napotkania niezdefiniowanego zachowania. Domyślnie ta funkcja jest przerywana. Jednak od października 2016 r. UBSan na Androida ma opcjonalną bibliotekę wykonawczą, która zapewnia bardziej szczegółowe raporty o błędach, w tym typ napotkanego niezdefiniowanego zachowania, informacje o plikach i wierszach kodu źródłowego. Aby włączyć raportowanie błędów przy sprawdzaniu liczb całkowitych, dodaj następujące elementy do pliku Android.mk:

LOCAL_SANITIZE:=integer
LOCAL_SANITIZE_DIAG:=integer

Wartość LOCAL_SANITIZE umożliwia odkażanie podczas kompilacji. LOCAL_SANITIZE_DIAG włącza tryb diagnostyczny dla określonego środka dezynfekującego. Możliwe jest ustawienie LOCAL_SANITIZE i LOCAL_SANITIZE_DIAG na różne wartości, ale włączone są tylko te kontrole w LOCAL_SANITIZE. Jeśli kontrola nie jest określona w LOCAL_SANITIZE, ale jest określona w LOCAL_SANITIZE_DIAG, kontrola nie jest włączona i nie są wyświetlane komunikaty diagnostyczne.

Oto przykład informacji dostarczonych przez bibliotekę uruchomieniową UBSan:

pixel-xl:/ # sanitizer-status ubsan
sanitizer-status/sanitizer-status.c:53:6: runtime error: unsigned integer overflow: 18446744073709551615 + 1 cannot be represented in type 'size_t' (aka 'unsigned long')

Oczyszczanie przepełnienia liczb całkowitych

Niezamierzone przepełnienie liczb całkowitych może spowodować uszkodzenie pamięci lub luki w zabezpieczeniach umożliwiające ujawnienie informacji w zmiennych związanych z dostępem do pamięci lub alokacją pamięci. Aby temu zaradzić, dodaliśmy narzędzie Clang UndefiniBehaviorSanitizer (UBSan) do czyszczenia przepełnienia liczb całkowitych ze znakiem i bez znaku, aby wzmocnić środowisko multimediów w systemie Android 7.0. W systemie Android 9 rozszerzyliśmy UBSan, aby objąć więcej komponentów i ulepszyć obsługę systemu kompilacji.

Ma to na celu dodanie kontroli operacji/instrukcji arytmetycznych – które mogą zostać przepełnione – aby bezpiecznie przerwać proces, jeśli nastąpi przepełnienie. Te środki dezynfekujące mogą złagodzić całą klasę luk w zabezpieczeniach związanych z uszkodzeniem pamięci i ujawnieniem informacji, których podstawową przyczyną jest przepełnienie liczb całkowitych, tak jak oryginalna luka Stagefright.

Przykłady i źródło

Kompilator zapewnia oczyszczanie przepełnienia liczb całkowitych (IntSan) i dodaje oprzyrządowanie do pliku binarnego w czasie kompilacji w celu wykrycia przepełnień arytmetycznych. Jest domyślnie włączona w różnych komponentach całej platformy, na przykład /platform/external/libnl/Android.bp .

Realizacja

IntSan wykorzystuje środki dezynfekujące firmy UBSan w postaci przepełnionych liczb całkowitych ze znakiem i bez znaku. To ograniczenie jest włączone na poziomie poszczególnych modułów. Pomaga chronić krytyczne komponenty Androida i nie należy go wyłączać.

Gorąco zachęcamy do włączenia oczyszczania przepełnienia liczby całkowitej dla dodatkowych komponentów. Idealnymi kandydatami są uprzywilejowany kod natywny lub kod natywny, który analizuje dane wejściowe niezaufanego użytkownika. Środek dezynfekujący wiąże się z niewielkim obciążeniem wydajnościowym, zależnym od użycia kodu i częstości wykonywania operacji arytmetycznych. Spodziewaj się niewielkiego procentu narzutu i sprawdź, czy wydajność jest problemem.

Wspieranie IntSan w plikach makefile

Aby włączyć IntSan w pliku makefile, dodaj:

LOCAL_SANITIZE := integer_overflow
    # Optional features
    LOCAL_SANITIZE_DIAG := integer_overflow
    LOCAL_SANITIZE_BLOCKLIST := modulename_BLOCKLIST.txt
  • LOCAL_SANITIZE pobiera rozdzieloną przecinkami listę środków dezynfekujących, przy czym integer_overflow jest wstępnie spakowanym zestawem opcji dla poszczególnych środków dezynfekujących przepełnienie liczb całkowitych ze znakiem i bez znaku z domyślną BLOCKLIST .
  • LOCAL_SANITIZE_DIAG włącza tryb diagnostyczny dla środków dezynfekujących. Używaj trybu diagnostycznego tylko podczas testowania, ponieważ nie spowoduje to przerwania w przypadku przepełnienia, całkowicie negując przewagę bezpieczeństwa wynikającą z ograniczenia. Aby uzyskać dodatkowe informacje, zobacz Rozwiązywanie problemów .
  • LOCAL_SANITIZE_BLOCKLIST umożliwia określenie pliku BLOCKLIST, aby zapobiec oczyszczaniu funkcji i plików źródłowych. Aby uzyskać dodatkowe informacje, zobacz Rozwiązywanie problemów .

Jeśli chcesz bardziej szczegółowej kontroli, włącz środki dezynfekujące indywidualnie, używając jednej lub obu flag:

LOCAL_SANITIZE := signed-integer-overflow, unsigned-integer-overflow
    LOCAL_SANITIZE_DIAG := signed-integer-overflow, unsigned-integer-overflow

Wspieranie IntSan w plikach planów

Aby włączyć oczyszczanie przepełnienia liczb całkowitych w pliku planu, takim jak /platform/external/libnl/Android.bp , dodaj:

   sanitize: {
          integer_overflow: true,
          diag: {
              integer_overflow: true,
          },
          BLOCKLIST: "modulename_BLOCKLIST.txt",
       },

Podobnie jak w przypadku plików make, właściwość integer_overflow jest wstępnie spakowanym zestawem opcji dla poszczególnych funkcji oczyszczania przepełnienia liczb całkowitych ze znakiem i bez znaku z domyślną BLOCKLIST .

Zestaw właściwości diag włącza tryb diagnostyczny dla środków dezynfekujących. Używaj trybu diagnostycznego tylko podczas testowania. Tryb diagnostyczny nie przerywa pracy w przypadku przepełnienia, co całkowicie neguje przewagę w zakresie bezpieczeństwa wynikającą z łagodzenia skutków w kompilacjach użytkowników. Aby uzyskać dodatkowe informacje, zobacz Rozwiązywanie problemów .

Właściwość BLOCKLIST umożliwia określenie pliku BLOCKLIST, który umożliwia programistom zapobieganie oczyszczaniu funkcji i plików źródłowych. Aby uzyskać dodatkowe informacje, zobacz Rozwiązywanie problemów .

Aby włączyć środki dezynfekujące indywidualnie, użyj:

   sanitize: {
          misc_undefined: ["signed-integer-overflow", "unsigned-integer-overflow"],
          diag: {
              misc_undefined: ["signed-integer-overflow",
                               "unsigned-integer-overflow",],
          },
          BLOCKLIST: "modulename_BLOCKLIST.txt",
       },

Rozwiązywanie problemów

Jeśli włączasz oczyszczanie przepełnienia liczb całkowitych w nowych komponentach lub polegasz na bibliotekach platformy, które mają oczyszczanie przekroczeń liczb całkowitych, możesz napotkać kilka problemów z łagodnymi przepełnieniami liczb całkowitych powodującymi przerwania. Należy przetestować komponenty z włączoną funkcją odkażania, aby upewnić się, że wypłyną na powierzchnię łagodne przelewy.

Aby znaleźć przerwania spowodowane oczyszczaniem kompilacji użytkownika, wyszukaj awarie SIGABRT z komunikatami o przerwaniu wskazującymi przepełnienie przechwycone przez UBSan, takie jak:

pid: ###, tid: ###, name: Binder:###  >>> /system/bin/surfaceflinger <<<
    signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
    Abort message: 'ubsan: sub-overflow'

Ślad stosu powinien zawierać funkcję powodującą przerwanie, jednakże przepełnienia występujące w funkcjach wbudowanych mogą nie być widoczne w śladzie stosu.

Aby łatwiej określić przyczynę główną, włącz diagnostykę w bibliotece powodującą przerwanie i spróbuj odtworzyć błąd. Po włączeniu diagnostyki proces nie zostanie przerwany i zamiast tego będzie kontynuowany. Brak przerywania pomaga zmaksymalizować liczbę łagodnych przepełnień w określonej ścieżce wykonania bez konieczności ponownej kompilacji po naprawieniu każdego błędu. Diagnostyka generuje komunikat o błędzie, który zawiera numer linii i plik źródłowy powodujący przerwanie:

frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp:2188:32: runtime error: unsigned integer overflow: 0 - 1 cannot be represented in type 'size_t' (aka 'unsigned long')

Po zlokalizowaniu problematycznej operacji arytmetycznej upewnij się, że przepełnienie jest łagodne i zamierzone (np. nie ma wpływu na bezpieczeństwo). Przerwanie działania środka dezynfekującego można rozwiązać w następujący sposób:

  • Refaktoryzacja kodu, aby uniknąć przepełnienia ( przykład )
  • Przepełnienie jawnie za pomocą funkcji __builtin_*_overflow Clanga ( przykład )
  • Wyłączanie oczyszczania w funkcji poprzez określenie atrybutu no_sanitize ( przykład )
  • Wyłączanie oczyszczania funkcji lub pliku źródłowego za pomocą pliku BLOCKLIST ( przykład )

Należy zastosować możliwie najbardziej szczegółowe rozwiązanie. Na przykład duża funkcja z wieloma operacjami arytmetycznymi i pojedynczą operacją przepełnienia powinna podlegać refaktoryzacji pojedynczej operacji, a nie całej funkcji BLOCKLIST.

Typowe wzorce, które mogą powodować łagodne przelewy, obejmują:

  • Niejawne rzutowanie , w przypadku którego następuje przepełnienie bez znaku przed rzutowaniem na typ ze znakiem ( przykład )
  • Usuwanie połączonych list, które zmniejsza indeks pętli po usunięciu ( przykład )
  • Przypisywanie typu bez znaku do -1 zamiast określania rzeczywistej wartości maksymalnej ( przykład )
  • Pętle zmniejszające liczbę całkowitą bez znaku w warunku ( przykład , przykład )

Zaleca się, aby programiści upewnili się, że w przypadkach, w których środek dezynfekujący wykryje przepełnienie, jest to rzeczywiście łagodne i nie powoduje niezamierzonych skutków ubocznych ani konsekwencji dla bezpieczeństwa, przed wyłączeniem dezynfekcji.

Wyłączanie IntSana

Możesz wyłączyć IntSan za pomocą BLOCKLIST lub atrybutów funkcji. Wyłączaj oszczędnie i tylko wtedy, gdy refaktoryzacja kodu jest w innym przypadku nieuzasadniona lub jeśli występuje problematyczny narzut wydajności.

Więcej informacji na temat wyłączania IntSan z atrybutami funkcji i formatowaniem pliku BLOCKLIST znajdziesz w dokumentacji Clang. Lista BLOKOWANA powinna obejmować konkretny środek dezynfekujący, używając nazw sekcji określających docelowy środek dezynfekujący, aby uniknąć wpływu na inne środki dezynfekujące.

Walidacja

Obecnie nie ma testu CTS przeznaczonego specjalnie do oczyszczania z przepełnienia liczb całkowitych. Zamiast tego upewnij się, że testy CTS przeszły pomyślnie z włączoną funkcją IntSan lub bez niej, aby sprawdzić, czy nie ma to wpływu na urządzenie.

Ogranicza sanityzację

BoundsSanitizer (BoundSan) dodaje oprzyrządowanie do plików binarnych w celu wstawiania kontroli granic wokół dostępu do tablicy. Te kontrole są dodawane, jeśli kompilator nie może udowodnić w czasie kompilacji, że dostęp będzie bezpieczny i jeśli rozmiar tablicy będzie znany w czasie wykonywania, aby można było go sprawdzić. Android 10 wdraża BoundSan w Bluetooth i kodekach. BoundSan jest dostarczany przez kompilator i jest domyślnie włączony w różnych komponentach na całej platformie.

Realizacja

BoundSan używa środka dezynfekującego firmy UBSan . To ograniczenie jest włączone na poziomie poszczególnych modułów. Pomaga chronić krytyczne komponenty Androida i nie należy go wyłączać.

Gorąco zachęcamy do włączenia BoundSan dla dodatkowych komponentów. Idealnymi kandydatami są uprzywilejowany kod natywny lub złożony kod natywny, który analizuje dane wejściowe niezaufanego użytkownika. Narzut związany z wydajnością związany z włączeniem BoundSan zależy od liczby dostępów do tablicy, których bezpieczeństwa nie można udowodnić. Spodziewaj się średnio niewielkiego procentu narzutu i sprawdź, czy wydajność jest problemem.

Włączanie BoundSan w plikach planów

BoundSan można włączyć w plikach planów, dodając "bounds" do właściwości misc_undefined sanitize dla modułów binarnych i bibliotecznych:

    sanitize: {
       misc_undefined: ["bounds"],
       diag: {
          misc_undefined: ["bounds"],
       },
       BLOCKLIST: "modulename_BLOCKLIST.txt",
diag

Właściwość diag włącza tryb diagnostyczny dla środków dezynfekujących. Używaj trybu diagnostycznego tylko podczas testowania. Tryb diagnostyczny nie przerywa pracy w przypadku przepełnień, co neguje przewagę zabezpieczeń wynikającą z ograniczenia ryzyka i wiąże się z większym obciążeniem wydajnościowym, dlatego nie jest zalecane w przypadku kompilacji produkcyjnych.

LISTA ZABLOKOWANYCH

Właściwość BLOCKLIST umożliwia określenie pliku BLOCKLIST, którego programiści mogą używać w celu zapobiegania oczyszczaniu funkcji i plików źródłowych. Tej właściwości należy używać tylko wtedy, gdy problemem jest wydajność, a docelowe pliki/funkcje znacząco się do tego przyczyniają. Ręcznie sprawdź te pliki/funkcje, aby upewnić się, że dostęp do tablicy jest bezpieczny. Aby uzyskać dodatkowe informacje, zobacz Rozwiązywanie problemów .

Włączanie BoundSan w plikach makefile

BoundSan można włączyć w plikach makefile, dodając "bounds" do zmiennej LOCAL_SANITIZE dla modułów binarnych i bibliotecznych:

    LOCAL_SANITIZE := bounds
    # Optional features
    LOCAL_SANITIZE_DIAG := bounds
    LOCAL_SANITIZE_BLOCKLIST := modulename_BLOCKLIST.txt

LOCAL_SANITIZE akceptuje listę środków dezynfekujących oddzielonych przecinkami.

LOCAL_SANITIZE_DIAG włącza tryb diagnostyczny. Używaj trybu diagnostycznego tylko podczas testowania. Tryb diagnostyczny nie przerywa pracy w przypadku przepełnień, co neguje przewagę zabezpieczeń wynikającą z ograniczenia ryzyka i wiąże się z większym obciążeniem wydajnościowym, dlatego nie jest zalecane w przypadku kompilacji produkcyjnych.

LOCAL_SANITIZE_BLOCKLIST umożliwia specyfikację pliku BLOCKLIST, który umożliwia programistom zapobieganie oczyszczaniu funkcji i plików źródłowych. Tej właściwości należy używać tylko wtedy, gdy problemem jest wydajność, a docelowe pliki/funkcje znacząco się do tego przyczyniają. Ręcznie sprawdź te pliki/funkcje, aby upewnić się, że dostęp do tablicy jest bezpieczny. Aby uzyskać dodatkowe informacje, zobacz Rozwiązywanie problemów .

Wyłączanie BoundSan

Możesz wyłączyć BoundSan w funkcjach i plikach źródłowych za pomocą BLOKAD LIST lub atrybutów funkcji. Najlepiej pozostawić usługę BoundSan włączoną, więc wyłączaj ją tylko wtedy, gdy funkcja lub plik powoduje duże obciążenie wydajnościowe, a źródło zostało sprawdzone ręcznie.

Więcej informacji na temat wyłączania BoundSan z atrybutami funkcji i formatowaniem plików BLOCKLIST można znaleźć w dokumentacji Clang LLVM. Zawęź LISTĘ BLOKOWANĄ do konkretnego środka dezynfekującego, używając nazw sekcji określających docelowy środek dezynfekujący, aby uniknąć wpływu na inne środki dezynfekujące.

Walidacja

Nie ma testu CTS specjalnie dla BoundSan. Zamiast tego upewnij się, że testy CTS przeszły pomyślnie z włączoną funkcją BoundSan lub bez niej, aby sprawdzić, czy nie ma to wpływu na urządzenie.

Rozwiązywanie problemów

Dokładnie przetestuj komponenty po włączeniu BoundSan, aby upewnić się, że wszelkie wcześniej niewykryte dostępy poza zakresem zostaną rozwiązane.

Błędy BoundSan można łatwo zidentyfikować, ponieważ zawierają następujący komunikat o przerwaniu działania:

    pid: ###, tid: ###, name: Binder:###  >>> /system/bin/foobar <<<
    signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
    Abort message: 'ubsan: out-of-bounds'

Podczas pracy w trybie diagnostycznym plik źródłowy, numer linii i wartość indeksu są wypisywane do logcat . Domyślnie ten tryb nie generuje komunikatu o przerwaniu. Przejrzyj logcat aby sprawdzić, czy nie ma żadnych błędów.

    external/foo/bar.c:293:13: runtime error: index -1 out of bounds for type 'int [24]'