Podobnie jak w przypadku narzędzi do sprawdzania oparte na LLVM dla komponentów w przestrzeni użytkownika, Android zawiera narzędzie Kernel Address Sanitizer (KASan). KASan to połączenie modyfikacji jądra i czasu kompilacji, które powoduje powstanie zinstrumentowanego systemu, który umożliwia łatwiejsze wykrywanie błędów i analizę przyczyn.
KASan może wykrywać wiele rodzajów naruszeń pamięci w rdzeniu. Może też wykrywać odczyty i zapisy poza zakresem stosu, stosu dynamicznego oraz zmiennych globalnych, a także wykrywać użycie po zwalnianiu i podwójne zwalnianie.
Podobnie jak ASan, KASan używa kombinacji pomiaru funkcji pamięci w czasie kompilacji i pamieci zduplikowanej, aby śledzić dostęp do pamięci w czasie wykonywania. W KASan 1/8 miejsca w pamięci jądra jest zarezerwowana na pamięć cienia, która określa, czy dostęp do pamięci jest prawidłowy.
KASan jest obsługiwany w architekturach x86_64 i arm64. Od wersji 4.0 jest ona częścią upstreamowego jądra, a później została przeniesiona do jąder opartych na Androidzie 3.18.
Oprócz KASan kcov jest kolejną modyfikacją jądra, która jest przydatna do testowania. Została ona opracowana, aby umożliwić testowanie fuzzingowe z kierowaniem na pokrycie w jądrze. Mierzy pokrycie w ujęciu danych wejściowych wywołań systemowych i jest przydatny w przypadku systemów fuzzingu, takich jak syzkaller.
Implementacja
Aby skompilować jądro z włączonymi funkcjami KASan i kcov, dodaj do konfiguracji kompilacji jądra te flagi kompilacji:
CONFIG_KASAN CONFIG_KASAN_INLINE CONFIG_TEST_KASAN CONFIG_KCOV CONFIG_SLUB CONFIG_SLUB_DEBUG CONFIG_CC_OPTIMIZE_FOR_SIZE
oraz usunięcie:
CONFIG_SLUB_DEBUG_ON CONFIG_SLUB_DEBUG_PANIC_ON CONFIG_KASAN_OUTLINE CONFIG_KERNEL_LZ4
Następnie utwórz i zainstaluj ją w zwykły sposób. Kernel KASan jest znacznie większy niż oryginalny. W razie potrzeby zmodyfikuj parametry rozruchu i ustawienia bootloadera, aby uwzględnić tę opcję.
Po przeflashowaniu jądra sprawdź logi rozruchu jądra, aby sprawdzić, czy KASan jest włączony i działa. Rdzeń uruchomi się z informacjami mapy 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) ...
Oto jak 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 ==================================================================
Jeśli w jądrze są włączone moduły, możesz też wczytać moduł test_kasan kernel na potrzeby dalszych testów. Moduł próbuje uzyskać dostęp do pamięci poza granicami oraz użyć pamięci po wyzwoleniu. Jest on przydatny do sprawdzenia, czy na urządzeniu docelowym poprawnie włączono KASan.