UndefinedBehaviorSanitizer

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

  • alinhamento
  • bool
  • limites
  • enumerar
  • float-cast-overflow
  • float-divide-por-zero
  • inteiro-dividido-por-zero
  • atributo não nulo
  • nulo
  • retornar
  • retorna-atributo-não-nulo
  • turno-base
  • shift-expoente
  • estouro de inteiro com sinal
  • inacessível
  • estouro de inteiro sem sinal
  • vla-bound

Unsigned-integer-overflow, embora não seja um comportamento tecnicamente indefinido, está incluído no higienizador e usado em muitos módulos do 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 habilitar o UBSan globalmente ou localmente. Para ativar o UBsan globalmente, defina SANITIZE_TARGET em Android.mk. Para ativar o UBSan em um nível por módulo, defina LOCAL_SANITIZE e especifique os comportamentos indefinidos que deseja procurar no 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 do UBSan

O Android também possui dois atalhos, integer e default-ub , para habilitar um conjunto de higienizadores ao mesmo tempo. integer permite integer-divide-by-zero , signed-integer-overflow e unsigned-integer-overflow . default-ub habilita as verificações que têm problemas mínimos de desempenho do compilador: bool, integer-divide-by-zero, return, returns-nonnull-attribute, shift-exponent, unreachable and vla-bound A classe do higienizador de número inteiro 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, arquivo e informações de linha de código-fonte. Para ativar este 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 higienizador durante a compilação. LOCAL_SANITIZE_DIAG ativa o modo de diagnóstico para o desinfetante especificado. É possível definir LOCAL_SANITIZE e LOCAL_SANITIZE_DIAG com valores diferentes, mas apenas as verificações em LOCAL_SANITIZE sã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 transbordamento inteiro

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

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

Exemplos e fonte

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

Implementação

O IntSan usa os higienizadores de estouro de inteiros assinados e não assinados do UBSan. Essa mitigação é habilitada em um nível por módulo. Ele ajuda a manter os componentes críticos do Android seguros e não deve ser desativado.

Recomendamos fortemente que você habilite a Sanitização de transbordamento inteiro para componentes adicionais. Os candidatos ideais são código nativo privilegiado ou código nativo que analisa a entrada do usuário não confiável. Há uma pequena sobrecarga de desempenho associada ao higienizador 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 separada por vírgulas de sanitizadores, com integer_overflow sendo um conjunto pré-empacotado de opções para os sanitizadores de estouro de inteiros assinados e não assinados individuais com um BLOCKLIST padrão .
  • LOCAL_SANITIZE_DIAG ativa o modo de diagnóstico para os higienizadores. Use o modo de diagnóstico somente durante o teste, porque isso não será interrompido em estouros, anulando 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 sanitizados. Consulte Solução de problemas para obter detalhes adicionais.

Se você deseja 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

Compatível com IntSan em arquivos de blueprint

Para habilitar a sanitização de estouro de número inteiro em um arquivo 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 higienizadores individuais de estouro de inteiros assinados e não assinados com um BLOCKLIST padrão .

O conjunto de propriedades diag habilita o modo de diagnóstico para os desinfetantes. Use o modo de diagnóstico apenas durante o teste. O modo de diagnóstico não é interrompido em estouros, o que anula 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 sanitizados. 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 estouro de inteiro em novos componentes, ou depender de bibliotecas de plataforma que tiveram sanitização de estouro de inteiro, você pode ter alguns problemas com estouros de inteiro benignos causando abortos. Você deve testar os componentes com sanitização habilitada para garantir que estouros benignos possam surgir.

Para encontrar aborts causados ​​por sanitização em builds de usuários, procure por travamentos SIGABRT com mensagens de Abort indicando um overflow detectado 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 está causando o cancelamento, no entanto, 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 o cancelamento e tente reproduzir o erro. Com os diagnósticos ativados, o processo não será abortado e, em vez disso, continuará em execução. Não abortar ajuda a maximizar o número de estouros benignos em um determinado caminho de execução sem ter que 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 está causando o cancelamento:

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')

Depois que a operação aritmética problemática for localizada, verifique se o estouro é benigno e intencional (por exemplo, não tem implicações de segurança). Você pode resolver o cancelamento do desinfetante por:

  • Refatorando o código para evitar o estouro ( exemplo )
  • Estouro 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 estouro deve ter a única operação refatorada em vez de toda a função 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 para um tipo assinado ( exemplo )
  • Exclusões de lista vinculada que diminuem o índice de 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 inteiro sem sinal na condição ( exemplo , exemplo )

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

Desabilitando IntSan

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

Consulte a documentação upstream do Clang para obter mais informações sobre como desativar IntSan com atributos de função e formatação de arquivo BLOCKLIST . BLOCKLISTing deve ter como escopo o desinfetante específico usando nomes de seção especificando o desinfetante de destino para evitar afetar outros desinfetantes.

Validação

Atualmente, não há nenhum teste de CTS especificamente para sanitização de transbordamento 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.

Higienização de Limites

BoundsSanitizer (BoundSan) adiciona instrumentação a binários para inserir verificações de limites em acessos de 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 da matriz for conhecido em tempo de execução, para que possa ser verificado. O Android 10 implanta o BoundSan em Bluetooth e codecs. O BoundSan é fornecido pelo compilador e é ativado por padrão em vários componentes da plataforma.

Implementação

BoundSan usa o desinfetante de limites da UBSan . Essa mitigação é habilitada em um nível por módulo. Ele ajuda a manter os 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 a entrada do usuário não confiável. 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.

Ativando BoundSan em arquivos de blueprint

O BoundSan pode ser ativado em arquivos de projeto adicionando "bounds" à propriedade de sanitização 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 habilita o modo de diagnóstico para os desinfetantes. Use o modo de diagnóstico apenas durante o teste. O modo de diagnóstico não é interrompido em estouros, o que anula a vantagem de segurança da mitigação e carrega uma sobrecarga de desempenho mais alta, portanto, não é recomendado para compilações de produção.

LISTA DE BLOQUEIOS

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 sanitizados. Use esta propriedade somente se o desempenho for uma preocupação e os arquivos/funções de destino contribuam substancialmente. Audite manualmente esses arquivos/funções para garantir que os acessos à matriz sejam seguros. Consulte Solução de problemas para obter detalhes adicionais.

Ativando BoundSan em makefiles

BoundSan pode ser ativado 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 higienizadores separados por vírgula.

LOCAL_SANITIZE_DIAG ativa o modo de diagnóstico. Use o modo de diagnóstico apenas durante o teste. O modo de diagnóstico não é interrompido em estouros, o que anula a vantagem de segurança da mitigação e carrega uma sobrecarga de desempenho mais alta, portanto, não é recomendado para compilações de produção.

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

Desabilitando o 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 o arquivo estiver criando uma grande quantidade de 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 afetar outros desinfetantes.

Validação

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

Solução de problemas

Teste minuciosamente 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 tombstone:

    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, esse 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]'