AddressSanitizer (ASan) — это быстрый инструмент на основе компилятора для обнаружения ошибок памяти в машинном коде.
ASan обнаруживает:
- Переполнение/недополнение буфера стека и кучи
- Куча использования после бесплатного
- Использование стека за пределами области действия
- Двойное бесплатное/дикое бесплатное
ASan работает как на 32-битной, так и на 64-битной версии ARM, а также на x86 и x86-64. Накладные расходы ЦП ASan составляют примерно 2x, накладные расходы на размер кода составляют от 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 не может проникать в код 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
соответствующего файла 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 flash и перезагрузка.
Используйте свойство переноса
Подход, описанный в предыдущем разделе, включает 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
не являются ASan'd дляSANITIZE_TARGET=address
Подобные исполняемые файлы пропускаются в сборке SANITIZE_TARGET
, а версия из первого вызова make остается в /system/bin
.
Подобные библиотеки создаются без ASan. Они могут содержать некоторый код ASan из статических библиотек, от которых они зависят.