Integridade do Fluxo de Controle

Em 2016, cerca de 86% de todas as vulnerabilidades no Android estavam relacionadas à segurança da memória. A maioria das vulnerabilidades é explorada por invasores que alteram o fluxo normal de controle de um aplicativo para realizar atividades maliciosas arbitrárias com todos os privilégios do aplicativo explorado. A integridade do fluxo de controle (CFI) é um mecanismo de segurança que não permite alterações no gráfico de fluxo de controle original de um binário compilado, tornando significativamente mais difícil realizar tais ataques.

No Android 8.1, habilitamos a implementação de CFI do LLVM na pilha de mídia. No Android 9, habilitamos CFI em mais componentes e também no kernel. A CFI do sistema está ativada por padrão, mas você precisa habilitar a CFI do kernel.

O CFI do LLVM requer compilação com Link-Time Optimization (LTO) . O LTO preserva a representação do código de bits LLVM dos arquivos objeto até o momento do link, o que permite ao compilador raciocinar melhor sobre quais otimizações podem ser executadas. A ativação do LTO reduz o tamanho do binário final e melhora o desempenho, mas aumenta o tempo de compilação. Nos testes no Android, a combinação de LTO e CFI resulta em sobrecarga insignificante no tamanho e desempenho do código; em alguns casos, ambos melhoraram.

Para obter mais detalhes técnicos sobre CFI e como outras verificações de controle direto são tratadas, consulte a documentação de design do LLVM .

Exemplos e fonte

CFI é fornecido pelo compilador e adiciona instrumentação ao binário durante o tempo de compilação. Oferecemos suporte a CFI no conjunto de ferramentas Clang e ao sistema de compilação Android no AOSP.

CFI é habilitado por padrão para dispositivos Arm64 para o conjunto de componentes em /platform/build/target/product/cfi-common.mk . Ele também é ativado diretamente em um conjunto de arquivos makefiles/blueprint de componentes de mídia, como /platform/frameworks/av/media/libmedia/Android.bp e /platform/frameworks/av/cmds/stagefright/Android.mk .

Implementando sistema financeiro

O CFI é ativado por padrão se você usar o Clang e o sistema de compilação Android. Como o Finance ajuda a manter os usuários do Android seguros, você não deve desativá-lo.

Na verdade, recomendamos fortemente que você habilite o CFI para componentes adicionais. Os candidatos ideais são código nativo privilegiado ou código nativo que processa entradas de usuários não confiáveis. Se você estiver usando o clang e o sistema de compilação Android, poderá ativar o CFI em novos componentes adicionando algumas linhas aos seus makefiles ou arquivos de blueprint.

Suportando finanças em makefiles

Para ativar CFI em um arquivo make, como /platform/frameworks/av/cmds/stagefright/Android.mk , adicione:

LOCAL_SANITIZE := cfi
# Optional features
LOCAL_SANITIZE_DIAG := cfi
LOCAL_SANITIZE_BLACKLIST := cfi_blacklist.txt
  • LOCAL_SANITIZE especifica CFI como o sanitizador durante a construção.
  • LOCAL_SANITIZE_DIAG ativa o modo de diagnóstico para CFI. O modo de diagnóstico imprime informações adicionais de depuração no logcat durante falhas, o que é útil ao desenvolver e testar suas compilações. No entanto, certifique-se de remover o modo de diagnóstico nas compilações de produção.
  • LOCAL_SANITIZE_BLACKLIST permite que os componentes desabilitem seletivamente a instrumentação CFI para funções individuais ou arquivos de origem. Você pode usar uma lista negra como último recurso para corrigir quaisquer problemas enfrentados pelo usuário que possam existir. Para obter mais detalhes, consulte Desativando CFI .

Suporte financeiro em arquivos de blueprint

Para ativar CFI em um arquivo de blueprint, como /platform/frameworks/av/media/libmedia/Android.bp , adicione:

   sanitize: {
        cfi: true,
        diag: {
            cfi: true,
        },
        blacklist: "cfi_blacklist.txt",
    },

Solução de problemas

Se você estiver habilitando CFI em novos componentes, poderá encontrar alguns problemas com erros de incompatibilidade de tipo de função e erros de incompatibilidade de tipo de código assembly .

Erros de incompatibilidade de tipo de função ocorrem porque o CFI restringe chamadas indiretas para saltar apenas para funções que possuem o mesmo tipo dinâmico que o tipo estático usado na chamada. CFI restringe chamadas de função de membro virtual e não virtual para saltar apenas para objetos que são uma classe derivada do tipo estático do objeto usado para fazer a chamada. Isso significa que, quando você tiver um código que viole qualquer uma dessas suposições, a instrumentação adicionada por Finanças será abortada. Por exemplo, o rastreamento de pilha mostra um SIGABRT e o logcat contém uma linha sobre a integridade do fluxo de controle que encontra uma incompatibilidade.

Para corrigir isso, certifique-se de que a função chamada tenha o mesmo tipo que foi declarado estaticamente. Aqui estão dois exemplos de CLs:

Outro possível problema é tentar habilitar CFI em código que contém chamadas indiretas para assembly. Como o código assembly não é digitado, isso resulta em uma incompatibilidade de tipo.

Para corrigir isso, crie wrappers de código nativo para cada chamada de assembly e forneça aos wrappers a mesma assinatura de função do apontador de chamada. O wrapper pode então chamar diretamente o código assembly. Como as ramificações diretas não são instrumentadas por Finanças (elas não podem ser reposicionadas em tempo de execução e, portanto, não representam um risco à segurança), isso resolverá o problema.

Se houver muitas funções assembly e elas não puderem ser corrigidas, você também poderá colocar na lista negra todas as funções que contêm chamadas indiretas para assembly. Isso não é recomendado, pois desativa as verificações CFI nessas funções, abrindo assim a superfície de ataque.

Desativando Finanças

Não observamos nenhuma sobrecarga de desempenho, então você não deve precisar desabilitar o CFI. No entanto, se houver um impacto voltado para o usuário, você poderá desabilitar seletivamente o CFI para funções individuais ou arquivos de origem, fornecendo um arquivo de lista negra do sanitizador em tempo de compilação. A lista negra instrui o compilador a desabilitar a instrumentação CFI em locais especificados.

O sistema de compilação do Android fornece suporte para listas negras por componente (permitindo escolher arquivos de origem ou funções individuais que não receberão instrumentação CFI) para Make e Soong. Para obter mais detalhes sobre o formato de um arquivo de lista negra, consulte a documentação upstream do Clang .

Validação

Atualmente, não há teste CTS específico para Finanças. Em vez disso, certifique-se de que os testes CTS sejam aprovados com ou sem CFI habilitado para verificar se o CFI não está impactando o dispositivo.