O UndefinedBehaviorSanitizer (UBSan) realiza a instrumentação no 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 oferece suporte a:
- alinhamento
- bool
- limites
- enum
- float-cast-overflow
- float-divide-by-zero
- integer-divide-by-zero
- nonnull-attribute
- vazio
- volta
- returns-nonnull-attribute
- shift-base
- shift-exponent
- signed-integer-overflow
- não acessível
- unsigned-integer-overflow
- vla-bound
unsigned-integer-overflow, embora não seja tecnicamente um comportamento indefinido, está incluído no saneador e é usado em muitos módulos do Android, incluindo os componentes do mediaserver, para eliminar vulnerabilidades latentes de estouro de número inteiro.
Implementação
No sistema de build do Android, é possível ativar o UBSan de forma global ou local. Para ativar a UBSan globalmente, defina SANITIZE_TARGET em 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 em 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 sanitizadores ao mesmo tempo. O número 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 higienização de números inteiros pode ser usada com SANITIZE_TARGET e LOCAL_SANITIZE, enquanto default-ub só pode ser usado com SANITIZE_TARGET.
Melhor relatório de erros
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 é "abort". No entanto, a partir de outubro de 2016, a UBSan no Android tem uma biblioteca de tempo 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 a geração de relatórios 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 build. LOCAL_SANITIZE_DIAG ativa o modo de diagnóstico para o higienizador especificado. É possível definir LOCAL_SANITIZE e LOCAL_SANITIZE_DIAG com valores diferentes, mas apenas 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, ela não será ativada e as mensagens de diagnóstico não serão fornecidas.
Confira um exemplo das informações fornecidas pela biblioteca de tempo de execução do 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 os higienizadores de estouro de números inteiros assinados e não assinados do UndefinedBehaviorSanitizer (UBSan) do Clang para fortalecer o framework de mídia no Android 7.0. No Android 9, expandimos o UBSan para abranger mais componentes e melhoramos a compatibilidade do sistema de build com 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 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 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 ativar a limpeza de estouro de números inteiros para outros componentes. 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 saneador, 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.
Adicionar suporte ao 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_SANITIZEusa uma lista separada por vírgulas de sanitizadores, sendointeger_overflowum conjunto pré-empacotado de opções para os sanitizadores individuais de estouro de números inteiros com e sem sinal com uma LISTA DE BLOQUEIO padrão.LOCAL_SANITIZE_DIAGativa o modo de diagnóstico para os sanitizadores. Use o modo de diagnóstico apenas durante o teste, porque ele não interrompe em caso de estouros, negando completamente a vantagem de segurança da mitigação. Consulte Solução de problemas para mais detalhes.- O
LOCAL_SANITIZE_BLOCKLISTpermite especificar um arquivo de LISTA DE BLOQUEIO para impedir que funções e arquivos de origem sejam higienizados. Consulte Solução de problemas para mais detalhes.
Se você quiser um controle mais granular, ative os sanitizadores 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 ao 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 os arquivos make, a propriedade integer_overflow é um conjunto pré-empacotado de opções para os sanitizadores individuais de estouro de números inteiros com e sem sinal com uma LISTA DE BLOQUEIO padrão.
O conjunto de propriedades diag ativa o modo de diagnóstico para os
sanitizadores. Use o modo de diagnóstico apenas durante os testes. O modo de diagnóstico não
interrompe em caso de estouros, 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 impede que funções e arquivos de origem sejam
higienizados. Consulte Solução de problemas para
mais detalhes.
Para ativar os sanitizadores 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 ativando a limpeza de estouro de números inteiros em novos componentes ou depender de bibliotecas de plataforma que passaram por essa limpeza, poderá encontrar alguns problemas com estouros de números inteiros benignos causando encerramentos. Teste os componentes com a limpeza ativada para garantir que os estouros benignos possam ser detectados.
Para encontrar interrupções causadas pela limpeza em builds do usuário, pesquise
falhas SIGABRT com mensagens de interrupção indicando um estouro 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 precisa incluir a função que causou a interrupção. No entanto, estouros que ocorrem em funções inline podem não ser evidentes no rastreamento de pilha.
Para determinar a causa raiz com mais facilidade, ative os diagnósticos no gatilho da biblioteca, acione o encerramento e tente reproduzir o erro. Com o diagnóstico ativado, o processo não será interrompido e vai continuar sendo executado. Não interromper ajuda a maximizar o número de estouros benignos em um caminho de execução específico sem precisar recompilar depois de corrigir cada bug. O diagnóstico produz uma mensagem de erro que inclui o número da linha e o arquivo de origem que causou a interrupçã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')
Depois de localizar a operação aritmética problemática, verifique se o estouro é benigno e intencional (por exemplo, não tem implicações de segurança). Para resolver o aborto do higienizador, faça o seguinte:
- Refatorar o código para evitar o estouro (exemplo)
- Estouro explícito usando as funções __builtin_*_overflow do Clang (exemplo)
- Desativar a limpeza na função especificando o atributo
no_sanitize(exemplo) - Desativar a limpeza de uma função ou 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 deve ter a operação única refatorada em vez de toda a função na lista de bloqueio.
Padrões comuns que podem resultar em estouros benignos:
- Conversões implícitas em que um estouro sem sinal ocorre antes de ser convertido em um tipo com sinal (exemplo)
- Exclusões de lista vinculada que diminuem o índice do loop na exclusão (exemplo)
- Atribuir um tipo sem sinal a -1 em vez de especificar o valor máximo real (exemplo)
- Loops que diminuem um número inteiro sem sinal na condição (exemplo, exemplo)
Recomendamos que os desenvolvedores garantam que os casos em que o higienizador detecta um estouro sejam realmente benignos, sem efeitos colaterais ou implicações de segurança não intencionais, antes de desativar a higienização.
Desativar o IntSan
É possível desativar o IntSan com BLOCKLISTs ou atributos de função. Desative com moderação e somente quando a refatoração do código for inadequada ou 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 formatação de arquivo BLOCKLIST. A BLOCKLIST deve ser limitada ao higienizador específico usando nomes de seção que especificam o higienizador de destino para evitar afetar outros higienizadores.
Validação
No momento, não há um teste do CTS específico para a limpeza de estouro de números inteiros. Em vez disso, verifique se os testes do 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 aos 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 em codecs. O BoundSan é fornecido pelo compilador e ativado por padrão 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 ativar o BoundSan para outros componentes. Os candidatos ideais são código nativo privilegiado ou código nativo complexo que analisa entradas de usuários não confiáveis. O custo de desempenho associado à ativação do BoundSan depende do número de acessos a matrizes que não podem ser comprovadamente seguros. Espere uma pequena porcentagem de sobrecarga em média e teste se o desempenho é um problema.
Ativar o BoundSan em arquivos de blueprint
O BoundSan pode ser ativado em arquivos de blueprint adicionando "bounds"
à propriedade de limpeza 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 sanitizadores.
Use o modo de diagnóstico apenas durante os testes. O modo de diagnóstico não é interrompido em caso de estouros, o que anula a vantagem de segurança da mitigação e acarreta uma sobrecarga de desempenho maior. Por isso, não é recomendado para builds de produção.
BLOCKLIST
A propriedade BLOCKLIST permite a especificação de um arquivo de
LISTA DE BLOQUEIO 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 segmentados contribuírem substancialmente. Audite manualmente esses arquivos/funções para garantir que os acessos à 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 caso de estouros, o que nega a vantagem de segurança da mitigação e acarreta uma sobrecarga de desempenho maior. Por isso, não é recomendado para builds de produção.
LOCAL_SANITIZE_BLOCKLIST permite especificar um arquivo de BLOCKLIST
que impede 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 segmentados contribuírem substancialmente. Audite manualmente esses arquivos/funções para garantir que os acessos à matriz sejam seguros. Consulte Solução de problemas para mais detalhes.
Desativar o BoundSan
É possível desativar o BoundSan em funções e arquivos de origem com BLOCKLISTs ou atributos de função. É melhor manter o BoundSan ativado. 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 mais informações sobre como desativar o BoundSan com atributos de função e formatação de arquivos BLOCKLIST, consulte a documentação do Clang LLVM. Defina o escopo da LISTA DE BLOQUEIO para o higienizador específico usando nomes de seção que especificam o higienizador de destino para evitar afetar outros higienizadores.
Validação
Não há testes do CTS especificamente para o 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 completamente 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 porque incluem a seguinte mensagem de encerramento de encerramento:
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 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]'