В Arm v9 представлено расширение Arm Memory Tagging Extension (MTE), аппаратная реализация помеченной памяти.
На высоком уровне MTE помечает каждое выделение/освобождение памяти дополнительными метаданными. MTE присваивает метку ячейке памяти, которая затем может быть связана с указателями, ссылающимися на эту ячейку. Во время выполнения ЦП проверяет соответствие меток указателя и метаданных при каждой загрузке и сохранении.
В Android 12 распределитель памяти в куче ядра и пользовательского пространства может дополнять каждое выделение метаданными. Это помогает обнаруживать ошибки использования памяти после освобождения и переполнения буфера, которые являются наиболее распространенными источниками ошибок безопасности памяти в наших кодовых базах.
режимы работы MTE
MTE имеет три режима работы:
- Синхронный режим (SYNC)
- Асинхронный режим (ASYNC)
- Асимметричный режим (ASYMM)
Синхронный режим (SYNC)
Этот режим оптимизирован для обеспечения корректного обнаружения ошибок, а не для повышения производительности, и может использоваться в качестве точного инструмента обнаружения ошибок, когда допустимы более высокие накладные расходы на производительность. При включении MTE SYNC выступает в качестве меры по снижению рисков безопасности. При несоответствии тегов процессор немедленно прерывает выполнение и завершает процесс с кодом SIGSEGV ( SEGV_MTESERR ) и полной информацией об обращении к памяти и адресе ошибки.
Мы рекомендуем использовать этот режим во время тестирования в качестве альтернативы HWASan/KASAN или в производственной среде, когда целевой процесс представляет собой уязвимую поверхность для атак. Кроме того, если режим ASYNC выявил наличие ошибки, точный отчет об ошибке можно получить, используя API среды выполнения для переключения выполнения в режим SYNC.
При работе в режиме синхронизации (SYNC) распределитель памяти Android записывает трассировки стека для всех выделений и освобождений памяти и использует их для составления более подробных отчетов об ошибках, включающих объяснение ошибки памяти, такой как использование памяти после освобождения (use-after-free) или переполнение буфера (buffer-overflow), а также трассировки стека соответствующих событий памяти. Такие отчеты предоставляют больше контекстной информации и упрощают отслеживание и исправление ошибок.
Асинхронный режим (ASYNC)
Этот режим оптимизирован для повышения производительности, а не точности отчетов об ошибках, и может использоваться для обнаружения ошибок безопасности памяти с минимальными накладными расходами. При несоответствии тегов процессор продолжает выполнение до ближайшего события ядра (например, системного вызова или прерывания таймера), где он завершает процесс с кодом SIGSEGV (код SEGV_MTEAERR ), не регистрируя адрес ошибки или обращение к памяти.
Мы рекомендуем использовать этот режим в производственной среде на хорошо протестированных кодовых базах, где, как известно, низкая плотность ошибок, связанных с безопасностью памяти, что достигается за счет использования режима SYNC во время тестирования.
Асимметричный режим (ASYMM)
Дополнительная функция в Arm v8.7-A, асимметричный режим MTE, обеспечивает синхронную проверку чтения из памяти и асинхронную проверку записи в память с производительностью, аналогичной режиму ASYNC. В большинстве случаев этот режим является улучшением по сравнению с режимом ASYNC, и мы рекомендуем использовать его вместо ASYNC, когда он доступен.
По этой причине ни один из описанных ниже API не упоминает асимметричный режим. Вместо этого операционную систему можно настроить на постоянное использование асимметричного режима при запросе асинхронной обработки. Дополнительную информацию см. в разделе «Настройка предпочтительного уровня MTE для конкретного ЦП» .
MTE в пользовательском пространстве
В следующих разделах описывается, как включить MTE для системных процессов и приложений. По умолчанию MTE отключен, если для конкретного процесса не задан один из указанных ниже параметров (см. ниже, для каких компонентов включен MTE).
Включите MTE с помощью системы сборки.
Параметр MTE, являющийся свойством, действующим на весь процесс, управляется настройкой основного исполняемого файла, заданной во время сборки. Следующие параметры позволяют изменять эту настройку для отдельных исполняемых файлов или для целых подкаталогов в дереве исходного кода. Эта настройка игнорируется для библиотек или любой цели, которая не является ни исполняемым файлом, ни тестом.
1. Включение MTE в Android.bp ( пример ) для конкретного проекта:
| Режим MTE | Параметр |
|---|---|
| Асинхронный MTE | sanitize: {
memtag_heap: true,
} |
| Синхронный МТЕ | sanitize: {
memtag_heap: true,
diag: {
memtag_heap: true,
},
} |
или в файле Android.mk:
| Режим MTE | Параметр |
|---|---|
Asynchronous MTE | LOCAL_SANITIZE := memtag_heap |
Synchronous MTE | LOCAL_SANITIZE := memtag_heapLOCAL_SANITIZE_DIAG := memtag_heap |
2. Включение MTE для подкаталога в исходном дереве с помощью переменной произведения:
| режим MTE | Включить список | Список исключений |
|---|---|---|
| асинхронный | PRODUCT_MEMTAG_HEAP_ASYNC_INCLUDE_PATHS MEMTAG_HEAP_ASYNC_INCLUDE_PATHS | PRODUCT_MEMTAG_HEAP_EXCLUDE_PATHS MEMTAG_HEAP_EXCLUDE_PATHS |
| синхронизация | PRODUCT_MEMTAG_HEAP_SYNC_INCLUDE_PATHS MEMTAG_HEAP_SYNC_INCLUDE_PATHS |
или
| Режим MTE | Параметр |
|---|---|
| Асинхронный MTE | MEMTAG_HEAP_ASYNC_INCLUDE_PATHS |
| Синхронный МТЕ | MEMTAG_HEAP_SYNC_INCLUDE_PATHS |
или указав путь исключения для исполняемого файла:
| Режим MTE | Параметр |
|---|---|
| Асинхронный MTE | PRODUCT_MEMTAG_HEAP_EXCLUDE_PATHS MEMTAG_HEAP_EXCLUDE_PATHS |
| Синхронный МТЕ |
Например (аналогично использованию PRODUCT_CFI_INCLUDE_PATHS )
PRODUCT_MEMTAG_HEAP_SYNC_INCLUDE_PATHS=vendor/$(vendor)
PRODUCT_MEMTAG_HEAP_EXCLUDE_PATHS=vendor/$(vendor)/projectA \
vendor/$(vendor)/projectBВключите MTE с помощью системных свойств.
Указанные выше параметры сборки можно переопределить во время выполнения, установив следующее системное свойство:
arm64.memtag.process.<basename> = (off|sync|async) Где basename обозначает базовое имя исполняемого файла.
Например, чтобы настроить /system/bin/ping или /data/local/tmp/ping на использование асинхронного MTE, используйте adb shell setprop arm64.memtag.process.ping async .
Включите MTE с помощью переменной среды.
Ещё один способ переопределить параметры сборки для нативных процессов (не приложений) — определить переменную среды: MEMTAG_OPTIONS=(off|sync|async) Если определены и переменная среды, и системное свойство, приоритет имеет переменная.
Включите MTE для приложений
Если не указано иное, MTE отключен по умолчанию, но приложения, желающие использовать MTE, могут сделать это, установив android:memtagMode в теге <application> или <process> в файле AndroidManifest.xml .
android:memtagMode=(off|default|sync|async) Если этот атрибут установлен в теге <application> , он влияет на все процессы, используемые приложением, и может быть переопределен для отдельных процессов путем установки тега <process> .
Для экспериментов можно использовать изменения совместимости , чтобы установить значение по умолчанию для атрибута memtagMode для приложения, которое не указывает никакого значения в манифесте (или указывает default ).
Эти параметры находятся в меню глобальных настроек : Система > Дополнительно > Параметры разработчика > Изменения совместимости приложений . Установка параметра NATIVE_MEMTAG_ASYNC или NATIVE_MEMTAG_SYNC включает MTE для конкретного приложения. В качестве альтернативы, это можно установить с помощью команды am следующим образом:
$ adb shell am compat enable NATIVE_MEMTAG_[A]SYNC my.app.nameСоздайте образ системы MTE.
Мы настоятельно рекомендуем включить MTE для всех нативных бинарных файлов во время разработки и запуска. Это помогает выявлять ошибки безопасности памяти на ранних стадиях и обеспечивает реалистичное покрытие пользовательского опыта, если включено в тестовых сборках.
Мы настоятельно рекомендуем включать MTE в синхронном режиме для всех нативных бинарных файлов во время разработки.
SANITIZE_TARGET=memtag_heap SANITIZE_TARGET_DIAG=memtag_heap m Как и любая другая переменная в системе сборки, SANITIZE_TARGET может использоваться в качестве переменной окружения или параметра make (например, в файле product.mk ).
Это включает MTE для всех нативных процессов, но не для приложений (которые являются форками zygote64 ), для которых MTE можно включить, следуя инструкциям в разделе «Включение MTE для приложений» .
Настройте предпочтительный уровень MTE для конкретного процессора.
На некоторых процессорах производительность MTE в асинхронном или даже синхронном режимах может быть аналогична производительности в асинхронном режиме. Это делает целесообразным включение более строгих проверок на таких процессорах, когда запрашивается менее строгий режим проверки, чтобы получить преимущества обнаружения ошибок, обеспечиваемые более строгими проверками, без снижения производительности.
По умолчанию процессы, настроенные на работу в асинхронном режиме, выполняются в асинхронном режиме на всех процессорах. Чтобы настроить ядро на выполнение этих процессов в синхронном режиме на конкретных процессорах, значение sync необходимо записать в запись sysfs /sys/devices/system/cpu/cpu<N>/mte_tcf_preferred при загрузке. Это можно сделать с помощью скрипта инициализации. Например, чтобы настроить процессоры 0-1 на выполнение процессов в асинхронном режиме в синхронном режиме, а процессоры 2-3 — в асинхронном режиме, в раздел init скрипта инициализации поставщика можно добавить следующее:
write /sys/devices/system/cpu/cpu0/mte_tcf_preferred sync write /sys/devices/system/cpu/cpu1/mte_tcf_preferred sync write /sys/devices/system/cpu/cpu2/mte_tcf_preferred asymm write /sys/devices/system/cpu/cpu3/mte_tcf_preferred asymm
В сообщениях об ошибках памяти, полученных в результате выполнения процессов в асинхронном режиме (SYNC), содержится точная трассировка стека, указывающая на местоположение ошибки памяти. Однако они не включают трассировку стека выделения или освобождения памяти. Эти трассировки стека доступны только в том случае, если процесс настроен на работу в синхронном режиме.
int mallopt(M_THREAD_DISABLE_MEM_INIT, level) где level равен 0 или 1.
Отключает инициализацию памяти в функции malloc и предотвращает изменение тегов памяти, если это не необходимо для корректной работы.
int mallopt(M_MEMTAG_TUNING, level) где level :
-
M_MEMTAG_TUNING_BUFFER_OVERFLOW -
M_MEMTAG_TUNING_UAF
Выбирает стратегию распределения тегов.
- Значение по умолчанию —
M_MEMTAG_TUNING_BUFFER_OVERFLOW. -
M_MEMTAG_TUNING_BUFFER_OVERFLOWобеспечивает детерминированное обнаружение ошибок переполнения и недополнения линейного буфера путем присвоения различных значений тегов соседним выделениям памяти. В этом режиме вероятность обнаружения ошибок использования памяти после освобождения несколько снижена, поскольку для каждого адреса памяти доступна только половина возможных значений тегов. MTE не может обнаружить переполнение в пределах одного и того же блока тегов (16-байтовый выровненный фрагмент) и может пропустить небольшие переполнения даже в этом режиме. Такое переполнение не может быть причиной повреждения памяти, поскольку память в пределах одного блока никогда не используется для нескольких выделений. -
M_MEMTAG_TUNING_UAFпозволяет независимо рандомизировать теги для обеспечения равномерной вероятности обнаружения как пространственных (переполнение буфера), так и временных (использование памяти после освобождения) ошибок примерно в 93%.
Помимо описанных выше API, опытным пользователям может быть полезно знать следующее:
- Установка аппаратного регистра
PSTATE.TCOможет временно отключить проверку тегов ( пример ). Например, при копировании диапазона памяти с неизвестным содержимым тегов или при устранении узкого места в производительности в цикле интенсивного использования памяти. - При использовании
M_HEAP_TAGGING_LEVEL_SYNCобработчик сбоев системы предоставляет дополнительную информацию, такую как трассировки стека выделения и освобождения памяти. Эта функциональность требует доступа к битам тегов и включается путем передачи флагаSA_EXPOSE_TAGBITSпри установке обработчика сигналов. Любой программе, которая устанавливает свой собственный обработчик сигналов и делегирует неизвестные сбои системному обработчику, рекомендуется поступать аналогично.
MTE в ядре
Чтобы включить ускорение KASAN с помощью MTE для ядра, настройте ядро с CONFIG_KASAN=y и CONFIG_KASAN_HW_TAGS=y . Эти параметры включены по умолчанию в ядрах GKI, начиная с Android 12-5.10 .
Это можно контролировать при загрузке системы, используя следующие аргументы командной строки:
-
kasan=[on|off]- включить или выключить KASAN (по умолчанию:on) -
kasan.mode=[sync |async ]- выберите синхронный или асинхронный режим (по умолчанию:sync) -
kasan.stacktrace=[on|off]- следует ли собирать трассировки стека (по умолчанию:on)- Для сбора трассировки стека также требуется
stack_depot_disable=off.
- Для сбора трассировки стека также требуется
-
kasan.fault=[report|panic]— следует ли только выводить отчет или также вызывать панику ядра (по умолчанию:report). Независимо от этого параметра, проверка тегов отключается после первой зарегистрированной ошибки.
Рекомендуемое использование
Мы настоятельно рекомендуем использовать режим SYNC во время запуска, разработки и тестирования. Эту опцию следует включить глобально для всех процессов с помощью переменной среды или системы сборки . В этом режиме ошибки обнаруживаются на ранних этапах разработки, кодовая база стабилизируется быстрее, и исключаются затраты на обнаружение ошибок на более поздних этапах производства.
Мы настоятельно рекомендуем использовать асинхронный режим в производственной среде. Это обеспечивает инструмент с низкими накладными расходами для обнаружения уязвимостей в безопасности памяти в процессе, а также дополнительную многоуровневую защиту. После обнаружения уязвимости разработчик может использовать API среды выполнения для переключения в синхронный режим и получения точной трассировки стека от выборки пользователей.
Мы настоятельно рекомендуем настроить предпочтительный уровень MTE для SoC, специфичный для конкретного процессора. Асимметричный режим обычно имеет те же характеристики производительности, что и асинхронный, и почти всегда предпочтительнее его. Небольшие ядра с последовательным выполнением инструкций часто демонстрируют схожую производительность во всех трех режимах и могут быть настроены на предпочтение синхронного режима.
Разработчикам следует проверять наличие сбоев, используя файлы /data/tombstones , logcat или отслеживая ошибки, возникающие у конечных пользователей, в конвейере DropboxManager от стороннего поставщика. Дополнительную информацию об отладке нативного кода Android см. здесь .
Компоненты платформы с поддержкой MTE
В Android 12 ряд критически важных для безопасности системных компонентов используют MTE ASYNC для обнаружения сбоев, вызванных действиями конечного пользователя, и в качестве дополнительного уровня многоуровневой защиты. К этим компонентам относятся:
- Сетевые демоны и утилиты (за исключением
netd) - Bluetooth, SecureElement, NFC HAL и системные приложения
- демон
statsd -
system_server -
zygote64(чтобы позволить приложениям добровольно использовать MTE)
Эти цели были выбраны на основе следующих критериев:
- Привилегированный процесс (определяемый как процесс, имеющий доступ к чему-либо, чего не имеет домен SELinux unprivileged_app)
- Обрабатывает недостоверные входные данные ( Правило двух ).
- Допустимое замедление производительности (замедление не приводит к заметной для пользователя задержке)
Мы призываем поставщиков включать MTE в производство для большего числа компонентов, следуя указанным выше критериям. В процессе разработки мы рекомендуем тестировать эти компоненты в режиме синхронизации (SYNC), чтобы выявлять легко исправимые ошибки и оценивать влияние асинхронного режима (ASYNC) на их производительность.
Мы планируем расширить список системных компонентов, на которых поддерживается MTE, руководствуясь характеристиками производительности будущих аппаратных разработок.