A Arm v9 apresenta a Arm Memory Tagging Extension (MTE), uma implementação de hardware de memória marcada.
Em um nível alto, a MTE marca cada alocação/desalocação de memória com metadados adicionais. A MTE atribui uma tag a um local de memória, que pode ser associado a ponteiros que fazem referência a esse local. No tempo de execução, a CPU verifica se o ponteiro e as tags de metadados correspondem em cada carga e armazenamento.
No Android 12, o kernel e o alocador de memória de heap do espaço do usuário podem aumentar cada alocação com metadados. Isso ajuda a detectar bugs use-after-free e de estouro de buffer, que são a fonte mais comum de bugs de segurança de memória em nossas bases de código.
Modos de operação da MTE
A MTE tem três modos de operação:
- Modo síncrono (SYNC)
- Modo assíncrono (ASYNC)
- Modo assimétrico (ASYMM)
Modo síncrono (SYNC)
Esse modo é otimizado para a correção da detecção de bugs em relação ao desempenho e
pode ser usado como uma ferramenta precisa de detecção de bugs, quando uma sobrecarga maior for
aceitável. Quando ativada, a MTE SYNC atua como uma mitigação de segurança.
Em casos de incompatibilidade de tag, o processador interrompe a execução imediatamente e
encerra o processo com SIGSEGV (código
SEGV_MTESERR) e informações completas sobre o acesso à memória e o
endereço com falha.
Recomendamos usar esse modo durante os testes como uma alternativa ao HWASan/KASAN ou na produção quando o processo de destino representa uma superfície de ataque vulnerável. Além disso, quando o modo ASYNC indica a presença de um bug, é possível gerar um relatório preciso do bug usando as APIs de execução para alternar a execução para o modo SYNC.
Ao executar no modo SYNC, o alocador do Android registra stack traces para todas as alocações e desalocações e os usa para fornecer relatórios de erros melhores que incluem uma explicação de um erro de memória, como use-after-free ou de estouro de buffer, e os stack traces dos eventos de memória relevantes. Esses relatórios fornecem mais informações contextuais e facilitam o rastreamento e a correção de bugs.
Modo assíncrono (ASYNC)
Esse modo é otimizado para desempenho acima da precisão de relatórios de bugs e pode
ser usado como detecção de baixa sobrecarga para bugs de segurança de memória.
Em casos de incompatibilidade de tag, o processador continua a execução até a entrada do kernel mais próxima (por exemplo, uma chamada de sistema ou interrupção de timer), em que encerra
o processo com SIGSEGV (código SEGV_MTEAERR) sem
registrar o endereço ou acesso à memória com falha.
Recomendamos usar esse modo na produção em bases de código bem testadas em que é sabido que a densidade de bugs de segurança de memória é baixa, o que é possível usando o modo SYNC durante o teste.
Modo assimétrico (ASYMM)
Um recurso adicional no Arm v8.7-A, o modo MTE assimétrico, fornece verificação síncrona em leituras de memória e verificação assíncrona de gravações de memória, com desempenho semelhante ao do modo ASYNC. Na maioria das situações, este modo é uma melhoria em relação ao modo ASYNC, e recomendamos usá-lo em vez do ASYNC sempre que estiver disponível.
Por esse motivo, nenhuma das APIs descritas abaixo menciona o modo assimétrico. Em vez disso, o SO pode ser configurado para sempre usar o modo assimétrico quando o assíncrono for solicitado. Para mais informações, consulte Configurar o nível de MTE preferido específico da CPU.
MTE no espaço do usuário
As seções a seguir descrevem como a MTE pode ser ativada para processos e apps do sistema. A MTE fica desativada por padrão, a menos que uma das opções abaixo seja definida para um processo específico (consulte abaixo para quais componentes a MTE está ativada abaixo).
Ativar a MTE usando o sistema de build
Como uma propriedade de todo o processo, a MTE é controlada pela configuração de tempo de build de o executável principal. As opções a seguir permitem alterar essa configuração para executáveis individuais ou para subdiretórios inteiros na árvore de origem. A configuração é ignorada em bibliotecas ou qualquer destino que não seja executável nem um teste.
1. Como ativar a MTE em Android.bp (exemplo),
para um projeto específico:
| Modo MTE | Configuração |
|---|---|
| MTE assíncrona | sanitize: {
memtag_heap: true,
} |
| MTE síncrona | sanitize: {
memtag_heap: true,
diag: {
memtag_heap: true,
},
} |
ou em Android.mk:
| Modo MTE | Configuração |
|---|---|
Asynchronous MTE |
LOCAL_SANITIZE := memtag_heap |
Synchronous MTE |
LOCAL_SANITIZE := memtag_heapLOCAL_SANITIZE_DIAG := memtag_heap |
2. Como ativar a MTE em um subdiretório na árvore de origem usando uma variável de produto:
| Modo MTE | Lista de inclusão | Lista de exclusão |
|---|---|---|
| async | PRODUCT_MEMTAG_HEAP_ASYNC_INCLUDE_PATHS
MEMTAG_HEAP_ASYNC_INCLUDE_PATHS |
PRODUCT_MEMTAG_HEAP_EXCLUDE_PATHS
MEMTAG_HEAP_EXCLUDE_PATHS |
| sync | PRODUCT_MEMTAG_HEAP_SYNC_INCLUDE_PATHS
MEMTAG_HEAP_SYNC_INCLUDE_PATHS |
ou
| Modo MTE | Configuração |
|---|---|
| MTE assíncrona | MEMTAG_HEAP_ASYNC_INCLUDE_PATHS |
| MTE síncrona | MEMTAG_HEAP_SYNC_INCLUDE_PATHS |
ou especificando o caminho de exclusão de um executável:
| Modo MTE | Configuração |
|---|---|
| MTE assíncrona | PRODUCT_MEMTAG_HEAP_EXCLUDE_PATHS
MEMTAG_HEAP_EXCLUDE_PATHS |
| MTE síncrona |
Exemplo (uso semelhante a PRODUCT_CFI_INCLUDE_PATHS)
PRODUCT_MEMTAG_HEAP_SYNC_INCLUDE_PATHS=vendor/$(vendor)
PRODUCT_MEMTAG_HEAP_EXCLUDE_PATHS=vendor/$(vendor)/projectA \
vendor/$(vendor)/projectBAtivar a MTE usando propriedades do sistema
As configurações de build acima podem ser substituídas no tempo de execução definindo a seguinte propriedade do sistema:
arm64.memtag.process.<basename> = (off|sync|async)
Em que basename representa o nome base do executável.
Por exemplo, para definir /system/bin/ping ou /data/local/tmp/ping
para usar a MTE assíncrona, use adb shell setprop arm64.memtag.process.ping async.
Ativar a MTE usando uma variável de ambiente
Outra maneira de substituir a configuração de build para processos nativos (não apps)
é definindo a variável de ambiente: MEMTAG_OPTIONS=(off|sync|async)
Se a variável de ambiente e a propriedade do sistema forem definidas, a
variável terá precedência.
Ativar a MTE para apps
Se não for especificado, a MTE será desativada por padrão, mas
os apps que quiserem usar a MTE poderão fazer isso definindo android:memtagMode
na tag <application> ou
<process> no
AndroidManifest.xml.
android:memtagMode=(off|default|sync|async)
Quando definido na tag <application>, o atributo afeta todos os processos usados pelo app e pode ser substituído para processos individuais definindo a tag <process>.
Para testes, mudanças de
compatibilidade podem ser usadas para definir o valor padrão do
atributo memtagMode de um app que não
especifica nenhum valor no manifesto (ou especifica default).
Eles estão em Sistema > Avançado > Opções do desenvolvedor
> Mudanças de compatibilidade de apps no menu de configurações globais. Definir
NATIVE_MEMTAG_ASYNC ou NATIVE_MEMTAG_SYNC ativa a MTE
para um app específico. Como alternativa, isso pode ser definido usando o am
comando da seguinte maneira:
$ adb shell am compat enable NATIVE_MEMTAG_[A]SYNC my.app.name
Criar uma imagem do sistema MTE
Recomendamos ativar a MTE em todos os binários nativos durante o desenvolvimento e a inicialização. Isso ajuda a detectar bugs de segurança de memória com antecedência e fornece cobertura realista do usuário, se ativado em builds de teste.
Recomendamos ativar a MTE no modo síncrono em todos os binários nativos durante o desenvolvimento.
SANITIZE_TARGET=memtag_heap SANITIZE_TARGET_DIAG=memtag_heap m
Como qualquer variável no sistema de build, SANITIZE_TARGET pode ser
usado como uma variável de ambiente ou uma configuração make (por exemplo, em
um arquivo product.mk).
Isso ativa a MTE para todos os processos nativos, mas não para
apps (que são bifurcados de zygote64) para os quais a MTE pode ser
ativada seguindo as instruções em Ativar a MTE para apps.
Configurar o nível de MTE preferido específico da CPU
Em algumas CPUs, o desempenho da MTE nos modos ASYMM ou até mesmo SYNC pode ser semelhante a o do ASYNC. Isso faz com que valha a pena ativar verificações mais rigorosas nessas CPUs quando um modo de verificação menos rigoroso for solicitado, para aproveitar os benefícios de detecção de erros das verificações mais rigorosas sem as desvantagens de desempenho.
Por padrão, os processos configurados para execução no modo ASYNC são executados no modo ASYNC
em todas as CPUs. Para configurar o kernel para executar esses processos no modo SYNC em
CPUs específicas, o valor sync precisa ser gravado na
sysfs entrada
/sys/devices/system/cpu/cpu<N>/mte_tcf_preferred no momento da inicialização. Isso pode ser feito com um script de inicialização. Por exemplo, para configurar as CPUs 0 a 1
para executar processos de modo ASYNC no modo SYNC e as CPUs 2 a 3 para usar o modo ASYMM,
o seguinte pode ser adicionado à cláusula init de um script de inicialização do fornecedor:
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
Os tombstone de processos de modo ASYNC em execução no modo SYNC contêm um stack trace preciso do local do erro de memória. No entanto, eles não incluem um stack trace de alocação ou desalocação. Esses stack traces só estão disponíveis se o processo estiver configurado para execução no modo SYNC.
int mallopt(M_THREAD_DISABLE_MEM_INIT, level)
em que level é 0 ou 1.
Desativa a inicialização da memória no malloc e evita a mudança de tags de memória, a menos que seja necessário para a correção.
int mallopt(M_MEMTAG_TUNING, level)
em que level é:
M_MEMTAG_TUNING_BUFFER_OVERFLOWM_MEMTAG_TUNING_UAF
Seleciona a estratégia de alocação de tags.
- A configuração padrão é
M_MEMTAG_TUNING_BUFFER_OVERFLOW. M_MEMTAG_TUNING_BUFFER_OVERFLOWpermite a detecção determinística de bugs de estouro de buffer linear e de subfluxo, atribuindo valores de tag distintos a alocações adjacentes. Esse modo tem uma chance ligeiramente reduzida de detectar bugs use-after-free porque apenas metade dos valores de tag possíveis estão disponíveis para cada local de memória. A MTE não consegue detectar estouro no mesmo grânulo de tag (bloco alinhado de 16 bytes) e pode perder pequenos estouros mesmo nesse modo. Esse estouro não pode ser a causa da corrupção de memória, porque a memória em um grânulo nunca é usada para várias alocações.M_MEMTAG_TUNING_UAFativa tags aleatórias independentes para uma probabilidade uniforme de ~93% de detectar bugs espaciais (estouro de buffer) e temporais (use-after-free).
Além das APIs descritas acima, os usuários experientes podem querer estar cientes do seguinte:
- Definir o registro de hardware
PSTATE.TCOpode suprimir temporariamente a verificação de tags (exemplo). Por exemplo, ao copiar um intervalo de memória com conteúdo de tag desconhecido ou ao resolver um gargalo de desempenho em um loop ativo. - Ao usar
M_HEAP_TAGGING_LEVEL_SYNC, o gerenciador de falhas do sistema fornece informações extras, como stack traces de alocação e desalocação. Essa funcionalidade exige acesso aos bits de tag e é ativada transmitindo aSA_EXPOSE_TAGBITSflag ao definir o gerenciador de sinais. Recomendamos que qualquer programa que defina o próprio gerenciador de sinais e delegue falhas desconhecidas ao sistema faça o mesmo.
MTE no kernel
Para ativar o KASAN acelerado por MTE para o kernel, configure o kernel com
CONFIG_KASAN=y, CONFIG_KASAN_HW_TAGS=y. Essas configurações
são ativadas por padrão nos kernels GKI, começando com Android
12-5.10.
É possível controlar isso no momento da inicialização usando os seguintes argumentos de linha de comando:
kasan=[on|off]- ativa ou desativa o KASAN (padrão:on)kasan.mode=[sync|async]- escolha entre o modo síncrono e assíncrono (padrão:sync)kasan.stacktrace=[on|off]- se os stack traces devem ser coletados (padrão:on)- A coleta de stack trace também exige
stack_depot_disable=off.
- A coleta de stack trace também exige
kasan.fault=[report|panic]- se apenas o relatório deve ser impresso, ou se o kernel também deve entrar em pânico (padrão:report). Independente dessa opção, a verificação de tags é desativada após o primeiro erro informado.
Uso recomendado
Recomendamos usar o modo SYNC durante a inicialização, o desenvolvimento e os testes. Essa opção precisa ser ativada globalmente para todos os processos usando a variável de ambiente ou com o sistema de build. Nesse modo, os bugs são detectados no início do processo de desenvolvimento, a base de código é estabilizada mais rapidamente e o custo de detectar bugs mais tarde na produção é evitado.
Recomendamos usar o modo ASYNC na produção. Isso fornece uma ferramenta de baixa sobrecarga para detectar a presença de bugs de segurança de memória em um processo, bem como uma defesa mais aprofundada. Depois que um bug é detectado, o desenvolvedor pode aproveitar as APIs de execução para mudar para o modo SYNC e receber um stack trace preciso de um conjunto de usuários amostrados.
Recomendamos configurar o nível de MTE preferido específico da CPU para o SoC. O modo ASYMM normalmente tem as mesmas características de desempenho do ASYNC, e é quase sempre preferível. Os núcleos pequenos em ordem geralmente mostram desempenho semelhante em todos os três modos e podem ser configurados para preferir o SYNC.
Os desenvolvedores precisam verificar a presença de falhas verificando
/data/tombstones,
logcat ou monitorando o pipeline DropboxManager
do fornecedor para bugs do usuário final. Para mais informações sobre a depuração de código nativo do Android, consulte
as informações aqui.
Componentes da plataforma ativados pela MTE
No Android 12, vários componentes críticos de segurança do sistema usam a MTE ASYNC para detectar falhas do usuário final e atuar como uma camada adicional de defesa aprofundada. Esses componentes são:
- Daemons e utilitários de rede (com exceção de
netd) - Bluetooth, SecureElement, NFC HALs e apps do sistema
- Daemon
statsd system_serverzygote64(para permitir que os apps usem a MTE)
Esses destinos foram selecionados com base nos seguintes critérios:
- Um processo privilegiado (definido como um processo que tem acesso a algo que o domínio SELinux unprivileged_app não tem)
- Processa entrada não confiável (Regra de dois)
- Desaceleração de desempenho aceitável (a desaceleração não cria latência visível ao usuário )
Incentivamos os fornecedores a ativar a MTE na produção para mais componentes, seguindo os critérios mencionados acima. Durante o desenvolvimento, recomendamos testar esses componentes usando o modo SYNC para detectar bugs facilmente corrigidos e avaliar o impacto do ASYNC no desempenho deles.
Planejamos expandir a lista de componentes do sistema em que a MTE está ativada, orientada pelas características de desempenho dos próximos designs de hardware.