As seções de código executável dos binários do sistema AArch64 são marcadas como somente execução (ilegíveis) por padrão para aumentar a proteção contra ataques de reutilização de código just-in-time. Códigos que combinam dados e código e códigos que inspecionam intencionalmente essas seções (sem primeiro remapear os segmentos de memória como legíveis) não funcionam mais. Os apps que têm um SDK de destino do Android 10 (nível 29 da API ou mais recente) serão afetados se o app tentar ler seções de código de bibliotecas do sistema habilitadas para memória somente de execução (XOM) na memória sem primeiro marcar a seção como legível.
Para aproveitar ao máximo essa mitigação, é necessário ter suporte de hardware e kernel. Sem esse suporte, a mitigação pode ser aplicada apenas parcialmente. O kernel comum do Android 4.9 contém os patches adequados para oferecer suporte total a isso em dispositivos ARMv8.2.
Implementação
Os binários AArch64 gerados pelo compilador presumem que o código e os dados não são misturados. Ativar esse recurso não afeta negativamente o desempenho do dispositivo.
Para códigos que precisam realizar introspecção de memória intencional nos
segmentos executáveis, é recomendável chamar mprotect
nos
segmentos de código que exigem inspeção para permitir que sejam legíveis e, em seguida,
remover a legibilidade quando a inspeção for concluída.
Essa implementação faz com que as leituras em segmentos de memória marcados como
somente execução resultem em uma falha de segmentação (SEGFAULT
).
Isso pode ocorrer como resultado de um bug, vulnerabilidade, dados misturados com
código (agrupamento literal) ou introspecção intencional da memória.
Suporte e impacto do dispositivo
Dispositivos com hardware ou kernels anteriores (inferiores à versão 4.9) sem os
patches necessários podem não oferecer suporte total ou se beneficiar desse recurso. Dispositivos
sem suporte ao kernel podem não aplicar acessos de usuário à memória somente leitura,
mas o código do kernel que verifica explicitamente se uma página é legível ainda pode
aplicar essa propriedade, como process_vm_readv()
.
A flag CONFIG_ARM64_UAO
do kernel precisa ser definida para
garantir que o kernel respeite as páginas do userland marcadas como somente execução. Dispositivos ARMv8
anteriores ou ARMv8.2 com a substituição de acesso do usuário (UAO) desativada podem não
se beneficiar totalmente disso e ainda podem ler páginas somente de execução usando
syscalls.
Refazer o código
O código transferido do AArch32 pode conter dados e
códigos misturados, causando problemas. Em muitos casos, a correção desses problemas é tão simples
quanto mover as constantes para uma seção .data
no arquivo de assembly.
Talvez seja necessário refazer o assembly escrito à mão para separar as constantes agrupadas localmente.
Exemplos:
Os binários gerados pelo compilador Clang não devem ter problemas com dados misturados no código. Se o código gerado pela coleção de compiladores GNU (GCC) for incluído (de uma biblioteca estática), inspecione o binário de saída para garantir que as constantes não foram agrupadas em seções de código.
Se a introspecção de código for necessária em seções de código executável,
primeiro chame mprotect
para marcar o código como legível. Depois que a operação
for concluída, chame mprotect
novamente para marcá-la como ilegível.
Ativar o XOM
A execução exclusiva é ativada por padrão para todos os binários de 64 bits no sistema de build.
Desativar XOM
É possível desativar a execução somente no nível do módulo, em uma árvore de subdiretórios inteira ou globalmente para um build inteiro.
A XOM pode ser desativada para módulos individuais que não podem ser refatorados ou que precisam ler o
código executável, definindo as variáveis LOCAL_XOM
e xom
como false
.
// Android.mk LOCAL_XOM := false // Android.bp cc_binary { // or other module types ... xom: false, }
Se a memória somente leitura estiver desativada em uma biblioteca estática, o sistema de build vai aplicar
isso a todos os módulos dependentes dessa biblioteca estática. Para modificar
isso, use xom: true,
.
Para desativar a memória somente leitura em um determinado subdiretório (por exemplo,
foo/bar/), transmita o valor para XOM_EXCLUDE_PATHS
.
make -j XOM_EXCLUDE_PATHS=foo/bar
Como alternativa, defina a variável PRODUCT_XOM_EXCLUDE_PATHS
na configuração do produto.
É possível desativar os binários somente de execução globalmente transmitindo
ENABLE_XOM=false
para o comando make
.
make -j ENABLE_XOM=false
Validação
Não há CTS ou testes de verificação disponíveis para memória
somente de execução. É possível verificar manualmente os binários usando readelf
e verificando
as flags de segmento.