Дезинфицирующие средства LLVM

LLVM, инфраструктура компилятора, используемая для сборки Android, содержит несколько компонентов, выполняющих статический и динамический анализ. Из этих компонентов дезинфицирующие средства, в частности AddressSanitizer и UndefinedBehaviorSanitizer, можно широко использовать для анализа Android. Дезинфицирующие средства — это компоненты инструментирования на основе компилятора, содержащиеся в файле external/compiler-rt, которые можно использовать во время разработки и тестирования для устранения ошибок и улучшения Android. Текущий набор дезинфицирующих средств Android может обнаруживать и диагностировать многие ошибки неправильного использования памяти и потенциально опасное неопределенное поведение.

Для сборок Android рекомендуется загружаться и работать с включенными дезинфицирующими средствами, такими как AddressSanitizer и UndefinedBehaviorSanitizer. На этой странице представлены AddressSanitizer, UndefinedBehaviorSanitizer и KernelAddressSanitizer, показано, как их можно использовать в системе сборки Android, и приведены примеры файлов Android.mk и Android.bp, которые создают собственные компоненты с включенными этими дезинфицирующими средствами.

АдресДезинфицирующее средство

AddressSanitizer (ASan) — это инструментальная возможность на основе компилятора, которая обнаруживает многие типы ошибок памяти в коде C/C++ во время выполнения. ASan может обнаруживать многие классы ошибок памяти, в том числе:

  • Неограниченный доступ к памяти
  • Двойной бесплатно
  • Использование после освобождения

Android позволяет использовать инструментарий ASan на уровне полной сборки и на уровне приложения с помощью asanwrapper.

AddressSanitizer сочетает инструментирование всех вызовов функций, связанных с памятью, включая alloca, malloc и free, а также заполнение всех переменных и выделенных областей памяти памятью, которая запускает обратный вызов ASan при чтении или записи.

Инструментарий позволяет ASan обнаруживать недопустимые ошибки использования памяти, в том числе двойное освобождение и использование после области действия, возврат и освобождение, в то время как заполнение области памяти обнаруживает чтение или запись за пределами границ. Если происходит чтение или запись в эту область заполнения, ASan перехватывает это и выводит информацию, помогающую диагностировать нарушение памяти, включая стек вызовов, карту теневой памяти, тип нарушения памяти, что было прочитано или записано, инструкция, вызвавшая ошибку. нарушение и содержимое памяти.

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

Иногда процесс обнаружения ошибок может показаться недетерминированным, особенно для ошибок, которые требуют специальной настройки или более сложных методов, таких как заполнение кучи или использование условий гонки. Многие из этих ошибок проявляются не сразу и могут проявляться в тысячах инструкций вдали от нарушения памяти, которое было фактической основной причиной. ASan обрабатывает все функции, связанные с памятью, и дополняет данные областями, доступ к которым невозможен без запуска обратного вызова ASan. Это означает, что нарушения памяти обнаруживаются в момент их возникновения, а не в ожидании повреждения, вызывающего сбой. Это чрезвычайно полезно при обнаружении ошибок и диагностике основных причин.

Чтобы убедиться, что ASAN работает на целевом устройстве, Android включил исполняемый файл asan_test. Исполняемый файл asan_test тестирует и проверяет функциональность ASAN на целевом устройстве, предоставляя диагностические сообщения со статусом каждого теста. При использовании сборки ASAN для Android по умолчанию она находится в /data/nativetest/asan_test/asan_test или /data/nativetest64/asan_test/asan_test .

UndefinedBehaviorSanitizer

UndefinedBehaviorSanitizer (UBSan) выполняет инструментирование во время компиляции для проверки различных типов неопределенного поведения. В то время как UBSan способен обнаруживать многие неопределенные поведения , Android поддерживает выравнивание, логическое значение, границы, перечисление, переполнение с плавающей запятой, деление с плавающей запятой на ноль, целочисленное деление на ноль, ненулевой атрибут, ноль, возврат, возвращает-ненулевой-атрибут, сдвиг-база, сдвиг-экспонента, переполнение целого числа со знаком, недостижимый, переполнение целого числа без знака и привязка vla. unsigned-integer-overflow, хотя технически это и не неопределенное поведение, включено в дезинфицирующее средство и используется во многих модулях Android, включая компоненты медиасервера, для устранения любых скрытых уязвимостей целочисленного переполнения.

Реализация

В системе сборки Android вы можете включить UBsan глобально или локально. Чтобы включить UBsan глобально, установите SANITIZE_TARGET в Android.mk. Чтобы включить UBSan на уровне каждого модуля, установите LOCAL_SANITIZE и укажите неопределенное поведение, которое вы хотите искать в Android.mk. Например:

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)

Система сборки Android еще не поддерживает столь подробную диагностику в файлах чертежей, как в make-файлах. Вот ближайший аналог, написанный в виде схемы (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
        },
    },

}

Ярлыки UBSan

В Android также есть два ярлыка, integer и default-ub , для одновременного включения набора дезинфицирующих средств. integer включает integer-divide-by-zero , signed-integer-overflow unsigned-integer-overflow . default-ub включает проверки, которые имеют минимальные проблемы с производительностью компилятора: bool, integer-divide-by-zero, return, return-nonnull-attribute, shift-exponent, unreachable и vla-bound. Целочисленный класс дезинфицирующего средства можно использовать с SANITIZE_TARGET и LOCAL_SANITIZE, тогда как default-ub можно использовать только с SANITIZE_TARGET.

Улучшенная отчетность об ошибках

Реализация UBsan в Android по умолчанию вызывает указанную функцию при обнаружении неопределенного поведения. По умолчанию эта функция отключена. Однако, начиная с октября 2016 года, UBsan на Android имеет дополнительную библиотеку времени выполнения, которая предоставляет более подробные отчеты об ошибках, включая тип обнаруженного неопределенного поведения, информацию о файлах и строках исходного кода. Чтобы включить этот отчет об ошибках с целочисленными проверками, добавьте в файл Android.mk следующее:

LOCAL_SANITIZE:=integer
LOCAL_SANITIZE_DIAG:=integer

Значение LOCAL_SANITIZE включает дезинфицирующее средство во время сборки. LOCAL_SANITIZE_DIAG включает режим диагностики для указанного дезинфицирующего средства. Можно установить LOCAL_SANITIZE и LOCAL_SANITIZE_DIAG на разные значения, но включены только проверки в LOCAL_SANITIZE. Если проверка не указана в LOCAL_SANITIZE, но указана в LOCAL_SANITIZE_DIAG, проверка не включается и диагностические сообщения не выдаются.

Вот пример информации, предоставляемой библиотекой времени выполнения 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')

Средство очистки адресов ядра

Подобно дезинфицирующим средствам на основе LLVM для компонентов пользовательского пространства, Android включает дезинфицирующее средство адреса ядра (KASAN). KASAN представляет собой комбинацию модификаций ядра и времени компиляции, в результате чего создается инструментированная система, которая упрощает обнаружение ошибок и анализ их первопричин.

KASAN может обнаруживать многие типы нарушений памяти в ядре. Он также может обнаруживать чтение и запись за пределами стека в стеке, куче и глобальных переменных, а также может обнаруживать использование после освобождения и двойное освобождение.

Подобно ASAN, KASAN использует комбинацию инструментария функций памяти во время компиляции и теневой памяти для отслеживания доступа к памяти во время выполнения. В KASAN восьмая часть пространства памяти ядра выделена для теневой памяти, которая определяет, является ли доступ к памяти действительным или нет.

KASAN поддерживается на архитектурах x86_64 и arm64. Он является частью исходного ядра с версии 4.0 и был перенесен в ядра на базе Android 3.18. KASAN был протестирован на ядрах Android, скомпилированных с помощью gcc на базе 4.9.2.

Помимо KASAN, еще одной модификацией ядра, полезной для тестирования, является kcov. kcov был разработан, чтобы обеспечить нечёткое тестирование в ядре с управляемым покрытием. Он измеряет покрытие с точки зрения входных данных системных вызовов и полезен с системами фаззинга, такими как syzkaller .

Реализация

Чтобы скомпилировать ядро ​​с включенными KASAN и kcov, добавьте следующие флаги сборки в конфигурацию сборки ядра:

CONFIG_KASAN 
CONFIG_KASAN_INLINE 
CONFIG_TEST_KASAN 
CONFIG_KCOV 
CONFIG_SLUB 
CONFIG_SLUB_DEBUG 
CONFIG_CC_OPTIMIZE_FOR_SIZE

И удалить следующее:

CONFIG_SLUB_DEBUG_ON 
CONFIG_SLUB_DEBUG_PANIC_ON 
CONFIG_KASAN_OUTLINE 
CONFIG_KERNEL_LZ4

Затем соберите и прошейте ядро, как обычно. Ядро KASAN значительно больше исходного. Если применимо, измените любые параметры загрузки и настройки загрузчика, чтобы учесть это.

После прошивки ядра проверьте журналы загрузки ядра, чтобы убедиться, что KASAN включен и работает. Ядро запустится с информацией о карте памяти для KASAN, такой как:

...
[    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)
...

А вот так будет выглядеть баг:

[   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 ==================================================================

Кроме того, если в вашем ядре включены модули, вы можете загрузить модуль ядра test_kasan для дальнейшего тестирования. Модуль пытается получить доступ к памяти за пределами памяти и использовать ее после освобождения и полезен при тестировании KASAN на целевом устройстве.