Odkażacze LLVM

LLVM, infrastruktura kompilatora używana do budowania systemu Android, zawiera wiele komponentów, które wykonują analizę statyczną i dynamiczną. Spośród tych składników środki dezynfekujące — w szczególności AddressSanitizer i UndefinedBehaviorSanitizer — mogą być szeroko stosowane do analizy systemu Android. Środki odkażające to oparte na kompilatorach składniki instrumentacji zawarte w external/compiler-rt, których można używać podczas opracowywania i testowania w celu wypychania błędów i ulepszania systemu Android. Obecny zestaw środków odkażających Androida może wykryć i zdiagnozować wiele błędów związanych z niewłaściwym wykorzystaniem pamięci i potencjalnie niebezpieczne niezdefiniowane zachowanie.

Najlepszym rozwiązaniem w przypadku kompilacji systemu Android jest uruchamianie i uruchamianie z włączonymi środkami dezynfekującymi, takimi jak AddressSanitizer i UndefinedBehaviorSanitizer. Ta strona przedstawia AddressSanitizer, UndefinedBehaviorSanitizer i KernelAddressSanitizer, pokazuje, w jaki sposób mogą być używane w systemie kompilacji Androida i podaje przykładowe pliki Android.mk i Android.bp, które tworzą komponenty natywne z włączonymi tymi środkami odkażającymi.

AdresSanitizer

AddressSanitizer (ASan) to funkcja instrumentacji oparta na kompilatorze, która wykrywa wiele typów błędów pamięci w kodzie C/C++ w czasie wykonywania. ASan może wykryć wiele klas błędów pamięci, w tym:

  • Dostęp do pamięci poza granicami
  • Pokój dwuosobowy za darmo
  • Użyj po wolnym

Android pozwala na oprzyrządowanie ASan na poziomie pełnej kompilacji i na poziomie aplikacji z asanwrapper.

AddressSanitizer łączy instrumentację wszystkich wywołań funkcji związanych z pamięcią — w tym alloca, malloc i free — oraz uzupełnia wszystkie zmienne i przydzielone regiony pamięci pamięcią, która wyzwala wywołanie zwrotne ASan, gdy jest odczytywana lub zapisywana.

Oprzyrządowanie umożliwia ASan wykrywanie nieprawidłowych błędów użycia pamięci, w tym podwójnego zwolnienia i zakresu użycia po użyciu, powrotu i zwolnienia, podczas gdy dopełnienie regionu pamięci wykrywa odczyty lub zapisy poza granicami. Jeśli wystąpi odczyt lub zapis w tym regionie wypełniania, ASan przechwytuje go i wyświetla informacje pomocne w diagnozowaniu naruszenia pamięci, w tym stos wywołań, mapę pamięci cienia, typ naruszenia pamięci, co zostało odczytane lub zapisane, instrukcję, która spowodowała naruszenie i zawartość pamięci.

pixel-xl:/ # sanitizer-status                                                                                            
=================================================================
==14164==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x0032000054b0 at pc 0x005df16ffc3c bp 0x007fc236fdf0 sp 0x007fc236fdd0
WRITE of size 1 at 0x0032000054b0 thread T0
    #0 0x5df16ffc3b in test_crash_malloc sanitizer-status/sanitizer-status.c:36:13
    #1 0x5df17004e3 in main sanitizer-status/sanitizer-status.c:76:7
    #2 0x794cf665f3 in __libc_init (/system/lib64/libc.so+0x1b5f3)
    #3 0x5df16ffa53 in do_arm64_start (/system/bin/sanitizer-status+0xa53)

0x0032000054b0 is located 0 bytes to the right of 32-byte region [0x003200005490,0x0032000054b0)
allocated by thread T0 here:
    #0 0x794d0bdc67 in malloc (/system/lib64/libclang_rt.asan-aarch64-android.so+0x74c67)
    #1 0x5df16ffb47 in test_crash_malloc sanitizer-status/sanitizer-status.c:34:25
    #2 0x5df17004e3 in main sanitizer-status/sanitizer-status.c:76:7
    #3 0x794cf665f3 in __libc_init (/system/lib64/libc.so+0x1b5f3)
    #4 0x5df16ffa53 in do_arm64_start (/system/bin/sanitizer-status+0xa53)
    #5 0x794df78893  (<unknown module>)

SUMMARY: AddressSanitizer: heap-buffer-overflow sanitizer-status/sanitizer-status.c:36:13 in test_crash_malloc

Czasami proces wykrywania błędów może wydawać się niedeterministyczny, szczególnie w przypadku błędów wymagających specjalnej konfiguracji lub bardziej zaawansowanych technik, takich jak torowanie sterty lub wykorzystanie warunków wyścigu. Wiele z tych błędów nie jest od razu widocznych i może ujawnić tysiące instrukcji z dala od naruszenia pamięci, które było faktyczną przyczyną. ASan analizuje wszystkie funkcje związane z pamięcią i dane padów z obszarami, do których nie można uzyskać dostępu bez wyzwolenia wywołania zwrotnego ASan. Oznacza to, że naruszenia pamięci są wychwytywane natychmiast, zamiast czekać na awarię powodującą awarię. Jest to niezwykle przydatne w wykrywaniu błędów i diagnozowaniu przyczyn źródłowych.

Aby sprawdzić, czy ASAN działa na urządzeniu docelowym, system Android dołączył plik wykonywalny asan_test. Plik wykonywalny asan_test testuje i weryfikuje funkcjonalność ASAN na urządzeniu docelowym, podając komunikaty diagnostyczne ze statusem każdego testu. W przypadku korzystania z kompilacji ASAN dla systemu Android domyślnie znajduje się ona w /data/nativetest/asan_test/asan_test lub /data/nativetest64/asan_test/asan_test .

UndefinedBehaviorSanitizer

UndefinedBehaviorSanitizer (UBSan) wykonuje instrumentację w czasie kompilacji w celu sprawdzenia różnych typów niezdefiniowanego zachowania. Podczas gdy UBSan jest w stanie wykryć wiele niezdefiniowanych zachowań , Android obsługuje wyrównanie, bool, granice, wyliczenie, float-cast-overflow, float-divide-by-zero, integer-divide-by-zero, nonnull-attribute, null, return, zwraca-nonnull-atrybut, podstawę przesunięcia, wykładnik przesunięcia, przepełnienie liczby całkowitej ze znakiem, nieosiągalny, przepełnienie liczby całkowitej bez znaku i ograniczenie vla. unsigned-integer-overflow, chociaż technicznie nie jest to niezdefiniowane zachowanie, jest zawarte w narzędziu dezynfekującym i używane w wielu modułach systemu Android, w tym w komponentach mediaserver, w celu wyeliminowania wszelkich ukrytych luk w zabezpieczeniach związanych z przepełnieniem liczby całkowitej.

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 modułu, 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)

System kompilacji Androida nie obsługuje jeszcze tak szczegółowej diagnostyki w plikach projektu, jak pliki makefile. Oto najbliższy odpowiednik napisany jako plan (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: {
            undefined : true
        },
    },

}

Skróty do UBSan

Android ma również dwa skróty, integer i default-ub , aby włączyć zestaw środków dezynfekujących w tym samym czasie. liczba całkowita umożliwia integer-divide-by-zero , signed-integer-overflow unsigned-integer-overflow . default-ub włącza testy, które mają minimalne problemy z wydajnością kompilatora: bool, integer-divide-by-zero, return, return-nonnull-attribute, shift-exponent, unreachable i vla-bound. Klasa dezynfekcji liczb całkowitych może być używana z SANITIZE_TARGET i LOCAL_SANITIZE, podczas gdy default-ub może być używana 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 począwszy od października 2016 r. UBSan na Androida ma opcjonalną bibliotekę uruchomieniową, która zapewnia bardziej szczegółowe raportowanie błędów, w tym typ napotkanych niezdefiniowanych zachowań, informacje o pliku i wierszu kodu źródłowego. Aby włączyć to raportowanie błędów z sprawdzaniem liczb całkowitych, dodaj następujące elementy do pliku Android.mk:

LOCAL_SANITIZE:=integer
LOCAL_SANITIZE_DIAG:=integer

Wartość LOCAL_SANITIZE włącza sanitizer podczas kompilacji. LOCAL_SANITIZE_DIAG włącza tryb diagnostyczny dla określonego środka dezynfekującego. Możliwe jest ustawienie różnych wartości LOCAL_SANITIZE i LOCAL_SANITIZE_DIAG, ale tylko te sprawdzenia w LOCAL_SANITIZE są włączone. Jeśli kontrola nie jest określona w LOCAL_SANITIZE, ale jest określona w LOCAL_SANITIZE_DIAG, sprawdzanie nie jest włączone i nie są podawane 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')

Odkażacz adresów jądra

Podobnie jak w przypadku środków odkażających opartych na LLVM dla składników przestrzeni użytkownika, system Android zawiera narzędzie Kernel Address Sanitizer (KASAN). KASAN to połączenie modyfikacji jądra i czasu kompilacji, które skutkują instrumentalnym systemem, który pozwala na prostsze wykrywanie błędów i analizę przyczyn źródłowych.

KASAN potrafi wykryć wiele rodzajów naruszeń pamięci w jądrze. Może również wykrywać odczyty i zapisy poza zakresem na stosie, stercie i zmiennych globalnych, a także wykrywać użycie po zwolnieniu i podwójne zwolnienia.

Podobnie jak ASAN, KASAN wykorzystuje kombinację oprzyrządowania funkcji pamięci w czasie kompilacji i pamięci cienia do śledzenia dostępu do pamięci w czasie wykonywania. W KASAN, jedna ósma obszaru pamięci jądra jest dedykowana dla pamięci cienia, która określa, czy dostęp do pamięci jest prawidłowy, czy nie.

KASAN jest obsługiwany na architekturach x86_64 i arm64. Jest częścią jądra pierwotnego od wersji 4.0 i został przeniesiony do jąder opartych na systemie Android 3.18. KASAN został przetestowany na jądrach Androida skompilowanych za pomocą gcc w oparciu o 4.9.2.

Oprócz KASAN, kcov jest kolejną modyfikacją jądra, która jest przydatna do testowania. kcov został opracowany w celu umożliwienia testowania w jądrze opartego na pokryciu fuzz. Mierzy zasięg pod względem wejść wywołań systemowych i jest przydatny w przypadku systemów fuzzingowych, takich jak syzkaller .

Realizacja

Aby skompilować jądro z włączonymi KASAN i kcov, dodaj następujące flagi kompilacji do konfiguracji kompilacji jądra:

CONFIG_KASAN 
CONFIG_KASAN_INLINE 
CONFIG_TEST_KASAN 
CONFIG_KCOV 
CONFIG_SLUB 
CONFIG_SLUB_DEBUG 
CONFIG_CC_OPTIMIZE_FOR_SIZE

I usuwając następujące elementy:

CONFIG_SLUB_DEBUG_ON 
CONFIG_SLUB_DEBUG_PANIC_ON 
CONFIG_KASAN_OUTLINE 
CONFIG_KERNEL_LZ4

Następnie skompiluj i sflashuj jądro jak zwykle. Jądro KASAN jest znacznie większe niż oryginał. Jeśli dotyczy, zmodyfikuj parametry rozruchu i ustawienia programu ładującego, aby wziąć to pod uwagę.

Po flashowaniu jądra sprawdź dzienniki startowe jądra, aby sprawdzić, czy KASAN jest włączony i działa. Jądro uruchomi się z informacjami o mapie pamięci dla KASAN, takimi jak:

...
[    0.000000] c0      0 Virtual kernel memory layout:
[    0.000000] c0      0     kasan   : 0xffffff8000000000 - 0xffffff9000000000   (    64 GB)
[    0.000000] c0      0     vmalloc : 0xffffff9000010000 - 0xffffffbdbfff0000   (   182 GB)
[    0.000000] c0      0     vmemmap : 0xffffffbdc0000000 - 0xffffffbfc0000000   (     8 GB maximum)
[    0.000000] c0      0               0xffffffbdc0000000 - 0xffffffbdc3f95400   (    63 MB actual)
[    0.000000] c0      0     PCI I/O : 0xffffffbffa000000 - 0xffffffbffb000000   (    16 MB)
[    0.000000] c0      0     fixed   : 0xffffffbffbdfd000 - 0xffffffbffbdff000   (     8 KB)
[    0.000000] c0      0     modules : 0xffffffbffc000000 - 0xffffffc000000000   (    64 MB)
[    0.000000] c0      0     memory  : 0xffffffc000000000 - 0xffffffc0fe550000   (  4069 MB)
[    0.000000] c0      0       .init : 0xffffffc001d33000 - 0xffffffc001dce000   (   620 KB)
[    0.000000] c0      0       .text : 0xffffffc000080000 - 0xffffffc001d32284   ( 29385 KB)
...

A tak będzie wyglądał błąd:

[   18.539668] c3      1 ==================================================================
[   18.547662] c3      1 BUG: KASAN: null-ptr-deref on address 0000000000000008
[   18.554689] c3      1 Read of size 8 by task swapper/0/1
[   18.559988] c3      1 CPU: 3 PID: 1 Comm: swapper/0 Tainted: G        W      3.18.24-xxx #1
[   18.569275] c3      1 Hardware name: Android Device
[   18.577433] c3      1 Call trace:
[   18.580739] c3      1 [<ffffffc00008b32c>] dump_backtrace+0x0/0x2c4
[   18.586985] c3      1 [<ffffffc00008b600>] show_stack+0x10/0x1c
[   18.592889] c3      1 [<ffffffc001481194>] dump_stack+0x74/0xc8
[   18.598792] c3      1 [<ffffffc000202ee0>] kasan_report+0x11c/0x4d0
[   18.605038] c3      1 [<ffffffc00020286c>] __asan_load8+0x20/0x80
[   18.611115] c3      1 [<ffffffc000bdefe8>] android_verity_ctr+0x8cc/0x1024
[   18.617976] c3      1 [<ffffffc000bcaa2c>] dm_table_add_target+0x3dc/0x50c
[   18.624832] c3      1 [<ffffffc001bdbe60>] dm_run_setup+0x50c/0x678
[   18.631082] c3      1 [<ffffffc001bda8c0>] prepare_namespace+0x44/0x1ac
[   18.637676] c3      1 [<ffffffc001bda170>] kernel_init_freeable+0x328/0x364
[   18.644625] c3      1 [<ffffffc001478e20>] kernel_init+0x10/0xd8
[   18.650613] c3      1 ==================================================================

Dodatkowo, jeśli moduły są włączone w twoim jądrze, możesz załadować moduł jądra test_kasan do dalszych testów. Moduł próbuje uzyskać dostęp do pamięci poza granicami i korzystać z niej po zwolnieniu i jest przydatny w testowaniu KASAN na urządzeniu docelowym.