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 queinteger_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]'