A Arm v9 apresenta a Memory Tagging Extension (MTE) da Arm, uma implementação de hardware de memória marcada.
De modo geral, a MTE marca cada alocação/remoção de memória com metadados adicionais. Ele atribui uma tag a um local de memória, que pode ser associado a ponteiros que fazem referência a esse local. No momento da execução, a CPU verifica se o ponteiro e as tags de metadados correspondem a cada carga e armazenamento.
No Android 12, o alocador de memória heap do kernel e do espaço do usuário pode aumentar cada alocação com metadados. Isso ajuda a detectar bugs de uso após a liberação e de estouro de buffer, que são a fonte mais comum de bugs de segurança de memória nas 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 precisão da detecção de bugs em vez do 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 aborta 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 em 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.
Quando executado no modo SYNC, o alocador do Android registra rastros de pilha 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 estouro de buffer, e os rastros de pilha 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
a densidade de bugs de segurança de memória é conhecida por ser 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, oferece 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, esse modo é uma melhoria em relação ao modo ASYNC, e recomendamos usá-lo em vez de ASYNC sempre que ele 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 modo assíncrono é solicitado. Consulte a seção "Como configurar o nível de MTE preferido específico da CPU" para mais informações.
MTE no espaço do usuário
As seções a seguir descrevem como a MTE pode ser ativada para processos do sistema e apps. A MTE fica desativada por padrão, a menos que uma das opções abaixo seja definida para um processo específico. Confira abaixo quais componentes têm a MTE ativada.
Ativar a MTE usando o sistema de build
Como uma propriedade em todo o processo, a MTE é controlada pela configuração do tempo de build do 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 em qualquer destino que não seja executável nem um teste.
1. Ativação da MTE em Android.bp
(exemplo)
para um projeto específico:
Modo MTE | Configuração |
---|---|
MTE assíncrono | sanitize: { memtag_heap: true, } |
MTE síncrono | 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_heap LOCAL_SANITIZE_DIAG := memtag_heap |
2. Ativar a MTE em um subdiretório na árvore de origem usando uma variável de produto:
Modo MTE | Incluir lista | Lista de exclusão |
---|---|---|
assíncrono | PRODUCT_MEMTAG_HEAP_ASYNC_INCLUDE_PATHS
MEMTAG_HEAP_ASYNC_INCLUDE_PATHS |
PRODUCT_MEMTAG_HEAP_EXCLUDE_PATHS
MEMTAG_HEAP_EXCLUDE_PATHS |
sincronização | PRODUCT_MEMTAG_HEAP_SYNC_INCLUDE_PATHS
MEMTAG_HEAP_SYNC_INCLUDE_PATHS |
ou
Modo MTE | Configuração |
---|---|
MTE assíncrono | MEMTAG_HEAP_ASYNC_INCLUDE_PATHS |
MTE síncrono | MEMTAG_HEAP_SYNC_INCLUDE_PATHS |
ou especificando o caminho de exclusão de um executável:
Modo MTE | Configuração |
---|---|
MTE assíncrono | PRODUCT_MEMTAG_HEAP_EXCLUDE_PATHS
MEMTAG_HEAP_EXCLUDE_PATHS |
MTE síncrono |
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)/projectB
Ativar a MTE usando propriedades do sistema
As configurações de build acima podem ser substituídas no momento da execução definindo a seguinte propriedade do sistema:
arm64.memtag.process.<basename> = (off|sync|async)
Em que basename
representa o nome básico 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 do build é 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 usá-la 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 fazer testes, as mudanças de
compatibilidade podem ser usadas para definir o valor padrão do
atributo memtagMode
de um app que não
especifique nenhum valor no manifesto (ou que especifique
default
).
pode ser encontrado em System > Advanced > Developer options
> App Compatibility Changes
no menu de configurações globais. A configuração
NATIVE_MEMTAG_ASYNC
ou NATIVE_MEMTAG_SYNC
ativa a MTE
para um app específico.
Como alternativa, é possível definir isso usando o comando am
desta forma:
$ adb shell am compat enable NATIVE_MEMTAG_[A]SYNC my.app.name
Criar uma imagem do sistema MTE
É altamente recomendável 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 mais cedo e oferece cobertura de usuário realista, se ativada nos 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
usada 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 acima.
Configurar o nível de MTE preferido específico da CPU
Em algumas CPUs, o desempenho da MTE nos modos ASYMM ou até SYNC pode ser semelhante ao
de ASYNC. Isso faz com que valha a pena ativar
verificações mais rígidas nessas CPUs quando um modo de verificação menos rigoroso é solicitado, para obter os benefícios de detecção de erros das verificações mais rígidas sem as
desvantagens de desempenho.
Por padrão, os processos configurados para execução no modo ASYNC serão executados no modo
ASYNC em todas as CPUs. Para configurar o kernel para executar esses processos no modo SYNC em
CPUs específicas, a sincronização de valor precisa ser gravada na
entrada sysfs
/sys/devices/system/cpu/cpu<N>/mte_tcf_preferred
durante a inicialização. Isso pode ser feito com um script de inicialização. Por exemplo, para configurar as CPUs 0 a 1
para executar processos no modo ASYNC no modo SYNC e as CPUs 2 a 3 para usar a execução no modo ASYMM,
a cláusula de inicialização de um script de inicialização do fornecedor pode ser adicionada:
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
As lápides de processos do 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 rastros de pilha só estarão disponíveis se o processo estiver configurado para ser executado no modo SYNC.
int mallopt(M_THREAD_DISABLE_MEM_INIT, level)
em que level
é 0 ou 1.
Desativa a inicialização de memória no malloc e evita a mudança de tags de memória, a menos que seja necessário para correção.
int mallopt(M_MEMTAG_TUNING, level)
em que level
é:
M_MEMTAG_TUNING_BUFFER_OVERFLOW
M_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_OVERFLOW
: permite a detecção determinística de bugs de overflow e underflow de buffer linear, atribuindo valores de tag distintos a alocações adjacentes. Esse modo tem uma chance ligeiramente reduzida de detectar bugs de uso após a liberação, porque apenas metade dos valores de tag possíveis estão disponíveis para cada local de memória. A MTE não pode detectar transbordamentos no mesmo grânulo de tag (fragmento alinhado de 16 bytes) e pode perder pequenos transbordamentos, mesmo nesse modo. Esse overflow não pode ser a causa da corrupção da memória, porque a memória em um grânulo nunca é usada para várias alocações.M_MEMTAG_TUNING_UAF
: ativa tags aleatórias independentes para uma probabilidade uniforme de 93% de detecção de bugs espaciais (transbordamento de buffer) e temporais (uso após a liberação).
Além das APIs descritas acima, os usuários experientes podem querer saber o seguinte:
- A configuração do registro de hardware
PSTATE.TCO
pode 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 quente. - Ao usar
M_HEAP_TAGGING_LEVEL_SYNC
, o gerenciador de falhas do sistema fornece informações extras, como a alocação e a dealocação de stack traces. Essa funcionalidade requer acesso aos bits de tag e é ativada transmitindo a flagSA_EXPOSE_TAGBITS
ao definir o manipulador de sinal. Qualquer programa que defina seu próprio gerenciador de indicador e delegue falhas desconhecidas ao sistema é recomendado a fazer o mesmo.
MTE no kernel
Para ativar o KASAN acelerado por MTE no kernel, configure o kernel com
CONFIG_KASAN=y
, CONFIG_KASAN_HW_TAGS=y
. Essas configurações
são ativadas por padrão nos kernels do GKI, começando com Android
12-5.10
.
Isso pode ser controlado 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 é necessário coletar rastros de pilha (padrão:on
)- A coleta de stack trace também requer
stack_depot_disable=off
.
- A coleta de stack trace também requer
kasan.fault=[report|panic]
: se o relatório será impresso apenas ou se o kernel também será colocado em pânico (padrão:report
). Independentemente dessa opção, a verificação de tags será desativada após o primeiro erro relatado.
Uso recomendado
Recomendamos o uso do modo SYNC durante a ativação, o desenvolvimento e os testes. Essa opção precisa ser ativada globalmente para todos os processos que usam a variável de ambiente ou 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.
É altamente recomendável usar o modo ASYNC na produção. Isso fornece uma ferramenta de sobrecarga baixa para detectar a presença de bugs de segurança de memória em um processo, além de outras defesas detalhadas. Depois que um bug é detectado, o desenvolvedor pode usar as APIs de execução para alternar 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 geralmente tem as mesmas características de desempenho que o ASYNC, e é quase sempre preferível. Cores pequenas na ordem geralmente mostram desempenho semelhante nos três modos e podem ser configuradas para preferir a SYNC.
Os desenvolvedores precisam verificar a presença de falhas usando
/data/tombstones
,
logcat
ou monitorando o pipeline de DropboxManager
do fornecedor em busca de 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 com MTE ativada
No Android 12, vários componentes críticos de segurança do sistema usam o MTE ASYNC para detectar falhas do usuário final e atuar como uma camada adicional de defesa em profundidade. Esses componentes são:
- Daemons e utilitários de rede (com exceção de
netd
) - Bluetooth, SecureElement, HALs de NFC e apps do sistema
- Daemon
statsd
system_server
zygote64
(para permitir que os apps ativem o uso da MTE)
Essas metas foram selecionadas 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 entradas não confiáveis (Regra de dois).
- Diminuição aceitável no desempenho (a diminuição não cria latência visível para o usuário)
Recomendamos que os fornecedores ativem 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 na performance.
No futuro, o Android planeja expandir a lista de componentes do sistema em que a MTE é
ativada, guiada pelas características de desempenho dos próximos designs de hardware.