AddressSanitizer (ASan) — это быстрый инструмент на основе компилятора для обнаружения ошибок, связанных с памятью в нативном коде.
ASan обнаруживает:
- Переполнение/недополнение буфера стека и кучи
- Использование памяти после освобождения памяти
- Использование стека вне области видимости
- Двойной бесплатный/дикий бесплатный
ASan работает как на 32-битных, так и на 64-битных ARM-процессорах, а также на x86 и x86-64. Накладные расходы ASan на ЦП составляют примерно в 2 раза больше, накладные расходы на размер кода — от 50% до 2 раз больше, а также значительные накладные расходы на память (зависят от используемых шаблонов выделения памяти, но примерно в 2 раза больше).
Android 10 и последняя ветка релизов AOSP на архитектуре AArch64 поддерживают аппаратную проверку адресов (HWASan) , аналогичный инструмент с меньшими затратами оперативной памяти и более широким спектром обнаруживаемых ошибок. HWASan обнаруживает использование стека после возврата, в дополнение к ошибкам, обнаруженным ASan.
HWASan имеет схожие накладные расходы на ЦП и размер кода, но значительно меньшие накладные расходы на ОЗУ (15%). HWASan является недетерминированным алгоритмом. Существует всего 256 возможных значений тегов, поэтому вероятность пропустить любую ошибку составляет всего 0,4%. В HWASan отсутствуют ограниченные по размеру красные зоны ASan для обнаружения переполнения и ограниченный по емкости карантин для обнаружения использования памяти после освобождения, поэтому для HWASan не имеет значения, насколько велико переполнение или как давно была освобождена память. Это делает HWASan лучше, чем ASan. Вы можете узнать больше о дизайне HWASan или об использовании HWASan на Android .
ASan обнаруживает не только переполнение кучи, но и переполнение стека/глобальные переменные, при этом работает быстро и потребляет минимальное количество памяти.
В этом документе описывается, как собрать и запустить часть/все приложения Android с использованием ASan. Если вы разрабатываете приложение на основе SDK/NDK с использованием ASan, см. раздел «Санитаризация адресов» .
Очистка отдельных исполняемых файлов с помощью ASan
Добавьте LOCAL_SANITIZE:=address или sanitize: { address: true } в правило сборки исполняемого файла. Вы можете поискать в коде существующие примеры или найти другие доступные средства очистки данных.
При обнаружении ошибки ASan выводит подробный отчет как в стандартный поток вывода, так и в logcat , после чего завершает процесс с ошибкой.
Очистка общих библиотек с помощью ASan
В силу особенностей работы ASan, библиотека, собранная с использованием ASan, может использоваться только исполняемым файлом, также собранным с использованием ASan.
Для проверки работоспособности разделяемой библиотеки, используемой в нескольких исполняемых файлах, не все из которых собраны с помощью ASan, вам потребуется две копии этой библиотеки. Рекомендуемый способ сделать это — добавить следующее в файл Android.mk для соответствующего модуля:
LOCAL_SANITIZE:=address LOCAL_MODULE_RELATIVE_PATH := asan
Это поместит библиотеку в /system/lib/asan вместо /system/lib . Затем запустите исполняемый файл с помощью команды:
LD_LIBRARY_PATH=/system/lib/asan
Для системных демонов добавьте следующее в соответствующий раздел файла /init.rc или /init.$device$.rc .
setenv LD_LIBRARY_PATH /system/lib/asan
Убедитесь, что процесс использует библиотеки из /system/lib/asan если они присутствуют, прочитав /proc/$PID/maps . Если это не так, возможно, потребуется отключить SELinux:
adb rootadb shell setenforce 0# restart the process with adb shell kill $PID # if it is a system service, or may be adb shell stop; adb shell start.
Более точные трассировки стека
ASan использует быстрый механизм размотки стека на основе указателей кадров для записи трассировки стека для каждого события выделения и освобождения памяти в программе. Большая часть Android построена без указателей кадров. В результате вы часто получаете только один или два значимых кадра. Чтобы исправить это, либо пересоберите библиотеку с ASan (рекомендуется!), либо с:
LOCAL_CFLAGS:=-fno-omit-frame-pointer LOCAL_ARM_MODE:=arm
Или установите ASAN_OPTIONS=fast_unwind_on_malloc=0 в настройках среды выполнения. Последний вариант может быть очень ресурсоемким для процессора, в зависимости от нагрузки.
Символизация
Первоначально отчеты ASan содержат ссылки на смещения в бинарных файлах и разделяемых библиотеках. Существует два способа получения информации об исходном файле и строке:
- Убедитесь, что исполняемый файл
llvm-symbolizerприсутствует в/system/bin.llvm-symbolizerсобирается из исходных кодов вthird_party/llvm/tools/llvm-symbolizer. - Отфильтруйте отчет с помощью скрипта
external/compiler-rt/lib/asan/scripts/symbolize.py.
Второй подход может предоставить больше данных (то есть, расположение file:line ) благодаря наличию символизированных библиотек на хосте.
ASan в приложениях
ASan не может заглянуть в Java-код, но может обнаруживать ошибки в библиотеках JNI. Для этого необходимо собрать исполняемый файл с поддержкой ASan, в данном случае это /system/bin/app_process( 32|64 ) . Это включает ASan во всех приложениях на устройстве одновременно, что создает большую нагрузку, но устройство с 2 ГБ оперативной памяти должно справиться с этим.
Добавьте LOCAL_SANITIZE:=address к правилу сборки app_process в frameworks/base/cmds/app_process .
Отредактируйте раздел service zygote соответствующего файла system/core/rootdir/init.zygote( 32|64 ).rc , добавив следующие строки в блок строк с отступом, содержащий class main , также с отступом на ту же величину:
setenv LD_LIBRARY_PATH /system/lib/asan:/system/lib
setenv ASAN_OPTIONS allow_user_segv_handler=true
Сборка, синхронизация adb, быстрая загрузка через fastboot и перезагрузка.
Используйте свойство wrap.
Подход, описанный в предыдущем разделе, внедряет ASan в каждое приложение в системе (фактически, в каждый потомок процесса Zygote). Можно запустить только одно (или несколько) приложений с ASan, пожертвовав некоторым объемом памяти ради более медленного запуска приложения.
Это можно сделать, запустив приложение с помощью свойства wrap. Следующий пример запускает приложение Gmail в среде ASan:
adb rootadb shell setenforce 0 # disable SELinuxadb shell setprop wrap.com.google.android.gm "asanwrapper"
В этом контексте asanwrapper перезаписывает /system/bin/app_process на /system/bin/asan/app_process , который собран с использованием ASan. Он также добавляет /system/lib/asan в начало пути поиска динамических библиотек. Таким образом, библиотеки, инструментированные ASan, из /system/lib/asan имеют приоритет перед обычными библиотеками в /system/lib при запуске с asanwrapper .
Если обнаруживается ошибка, приложение аварийно завершает работу, и отчет записывается в журнал.
SANITIZE_TARGET
В Android 7.0 и более поздних версиях поддерживается одновременная сборка всей платформы Android с использованием ASan. (Если вы собираете версию выше Android 9, HWASan — лучший выбор.)
Выполните следующие команды в том же дереве сборки.
make -j42SANITIZE_TARGET=address make -j42
В этом режиме userdata.img содержит дополнительные библиотеки и также должен быть прошит в устройство. Используйте следующую команду в командной строке:
fastboot flash userdata && fastboot flashall
В результате сборки создаются два набора разделяемых библиотек: обычные в /system/lib (первый вызов make) и с инструментом ASan в /data/asan/lib (второй вызов make). Исполняемые файлы из второй сборки перезаписывают файлы из первой сборки. Исполняемые файлы с инструментом ASan получают другой путь поиска библиотек, который включает /data/asan/lib перед /system/lib благодаря использованию /system/bin/linker_asan в PT_INTERP .
Система сборки перезаписывает промежуточные каталоги объектов при изменении значения $SANITIZE_TARGET . Это приводит к пересборке всех целей, сохраняя при этом установленные бинарные файлы в каталоге /system/lib .
Некоторые цели невозможно собрать с помощью ASan:
- Статически скомпилированные исполняемые файлы
-
LOCAL_CLANG:=falsetargets -
LOCAL_SANITIZE:=falseне используется дляSANITIZE_TARGET=address
Исполняемые файлы подобного рода пропускаются при сборке SANITIZE_TARGET , и версия, полученная при первом вызове make, остается в /system/bin .
Подобные библиотеки создаются без ASan. Они могут содержать некоторый код ASan из статических библиотек, от которых они зависят.