Расширение тегов памяти Arm

Arm v9 представляет Arm Memory Tagged Extension (MTE), аппаратную реализацию тегированной памяти.

На высоком уровне MTE помечает каждое выделение/освобождение памяти дополнительными метаданными. Он присваивает тег ячейке памяти, которую затем можно связать с указателями, ссылающимися на эту ячейку памяти. Во время выполнения ЦП проверяет соответствие указателя и тегов метаданных при каждой загрузке и сохранении.

В Android 12 распределитель кучи памяти ядра и пользовательского пространства может дополнять каждое выделение метаданными. Это помогает обнаруживать ошибки использования после освобождения и переполнения буфера, которые являются наиболее частыми источниками ошибок безопасности памяти в наших кодовых базах.

Режимы работы МТЭ

МТЭ имеет три режима работы:

  • Синхронный режим (SYNC)
  • Асинхронный режим (АСИНХР.)
  • Асимметричный режим (ASYMM)

Синхронный режим (SYNC)

Этот режим оптимизирован для корректности обнаружения ошибок, а не для производительности, и может использоваться в качестве точного инструмента обнаружения ошибок, когда приемлемы более высокие потери производительности. При включении MTE SYNC действует как средство защиты. При несовпадении тегов процессор немедленно прерывает выполнение и завершает процесс с SIGSEGV (код SEGV_MTESERR ) и полной информацией о доступе к памяти и ошибочном адресе.

Мы рекомендуем использовать этот режим во время тестирования в качестве альтернативы HWAsan/KASAN или в рабочей среде, когда целевой процесс представляет собой уязвимую поверхность для атаки. Кроме того, когда режим ASYNC указывает на наличие ошибки, можно получить точный отчет об ошибке, используя API среды выполнения для переключения выполнения в режим SYNC.

При работе в режиме SYNC распределитель Android записывает трассировку стека для всех выделений и освобождений и использует их для предоставления более качественных отчетов об ошибках, которые включают объяснение ошибки памяти, например использование после освобождения или переполнение буфера, и стек следы соответствующих событий памяти. Такие отчеты предоставляют больше контекстной информации и упрощают отслеживание и исправление ошибок.

Асинхронный режим (АСИНХР.)

Этот режим оптимизирован для повышения производительности, а не точности отчетов об ошибках, и может использоваться для обнаружения с низкими издержками ошибок безопасности памяти.
При несовпадении тегов процессор продолжает выполнение до ближайшей записи ядра (например, системного вызова или прерывания таймера), где он завершает процесс с сигналом 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,
  }
Синхронный MTE
  sanitize: {
  memtag_heap: true,
  diag: {
  memtag_heap: true,
  },
  }

или в Android.mk:

Режим MTE Параметр
Asynchronous MTE LOCAL_SANITIZE := memtag_heap
Synchronous MTE LOCAL_SANITIZE := memtag_heap
LOCAL_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
Синхронный MTE MEMTAG_HEAP_SYNC_INCLUDE_PATHS

или указав путь исключения исполняемого файла:

Режим MTE Параметр
Асинхронный MTE PRODUCT_MEMTAG_HEAP_EXCLUDE_PATHS MEMTAG_HEAP_EXCLUDE_PATHS
Синхронный MTE

Пример (аналогично использованию PRODUCT_CFI_INCLUDE_PATHS )

  RODUCT_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 ).
Их можно найти в разделе « System > Advanced > Developer options > App Compatibility Changes » в меню глобальных настроек. Параметр 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 в режимах ASYMM или даже SYNC может быть аналогична производительности ASYNC. Это делает целесообразным включать более строгие проверки для этих ЦП, когда запрашивается менее строгий режим проверки, чтобы получить преимущества обнаружения ошибок более строгих проверок без снижения производительности.
По умолчанию процессы, настроенные для работы в асинхронном режиме, будут работать в асинхронном режиме на всех процессорах. Чтобы настроить ядро ​​для запуска этих процессов в режиме SYNC на определенных ЦП, значение sync должно быть записано в запись sysfs /sys/devices/system/cpu/cpu<N>/mte_tcf_preferred во время загрузки. Это можно сделать с помощью скрипта инициализации. Например, чтобы настроить ЦП 0–1 для запуска процессов режима ASYNC в режиме SYNC, а ЦП 2–3 — для запуска в режиме ASYMM, в предложение инициализации сценария инициализации поставщика можно добавить следующее:

  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

Захоронения процессов режима ASYNC, запущенных в режиме SYNC, будут содержать точную трассировку стека с указанием местоположения ошибки памяти. Однако они не будут включать трассировку стека выделения или освобождения. Эти трассировки стека доступны только в том случае, если процесс настроен для работы в режиме 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 во время настройки, разработки и тестирования. Эта опция должна быть включена глобально для всех процессов, использующих переменную среды или систему сборки . В этом режиме ошибки обнаруживаются на ранней стадии процесса разработки, кодовая база стабилизируется быстрее, а затраты на обнаружение ошибок на более поздних этапах производства избегаются.

Мы настоятельно рекомендуем использовать режим ASYNC в рабочей среде. Это обеспечивает инструмент с низкими издержками для обнаружения наличия ошибок безопасности памяти в процессе, а также для дополнительной глубокоэшелонированной защиты. Как только ошибка обнаружена, разработчик может использовать API среды выполнения, чтобы переключиться в режим SYNC и получить точную трассировку стека от выбранной группы пользователей.

Мы настоятельно рекомендуем настроить предпочтительный уровень MTE для конкретного процессора для SoC. Асимметричный режим обычно имеет те же характеристики производительности, что и ASYNC, и почти всегда предпочтительнее его. Небольшие упорядоченные ядра часто показывают одинаковую производительность во всех трех режимах, и их можно настроить так, чтобы они предпочитали SYNC.

Разработчики должны проверять наличие сбоев, проверяя /data/tombstones tombstones , logcat или отслеживая конвейер DropboxManager поставщика на предмет ошибок конечных пользователей. Дополнительные сведения об отладке нативного кода Android см. в информации здесь .

Компоненты платформы с поддержкой MTE

В Android 12 ряд системных компонентов, критически важных для безопасности, используют MTE ASYNC для обнаружения сбоев конечных пользователей и в качестве дополнительного уровня глубокоэшелонированной защиты. Эти компоненты:

  • Сетевые демоны и утилиты (за исключением netd )
  • Bluetooth, SecureElement, NFC HAL и системные приложения
  • statsd
  • system_server
  • zygote64 (чтобы разрешить приложениям использовать MTE)

Эти цели были выбраны на основе следующих критериев:

  • Привилегированный процесс (определяемый как процесс, имеющий доступ к чему-то, чего нет у домена SELinux непривилегированного_приложения)
  • Обрабатывает недостоверный ввод ( Правило двух )
  • Приемлемое замедление производительности (замедление не приводит к заметной для пользователя задержке)

Мы призываем поставщиков включить MTE в производство для большего количества компонентов, следуя указанным выше критериям. Во время разработки мы рекомендуем протестировать эти компоненты в режиме SYNC, чтобы обнаружить легко устраняемые ошибки и оценить влияние ASYNC на их производительность.
В будущем Android планирует расширить список системных компонентов, на которых работает MTE, исходя из характеристик производительности будущих аппаратных разработок.