UndefinedBehaviorSanitizer

O UndefinedBehaviorSanitizer (UBSan) executa a instrumentação no momento da compilação para verificar vários tipos de comportamento indefinido. Embora o UBSan seja capaz de detectar muitos bugs de comportamento indefinido, o Android oferece suporte a:

  • alinhamento
  • booleano
  • limites
  • enum
  • float-cast-overflow
  • float-divide-by-zero
  • integer-divide-by-zero
  • atributo-não-nulo
  • vazio
  • volta
  • returns-nonnull-attribute
  • shift-base
  • shift-exponent
  • signed-integer-overflow
  • não acessível
  • unsigned-integer-overflow
  • vla-bound

O overflow de número inteiro sem sinal, embora não seja um comportamento tecnicamente indefinido, é incluído no limpador e usado em muitos módulos do Android, incluindo os componentes do mediaserver, para eliminar qualquer vulnerabilidade latente de overflow de número inteiro.

Implementação

No sistema de build do Android, é possível ativar o UBSan globalmente ou localmente. Para ativar o UBsan globalmente, defina SANITIZE_TARGET no Android.mk. Para ativar o UBSan no nível de cada módulo, defina LOCAL_SANITIZE e especifique os comportamentos indefinidos que você quer procurar no Android.mk. 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 tem dois atalhos, integer e default-ub, para ativar um conjunto de limpadores ao mesmo tempo. O inteiro ativa integer-divide-by-zero, signed-integer-overflow e unsigned-integer-overflow. default-ub ativa 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 de limpador de números inteiros pode ser usada com SANITIZE_TARGET e LOCAL_SANITIZE, enquanto default-ub só pode ser usado com SANITIZE_TARGET.

Geração de relatórios de erros aprimorada

A implementação padrão do UBSan do Android invoca uma função especificada quando um comportamento indefinido é encontrado. Por padrão, essa função é abortada. No entanto, a partir de outubro de 2016, o UBSan no Android tem uma biblioteca de execução opcional que oferece relatórios de erros mais detalhados, incluindo o tipo de comportamento indefinido encontrado, informações de arquivo e linha de código-fonte. Para ativar esse relatório de erro 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 limpador durante o build. LOCAL_SANITIZE_DIAG ativa o modo de diagnóstico para o limpador especificado. É possível definir LOCAL_SANITIZE e LOCAL_SANITIZE_DIAG como valores diferentes, mas somente as verificações em LOCAL_SANITIZE são ativadas. Se uma verificação não for especificada em LOCAL_SANITIZE, mas for especificada em LOCAL_SANITIZE_DIAG, a verificação não será ativada e as mensagens de diagnóstico não serão exibidas.

Confira um exemplo das informações fornecidas pela biblioteca 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')

Limpeza de estouro de números inteiros

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 o UndefinedBehaviorSanitizer (UBSan) do Clang para higienizadores de estouro de números inteiros assinados e não assinados para fortalecer o framework de mídia no Android 7.0. No Android 9, ampliamos o UBSan para abranger mais componentes e melhoramos o suporte do sistema de build para ele.

Isso foi criado para adicionar verificações em operações/instruções aritméticas, que podem estourar, e cancelar com segurança um processo caso um estouro ocorra. Esses higienizadores podem mitigar uma classe inteira 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 original do Stagefright.

Exemplos e origem

A limpeza de estouro de números inteiros (IntSan) é fornecida pelo compilador e adiciona instrumentação ao binário durante a 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 limpadores de estouro de números inteiros assinados e não assinados do UBSan. Essa atenuação é ativada módulo a módulo. Ela ajuda a proteger os componentes críticos do Android e não pode ser desativada.

Recomendamos que você ative a limpeza de estouro de números inteiros para outros componentes. Os candidatos ideais são códigos nativos privilegiados ou códigos nativos que analisam entradas de usuário não confiáveis. 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 a performance é uma preocupação.

Suporte a IntSan em makefiles

Para ativar 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 recebe uma lista de limpadores separada por vírgulas, em que integer_overflow é um conjunto pré-empacotado de opções para os limpadores de overflow de inteiros assinados e não assinados individuais com uma lista de blocos padrão.
  • LOCAL_SANITIZE_DIAG ativa o modo de diagnóstico para os limpadores. Use o modo de diagnóstico apenas durante o teste, porque ele não será interrompido em casos de overflow, anulando completamente a vantagem de segurança da mitigação. Consulte Solução de problemas para mais detalhes.
  • LOCAL_SANITIZE_BLOCKLIST permite especificar um arquivo de BLOCKLIST para impedir que funções e arquivos de origem sejam limpos. Consulte Solução de problemas para mais detalhes.

Se você quiser um controle mais granular, ative os limpadores individualmente usando uma ou ambas as flags:

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

Suporte a IntSan em arquivos de blueprint

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

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

Assim como nos arquivos de make, a propriedade integer_overflow é um conjunto preparado de opções para os limpadores de overflow de inteiros assinados e não assinados individuais com uma lista de bloqueio default.

O conjunto de propriedades diag ativa o modo de diagnóstico para os limpadores. Use o modo de diagnóstico apenas durante os testes. O modo de diagnóstico não é interrompido em casos de overflow, o que anula completamente a vantagem de segurança da mitigação em builds do usuário. Consulte Solução de problemas para mais detalhes.

A propriedade BLOCKLIST permite especificar um arquivo de lista de bloqueio que permite que os desenvolvedores impeçam que funções e arquivos de origem sejam higienizados. Consulte Solução de problemas para mais detalhes.

Para ativar os limpadores 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ê ativar a limpeza de estouro de inteiros em novos componentes ou usar bibliotecas de plataforma que tiveram limpeza de estouro de inteiros, poderá encontrar alguns problemas com estouros de inteiros benignos que causam interrupções. Teste componentes com a limpeza ativada para garantir que os overflows benignos possam ser exibidos.

Para encontrar falhas causadas pela limpeza em builds do usuário, procure SIGABRT com mensagens de abortamento 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 stack trace precisa incluir a função que está causando o abort. No entanto, os overflows que ocorrem em funções inline podem não ser evidentes no stack trace.

Para determinar a causa raiz com mais facilidade, ative o diagnóstico na biblioteca que está causando o abortamento e tente reproduzir o erro. Com a ativação do diagnóstico, o processo não será interrompido e continuará sendo executado. 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 o abortamento:

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 de localizar a operação aritmética problemática, verifique se o overflow é benigno e intencional (por exemplo, não tem implicações de segurança). Você pode resolver o encerramento do limpador:

  • Refatorar o código para evitar o overflow (exemplo)
  • Fluxo de retorno explícito usando as funções __builtin_*_overflow do Clang (exemplo).
  • Como desativar a limpeza na função especificando o atributo no_sanitize (exemplo)
  • Desativação da limpeza de uma função ou de um arquivo de origem usando um arquivo BLOCKLIST (exemplo)

Use 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 precisa ter a única operação refatorada em vez de toda a função BLOQUEADA.

Os padrões comuns que podem resultar em transbordamentos benignos incluem:

  • Casts implícitos em que um overflow não assinado ocorre antes de ser convertido em um tipo assinado (exemplo).
  • Exclusão de listas vinculadas que decrementa o índice do loop na exclusão (exemplo).
  • Atribuir um tipo não assinado a -1 em vez de especificar o valor máximo real (exemplo).
  • Laços que diminuem um número inteiro não assinado na condição (exemplo, exemplo).

Recomenda-se que os desenvolvedores garantam que os casos em que o limpador detecta um overflow sejam realmente benignos, sem efeitos colaterais ou implicações de segurança indesejadas antes de desativar a limpeza.

Desativar o IntSan

É possível desativar o IntSan com listas de bloqueio ou atributos de função. Desative com moderação e somente quando a refatoração do código for irracional ou se houver uma sobrecarga de desempenho problemática.

Consulte a documentação upstream do Clang para mais informações sobre como desativar o IntSan com atributos de função e a formatação de arquivos BLOCKLIST. A lista de bloqueio precisa ser aplicada ao limpador específico usando nomes de seção que especificam o limpador de destino para evitar o impacto em outros limpadores.

Validação

No momento, não há um teste CTS específico para a limpeza de estouro de números inteiros. Em vez disso, verifique se os testes de CTS são aprovados com ou sem o IntSan ativado para verificar se ele não está afetando o dispositivo.

Sanitização de limites

O BoundsSanitizer (BoundSan) adiciona instrumentação a binários para inserir verificações de limites em torno de acessos de matriz. Essas verificações são adicionadas quando o compilador não consegue provar no tempo de compilação que o acesso será seguro e que o tamanho da matriz será conhecido durante a execução, para que possa ser verificado. O Android 10 implanta o BoundSan no Bluetooth e nos codecs. O BoundSan é fornecido pelo compilador e ativado por default em vários componentes na plataforma.

Implementação

O BoundSan usa o limpador de limites do UBSan. Essa atenuação é ativada por módulo. Ela ajuda a proteger os componentes críticos do Android e não pode ser desativada.

Recomendamos que você ative o BoundSan para outros componentes. Os candidatos ideais são códigos nativos privilegiados ou códigos nativos complexos que analisam entradas de usuário não confiáveis. O overhead de desempenho associado à ativação do BoundSan depende do número de acessos de matriz que não podem ser considerados seguros. Espere uma pequena porcentagem de sobrecarga em média e teste se o desempenho é uma preocupação.

Ativar o BoundSan em arquivos de blueprint

O BoundSan pode ser ativado em arquivos de blueprint 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",
diag

A propriedade diag ativa o modo de diagnóstico para os desinfetantes. Use o modo de diagnóstico apenas durante os testes. O modo de diagnóstico não é interrompido em casos de overflow, o que anula a vantagem de segurança da mitigação e aumenta a sobrecarga de desempenho. Portanto, ele não é recomendado para builds de produção.

LISTA DE BLOQUEIO

A propriedade BLOCKLIST permite a especificação de um arquivo de BLACKLIST que os desenvolvedores podem usar para impedir que funções e arquivos de origem sejam limpos. Use essa propriedade apenas se o desempenho for uma preocupação e os arquivos/funções de destino contribuírem de forma significativa. Faça a auditoria manual desses arquivos/funções para garantir que os acessos de matriz sejam seguros. Consulte Solução de problemas para mais detalhes.

Ativar o BoundSan em makefiles

O 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 uma vírgula.

LOCAL_SANITIZE_DIAG ativa o modo de diagnóstico. Use o modo de diagnóstico apenas durante os testes. O modo de diagnóstico não é interrompido em casos de overflow, o que anula a vantagem de segurança da mitigação e aumenta a sobrecarga de desempenho. Portanto, ele não é recomendado para builds de produção.

LOCAL_SANITIZE_BLOCKLIST permite a especificação de um arquivo de BLACKLIST, que permite que os desenvolvedores impeçam que funções e arquivos de origem sejam higienizados. Use essa propriedade apenas se o desempenho for uma preocupação e os arquivos/funções de destino contribuírem de forma significativa. Faça a auditoria manual desses arquivos/funções para garantir que os acessos de matriz sejam seguros. Consulte Solução de problemas para mais detalhes.

Desativar BoundSan

É possível desativar o BoundSan em funções e arquivos de origem com listas de bloqueio ou atributos de função. É melhor manter o BoundSan ativado. Só desative se a função ou o arquivo estiver criando uma grande quantidade de sobrecarga de desempenho e a origem tiver sido analisada manualmente.

Para mais informações sobre como desativar o BoundSan com atributos de função e a formatação de arquivos BLOCKLIST, consulte a documentação do Clang LLVM. Defina o escopo da BLACKLIST para o limpador específico usando nomes de seção que especificam o limpador de destino para evitar o impacto em outros limpadores.

Validação

Não há um teste CTS específico para BoundSan. Em vez disso, verifique se os testes do CTS são aprovados com ou sem o BoundSan ativado para verificar se ele não está afetando o dispositivo.

Solução de problemas

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

Os erros de BoundSan podem ser facilmente identificados, porque incluem a seguinte mensagem de interrupção de túmulo:

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

Quando executado no modo de diagnóstico, o arquivo de origem, o número da linha e o valor do índice são impressos em logcat. Por padrão, esse modo não gera uma mensagem de interrupção. Analise logcat para verificar se há erros.

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