AddressSanitizer (ASan) — это быстрый инструмент на основе компилятора для обнаружения ошибок памяти в машинном коде.
ASan обнаруживает:
- Переполнение/опустошение буфера стека и кучи
- Использование кучи после бесплатного
- Использование стека вне области действия
- Двойной бесплатный/дикий бесплатный
ASan работает как на 32-битной, так и на 64-битной ARM, а также на x86 и x86-64. Накладные расходы процессора ASan примерно в 2 раза выше, накладные расходы на размер кода составляют от 50% до 2х, а также большие накладные расходы на память (в зависимости от ваших шаблонов распределения, но порядка 2х).
Android 10 и основная ветвь AOSP на AArch64 поддерживают ASan с аппаратным ускорением (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
. На данный момент игнорируйте цель app_process__asan
в том же файле (если она все еще существует в то время, когда вы читаете это).
Отредактируйте раздел service zygote
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 и перезагрузка.
Использование свойства обертки
Подход, описанный в предыдущем разделе, помещает 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
в начало пути поиска динамической библиотеки. Таким образом, библиотеки с инструментами ASan из /system/lib/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
не используется для SANITIZE_TARGETSANITIZE_TARGET=address
Подобные исполняемые файлы пропускаются в сборке SANITIZE_TARGET
, а версия, полученная при первом вызове make, остается в /system/bin
.
Такие библиотеки строятся без ASan. Они могут содержать некоторый код ASan из статических библиотек, от которых они зависят.