As seções de código executável para binários do sistema AArch64 são, por padrão, marcadas como somente execução (não legível) como uma mitigação de proteção contra ataques de reutilização de código just-in-time. Código que mistura dados e código e código que inspeciona propositadamente essas seções (sem primeiro remapear os segmentos de memória como legíveis) não funcionam mais. Aplicativos com um SDK de destino de 10 (API de nível 29 ou superior) são afetados se o aplicativo tentar ler seções de código de bibliotecas de sistema habilitadas para memória somente para execução (XOM) na memória sem primeiro marcar a seção como legível.
Para se beneficiar totalmente dessa mitigação, são necessários 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 apropriados para fornecer suporte completo para isso em dispositivos ARMv8.2.
Implementação
Os binários AArch64 gerados pelo compilador assumem que o código e os dados não são misturados. A ativação desse recurso não afeta negativamente o desempenho do dispositivo.
Para código que precisa realizar introspecção intencional de memória em seus segmentos executáveis, é aconselhável chamar mprotect
nos segmentos de código que requerem inspeção para permitir que sejam legíveis e, em seguida, remover a legibilidade quando a inspeção estiver 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 (pooling literal) ou introspecção intencional de memória.
Suporte e impacto do dispositivo
Dispositivos com hardware ou kernels anteriores (inferiores a 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 forçar o acesso do usuário à memória somente de execução, no entanto, o código do kernel que verifica explicitamente se uma página é legível ainda pode forçar essa propriedade, como process_vm_readv()
.
O sinalizador do kernel CONFIG_ARM64_UAO
deve ser definido no kernel para garantir que o kernel respeite as páginas da área de usuário marcadas como somente execução. Dispositivos ARMv8 anteriores, ou dispositivos ARMv8.2 com substituição de acesso do usuário (UAO) desabilitado, podem não se beneficiar totalmente disso e ainda podem ler páginas somente de execução usando syscalls.
Refatorando o código existente
O código que foi portado do AArch32 pode conter dados e códigos misturados, causando problemas. Em muitos casos, corrigir esses problemas é tão simples quanto mover as constantes para uma seção .data
no arquivo de montagem.
O assembly manuscrito pode precisar ser refatorado para separar as constantes agrupadas localmente.
Exemplos:
Os binários gerados pelo compilador Clang não devem ter problemas com a mistura de dados no código. Se o código gerado pela coleção do compilador GNU (GCC) for incluído (de uma biblioteca estática), inspecione o binário de saída para garantir que as constantes não tenham sido 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 legível. Em seguida, após a conclusão da operação, chame mprotect
novamente para marcá-lo como ilegível.
Possibilitando
Somente execução é habilitado por padrão para todos os binários de 64 bits no sistema de compilação.
Desativando
Você pode desabilitar a execução somente em nível de módulo, por uma árvore de subdiretório inteira ou globalmente para uma compilação inteira.
O XOM pode ser desabilitado para módulos individuais que não podem ser refatorados ou precisam ler seu 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 de execução estiver desabilitada em uma biblioteca estática, o sistema de compilação aplicará isso a todos os módulos dependentes dessa biblioteca estática. Você pode substituir isso usando xom: true,
.
Para desabilitar a memória somente de execução em um subdiretório específico (por exemplo, foo/bar/), passe o valor para XOM_EXCLUDE_PATHS
.
make -j XOM_EXCLUDE_PATHS=foo/bar
Como alternativa, você pode definir a variável PRODUCT_XOM_EXCLUDE_PATHS
na configuração do seu produto.
Você pode desabilitar binários somente de execução globalmente passando ENABLE_XOM=false
para seu 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. Você pode verificar manualmente os binários usando readelf
e verificando os sinalizadores de segmento.