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

В Arm v9 представлено расширение тегирования памяти Arm (MTE), аппаратная реализация тегированной памяти.

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

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

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

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

  • Синхронный режим (SYNC)
  • Асинхронный режим (ASYNC)
  • Асимметричный режим (ASYMM)

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

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

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

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

Асинхронный режим (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 ( пример ), для конкретного проекта:

Режим МТЕ Параметр
Асинхронный МТЕ
  sanitize: {
  memtag_heap: true,
  }
Синхронный МТЕ
  sanitize: {
  memtag_heap: true,
  diag: {
  memtag_heap: true,
  },
  }

или в Android.mk:

Режим МТЕ Параметр
Asynchronous MTE LOCAL_SANITIZE := memtag_heap
Synchronous MTE LOCAL_SANITIZE := memtag_heap
LOCAL_SANITIZE_DIAG := memtag_heap

2. Включение 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

или

Режим МТЕ Параметр
Асинхронный МТЕ MEMTAG_HEAP_ASYNC_INCLUDE_PATHS
Синхронный МТЕ MEMTAG_HEAP_SYNC_INCLUDE_PATHS

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

Режим МТЕ Параметр
Асинхронный МТЕ 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 ).
Их можно найти в 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. Это делает целесообразным включать более строгие проверки на этих процессорах, когда запрашивается менее строгий режим проверки, чтобы получить преимущества обнаружения ошибок от более строгих проверок без снижения производительности.
По умолчанию процессы, настроенные для работы в режиме ASYNC, будут работать в режиме ASYNC на всех процессорах. Чтобы настроить ядро ​​для запуска этих процессов в режиме SYNC на определенных процессорах, значение sync необходимо записать в запись sysfs /sys/devices/system/cpu/cpu<N>/mte_tcf_preferred во время загрузки. Это можно сделать с помощью сценария инициализации. Например, чтобы настроить ЦП 0–1 для запуска процессов режима ASYNC в режиме SYNC, а ЦП 2–3 для использования работы в режиме ASYMM, в предложение 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

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

МТЕ в ядре

Чтобы включить 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. Режим Asymm обычно имеет те же характеристики производительности, что и ASYNC, и почти всегда предпочтительнее его. Небольшие ядра, расположенные по порядку, часто демонстрируют одинаковую производительность во всех трех режимах, и их можно настроить на предпочтение синхронизации.

Разработчикам следует проверять наличие сбоев, проверяя /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 на их производительность.
В будущем Android планирует расширить список системных компонентов, на которых включен MTE, исходя из характеристик производительности будущих аппаратных разработок.