UndefinedBehaviorSanitizer

UndefinedBehaviorSanitizer (UBSan) executa instrumentação em tempo de compilação para verificar vários tipos de comportamento indefinido. Embora o UBSan seja capaz de detectar muitos bugs de comportamento indefinidos , o Android suporta:

  • alinhamento
  • bool
  • limites
  • enumeração
  • float-cast-overflow
  • flutuar-dividir por zero
  • número inteiro dividido por zero
  • atributo não nulo
  • nulo
  • retornar
  • retorna-atributo não nulo
  • base de turno
  • expoente de mudança
  • estouro de número inteiro assinado
  • inacessível
  • estouro de número inteiro não assinado
  • vinculado a vla

overflow de número inteiro não assinado, embora não seja um comportamento tecnicamente indefinido, está incluído no sanitizer e usado em muitos módulos Android, incluindo os componentes do mediaserver, para eliminar quaisquer vulnerabilidades latentes de estouro de número inteiro.

Implementação

No sistema de compilação do Android, você pode ativar o UBSan global ou localmente. Para ativar o UBSan globalmente, defina SANITIZE_TARGET em Android.mk. Para ativar o UBSan em nível por módulo, defina LOCAL_SANITIZE e especifique os comportamentos indefinidos que você deseja procurar em Android.mk. Por exemplo:

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_CFLAGS := -std=c11 -Wall -Werror -O0

LOCAL_SRC_FILES:= sanitizer-status.c

LOCAL_MODULE:= sanitizer-status

LOCAL_SANITIZE := alignment bounds null unreachable integer
LOCAL_SANITIZE_DIAG := alignment bounds null unreachable integer

include $(BUILD_EXECUTABLE)

E a configuração equivalente do blueprint (Android.bp):

cc_binary {

    cflags: [
        "-std=c11",
        "-Wall",
        "-Werror",
        "-O0",
    ],

    srcs: ["sanitizer-status.c"],

    name: "sanitizer-status",

    sanitize: {
        misc_undefined: [
            "alignment",
            "bounds",
            "null",
            "unreachable",
            "integer",
        ],
        diag: {
            misc_undefined: [
                "alignment",
                "bounds",
                "null",
                "unreachable",
                "integer",
            ],
        },
    },

}

Atalhos UBSan

O Android também possui dois atalhos, integer e default-ub , para ativar um conjunto de desinfetantes ao mesmo tempo. inteiro habilita integer-divide-by-zero , signed-integer-overflow e unsigned-integer-overflow . default-ub habilita as verificações que apresentam problemas mínimos de desempenho do compilador: bool, integer-divide-by-zero, return, returns-nonnull-attribute, shift-exponent, unreachable and vla-bound . A classe sanitizer inteira pode ser usada com SANITIZE_TARGET e LOCAL_SANITIZE, enquanto default-ub só pode ser usada com SANITIZE_TARGET.

Melhor relatório de erros

A implementação UBSan padrão do Android invoca uma função especificada quando um comportamento indefinido é encontrado. Por padrão, esta função é abortada. No entanto, a partir de outubro de 2016, o UBSan no Android tem uma biblioteca de tempo de execução opcional que fornece relatórios de erros mais detalhados, incluindo tipo de comportamento indefinido encontrado, informações de arquivo e linha de código-fonte. Para ativar esse relatório de erros com verificações de números inteiros, adicione o seguinte a um arquivo Android.mk:

LOCAL_SANITIZE:=integer
LOCAL_SANITIZE_DIAG:=integer

O valor LOCAL_SANITIZE ativa o sanitizador durante a construção. LOCAL_SANITIZE_DIAG ativa o modo de diagnóstico para o sanitizador especificado. É possível definir LOCAL_SANITIZE e LOCAL_SANITIZE_DIAG com valores diferentes, mas apenas as verificações em LOCAL_SANITIZE estão habilitadas. Se uma verificação não for especificada em LOCAL_SANITIZE, mas for especificada em LOCAL_SANITIZE_DIAG, a verificação não será habilitada e as mensagens de diagnóstico não serão fornecidas.

Aqui está um exemplo das informações fornecidas pela biblioteca de tempo de execução UBSan:

pixel-xl:/ # sanitizer-status ubsan
sanitizer-status/sanitizer-status.c:53:6: runtime error: unsigned integer overflow: 18446744073709551615 + 1 cannot be represented in type 'size_t' (aka 'unsigned long')

Sanitização de estouro de número inteiro

Estouros de números inteiros não intencionais podem causar corrupção de memória ou vulnerabilidades de divulgação de informações em variáveis ​​associadas a acessos ou alocações de memória. Para combater isso, adicionamos os sanitizadores de estouro de número inteiro assinado e não assinado UndefinedBehaviorSanitizer (UBSan) do Clang para fortalecer a estrutura de mídia no Android 7.0. No Android 9, expandimos o UBSan para abranger mais componentes e melhoramos o suporte do sistema de compilação para ele.

Isso foi projetado para adicionar verificações em operações/instruções aritméticas - que podem estourar - para abortar um processo com segurança se ocorrer um estouro. Esses sanitizadores podem mitigar toda uma classe de vulnerabilidades de corrupção de memória e divulgação de informações em que a causa raiz é um estouro de número inteiro, como a vulnerabilidade Stagefright original.

Exemplos e fonte

Integer Overflow Sanitization (IntSan) é fornecido pelo compilador e adiciona instrumentação ao binário durante o tempo de compilação para detectar overflows aritméticos. Ele é habilitado por padrão em vários componentes da plataforma, por exemplo /platform/external/libnl/Android.bp .

Implementação

IntSan usa sanitizadores de estouro de número inteiro assinado e não assinado do UBSan. Essa mitigação é habilitada em nível por módulo. Ajuda a manter seguros os componentes críticos do Android e não deve ser desativado.

Recomendamos fortemente que você habilite a limpeza de estouro de número inteiro para componentes adicionais. Os candidatos ideais são código nativo privilegiado ou código nativo que analisa entradas de usuários não confiáveis. Há uma pequena sobrecarga de desempenho associada ao sanitizador que depende do uso do código e da prevalência de operações aritméticas. Espere uma pequena porcentagem de sobrecarga e teste se o desempenho é uma preocupação.

Suportando IntSan em makefiles

Para habilitar o IntSan em um makefile, adicione:

LOCAL_SANITIZE := integer_overflow
    # Optional features
    LOCAL_SANITIZE_DIAG := integer_overflow
    LOCAL_SANITIZE_BLOCKLIST := modulename_BLOCKLIST.txt
  • LOCAL_SANITIZE usa uma lista de sanitizadores separados por vírgula, com integer_overflow sendo um conjunto pré-empacotado de opções para os higienizadores de estouro de número inteiro assinados e não assinados individuais com um BLOCKLIST padrão .
  • LOCAL_SANITIZE_DIAG ativa o modo de diagnóstico para os sanitizadores. Use o modo de diagnóstico somente durante o teste, pois isso não interromperá os overflows, negando completamente a vantagem de segurança da mitigação. Consulte Solução de problemas para obter detalhes adicionais.
  • LOCAL_SANITIZE_BLOCKLIST permite que você especifique um arquivo BLOCKLIST para evitar que funções e arquivos de origem sejam limpos. Consulte Solução de problemas para obter detalhes adicionais.

Se quiser um controle mais granular, habilite os desinfetantes individualmente usando um ou ambos os sinalizadores:

LOCAL_SANITIZE := signed-integer-overflow, unsigned-integer-overflow
    LOCAL_SANITIZE_DIAG := signed-integer-overflow, unsigned-integer-overflow

Apoiando o IntSan em arquivos de blueprint

Para ativar a limpeza de estouro de número inteiro em um arquivo de blueprint, como /platform/external/libnl/Android.bp , adicione:

   sanitize: {
          integer_overflow: true,
          diag: {
              integer_overflow: true,
          },
          BLOCKLIST: "modulename_BLOCKLIST.txt",
       },

Tal como acontece com os arquivos make, a propriedade integer_overflow é um conjunto pré-empacotado de opções para os sanitizadores de estouro de número inteiro assinados e não assinados individuais com um BLOCKLIST padrão .

O diag de propriedades de diagnóstico ativa o modo de diagnóstico para os sanitizadores. Use o modo de diagnóstico somente durante o teste. O modo de diagnóstico não é interrompido em overflows, o que nega completamente a vantagem de segurança da mitigação nas compilações do usuário. Consulte Solução de problemas para obter detalhes adicionais.

A propriedade BLOCKLIST permite a especificação de um arquivo BLOCKLIST que permite aos desenvolvedores impedir que funções e arquivos de origem sejam limpos. Consulte Solução de problemas para obter detalhes adicionais.

Para ativar os desinfetantes individualmente, use:

   sanitize: {
          misc_undefined: ["signed-integer-overflow", "unsigned-integer-overflow"],
          diag: {
              misc_undefined: ["signed-integer-overflow",
                               "unsigned-integer-overflow",],
          },
          BLOCKLIST: "modulename_BLOCKLIST.txt",
       },

Solução de problemas

Se você estiver habilitando a sanitização de overflow de inteiro em novos componentes ou depender de bibliotecas de plataforma que tiveram sanitização de overflow de inteiro, você poderá encontrar alguns problemas com estouros de inteiros benignos causando anulações. Você deve testar os componentes com a higienização habilitada para garantir que transbordamentos benignos possam surgir.

Para encontrar abortos causados ​​por higienização em compilações de usuários, pesquise travamentos SIGABRT com mensagens de Abort indicando um overflow capturado pelo UBSan, como:

pid: ###, tid: ###, name: Binder:###  >>> /system/bin/surfaceflinger <<<
    signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
    Abort message: 'ubsan: sub-overflow'

O rastreamento de pilha deve incluir a função que causa a anulação; no entanto, os estouros que ocorrem em funções embutidas podem não ser evidentes no rastreamento de pilha.

Para determinar mais facilmente a causa raiz, ative o diagnóstico na biblioteca que aciona a anulação e tente reproduzir o erro. Com o diagnóstico ativado, o processo não será abortado e, em vez disso, continuará em execução. Não abortar ajuda a maximizar o número de overflows benignos em um caminho de execução específico, sem precisar recompilar após corrigir cada bug. O diagnóstico produz uma mensagem de erro que inclui o número da linha e o arquivo de origem que causa a anulação:

frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp:2188:32: runtime error: unsigned integer overflow: 0 - 1 cannot be represented in type 'size_t' (aka 'unsigned long')

Uma vez localizada a operação aritmética problemática, certifique-se de que o overflow seja benigno e intencional (por exemplo, não tem implicações de segurança). Você pode resolver o aborto do desinfetante:

  • Refatorando o código para evitar o overflow ( exemplo )
  • Overflow explicitamente por meio das funções __builtin__*_overflow do Clang ( exemplo )
  • Desativando a sanitização na função especificando o atributo no_sanitize ( exemplo )
  • Desativando a limpeza de uma função ou arquivo de origem por meio de um arquivo BLOCKLIST ( exemplo )

Você deve usar a solução mais granular possível. Por exemplo, uma função grande com muitas operações aritméticas e uma única operação de overflow deve ter a operação única refatorada em vez de toda a função ser BLOCKLISTed.

Padrões comuns que podem resultar em transbordamentos benignos incluem:

  • Conversões implícitas em que ocorre um estouro não assinado antes de ser convertido em um tipo assinado ( exemplo )
  • Exclusões de listas vinculadas que diminuem o índice do loop na exclusão ( exemplo )
  • Atribuindo um tipo não assinado a -1 em vez de especificar o valor máximo real ( exemplo )
  • Loops que decrementam um número inteiro sem sinal na condição ( exemplo , exemplo )

Recomenda-se que os desenvolvedores garantam que, nos casos em que o sanitizador detecta um estouro, ele seja realmente benigno, sem efeitos colaterais indesejados ou implicações de segurança, antes de desativar a higienização.

Desativando IntSan

Você pode desativar o IntSan com BLOCKLISTs ou atributos de função. Desative com moderação e somente quando refatorar o código não for razoável ou se houver sobrecarga de desempenho problemática.

Consulte a documentação upstream do Clang para obter mais informações sobre como desabilitar o IntSan com atributos de função e formatação de arquivo BLOCKLIST . A BLOCKLISTing deve ter como escopo o sanitizador específico usando nomes de seção especificando o sanitizante alvo para evitar impactar outros sanitizantes.

Validação

Atualmente, não há testes CTS específicos para higienização de estouro de número inteiro. Em vez disso, certifique-se de que os testes CTS sejam aprovados com ou sem o IntSan ativado para verificar se ele não está afetando o dispositivo.

Sanitização de Limites

BoundsSanitizer (BoundSan) adiciona instrumentação aos binários para inserir verificações de limites em torno dos acessos ao array. Essas verificações são adicionadas se o compilador não puder provar em tempo de compilação que o acesso será seguro e se o tamanho do array for conhecido em tempo de execução, para que possa ser verificado. O Android 10 implanta BoundSan em Bluetooth e codecs. BoundSan é fornecido pelo compilador e habilitado por padrão em vários componentes da plataforma.

Implementação

BoundSan usa o desinfetante de limites do UBSan . Essa mitigação é habilitada em nível por módulo. Ajuda a manter componentes críticos do Android seguros e não deve ser desativado.

Recomendamos fortemente que você habilite o BoundSan para componentes adicionais. Os candidatos ideais são código nativo privilegiado ou código nativo complexo que analisa entradas de usuários não confiáveis. A sobrecarga de desempenho associada à ativação do BoundSan depende do número de acessos ao array que não podem ser comprovados como seguros. Espere uma pequena porcentagem de sobrecarga em média e teste se o desempenho é uma preocupação.

Habilitando BoundSan em arquivos de blueprint

BoundSan pode ser habilitado em arquivos de blueprint adicionando "bounds" à propriedade sanitize misc_undefined para módulos binários e de biblioteca:

    sanitize: {
       misc_undefined: ["bounds"],
       diag: {
          misc_undefined: ["bounds"],
       },
       BLOCKLIST: "modulename_BLOCKLIST.txt",
diagnóstico

A propriedade diag ativa o modo de diagnóstico para os sanitizadores. Use o modo de diagnóstico somente durante o teste. O modo de diagnóstico não aborta em overflows, o que anula a vantagem de segurança da mitigação e acarreta uma sobrecarga de desempenho mais elevada, por isso não é recomendado para compilações de produção.

LISTA DE BLOQUEIO

A propriedade BLOCKLIST permite a especificação de um arquivo BLOCKLIST que os desenvolvedores podem usar para evitar que funções e arquivos de origem sejam limpos. Use esta propriedade somente se o desempenho for uma preocupação e os arquivos/funções de destino contribuírem substancialmente. Audite manualmente esses arquivos/funções para garantir que os acessos ao array sejam seguros. Consulte Solução de problemas para obter detalhes adicionais.

Habilitando BoundSan em makefiles

BoundSan pode ser habilitado em makefiles adicionando "bounds" à variável LOCAL_SANITIZE para módulos binários e de biblioteca:

    LOCAL_SANITIZE := bounds
    # Optional features
    LOCAL_SANITIZE_DIAG := bounds
    LOCAL_SANITIZE_BLOCKLIST := modulename_BLOCKLIST.txt

LOCAL_SANITIZE aceita uma lista de desinfetantes separados por vírgula.

LOCAL_SANITIZE_DIAG ativa o modo de diagnóstico. Use o modo de diagnóstico somente durante o teste. O modo de diagnóstico não aborta em overflows, o que anula a vantagem de segurança da mitigação e acarreta uma sobrecarga de desempenho mais elevada, por isso não é recomendado para compilações de produção.

LOCAL_SANITIZE_BLOCKLIST permite a especificação de um arquivo BLOCKLIST que permite aos desenvolvedores evitar que funções e arquivos de origem sejam higienizados. Use esta propriedade somente se o desempenho for uma preocupação e os arquivos/funções de destino contribuírem substancialmente. Audite manualmente esses arquivos/funções para garantir que os acessos ao array sejam seguros. Consulte Solução de problemas para obter detalhes adicionais.

Desativando BoundSan

Você pode desativar o BoundSan em funções e arquivos de origem com BLOCKLISTs ou atributos de função. É melhor manter o BoundSan ativado, portanto, desative-o apenas se a função ou arquivo estiver criando uma grande sobrecarga de desempenho e a fonte tiver sido revisada manualmente.

Para obter mais informações sobre como desabilitar BoundSan com atributos de função e formatação de arquivo BLOCKLIST , consulte a documentação do Clang LLVM. Defina o escopo da BLOCKLISTing para o desinfetante específico usando nomes de seção especificando o desinfetante de destino para evitar impactar outros desinfetantes.

Validação

Não há teste CTS específico para BoundSan. Em vez disso, certifique-se de que os testes CTS sejam aprovados com ou sem BoundSan habilitado para verificar se não está afetando o dispositivo.

Solução de problemas

Teste exaustivamente os componentes depois de ativar o BoundSan para garantir que todos os acessos fora dos limites não detectados anteriormente sejam resolvidos.

Os erros do BoundSan podem ser facilmente identificados, pois incluem a seguinte mensagem de cancelamento de exclusão:

    pid: ###, tid: ###, name: Binder:###  >>> /system/bin/foobar <<<
    signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
    Abort message: 'ubsan: out-of-bounds'

Ao executar no modo de diagnóstico, o arquivo de origem, o número da linha e o valor do índice são impressos no logcat . Por padrão, este modo não gera uma mensagem de cancelamento. Revise logcat para verificar se há erros.

    external/foo/bar.c:293:13: runtime error: index -1 out of bounds for type 'int [24]'