AddressSanitizer (ASan) — быстрый инструмент на основе компилятора для обнаружения ошибок памяти в машинном коде.
ASan обнаруживает:
- Переполнение/незаполнение буфера стека и кучи
- Использование кучи после освобождения
- Использование стека вне области действия
- Двойной бесплатный/дикий бесплатный
ASan работает как на 32-битных, так и на 64-битных ARM, а также на платформах x86 и x86-64. ASan потребляет примерно вдвое больше ресурсов процессора, накладные расходы на размер кода — от 50% до 2x, а также потребляет значительно больше памяти (в зависимости от ваших шаблонов распределения, но порядка 2x).
Android 10 и последняя версия AOSP на базе AArch64 поддерживают Hardware-assisted AddressSanitizer (HWASan) — аналогичный инструмент с меньшим потреблением оперативной памяти и более широким спектром обнаруживаемых ошибок. HWASan обнаруживает использование стека после возврата, в дополнение к ошибкам, обнаруженным ASan.
HWASan имеет схожие накладные расходы на процессор и размер кода, но значительно меньшие накладные расходы на ОЗУ (15%). HWASan недетерминирован. Существует всего 256 возможных значений тегов, поэтому вероятность пропуска любой ошибки составляет 0,4%. В HWASan нет красных зон ограниченного размера, как в ASan, для обнаружения переполнений и карантина ограниченной емкости для обнаружения использования после освобождения, поэтому для HWASan не имеет значения, насколько велико переполнение или как давно была освобождена память. Это делает HWASan лучше ASan. Подробнее об устройстве HWASan или об использовании HWASan на Android можно узнать здесь.
ASan обнаруживает переполнения стека/глобального хранилища в дополнение к переполнению кучи и работает быстро с минимальными затратами памяти.
В этом документе описывается, как собрать и запустить часть или все приложения Android с помощью ASan. Если вы создаёте приложение SDK/NDK с помощью ASan, см. раздел Address Sanitizer .
Очистка отдельных исполняемых файлов с помощью 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 root
adb 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, быстрая загрузка через flash и перезагрузка.
Используйте свойство wrap
Подход, описанный в предыдущем разделе, внедряет ASan в каждое приложение в системе (фактически, в каждый потомок процесса Zygote). ASan позволяет запустить только одно (или несколько) приложений, что позволяет компенсировать некоторые затраты памяти за счёт более медленного запуска приложения.
Это можно сделать, запустив приложение со свойством wrap.
В следующем примере приложение Gmail запускается под управлением ASan:
adb root
adb shell setenforce 0 # disable SELinux
adb shell setprop wrap.com.google.android.gm "asanwrapper"
В этом контексте asanwrapper
переписывает /system/bin/app_process
в /system/bin/asan/app_process
, который собран с помощью ASan. Он также добавляет /system/lib/asan
в начало пути поиска динамических библиотек. Таким образом, библиотеки из /system/lib/asan
инструментированные ASan, имеют приоритет перед обычными библиотеками из /system/lib
при запуске с asanwrapper
.
При обнаружении ошибки приложение аварийно завершает работу, а отчет записывается в журнал.
SANITIZE_TARGET
В Android 7.0 и более поздних версиях реализована поддержка сборки всей платформы Android с помощью ASan одновременно. (Если вы собираете версию выше Android 9, лучшим выбором будет HWASan.)
Выполните следующие команды в том же дереве сборки.
make -j42
SANITIZE_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:=false
цели -
LOCAL_SANITIZE:=false
не обработаны ASan дляSANITIZE_TARGET=address
Подобные исполняемые файлы пропускаются в сборке SANITIZE_TARGET
, а версия из первого вызова make остается в /system/bin
.
Подобные библиотеки создаются без ASan. Они могут содержать часть ASan-кода из статических библиотек, от которых зависят.