AddressSanitizer (ASan) é uma ferramenta rápida baseada em compilador para detectar bugs de memória em código nativo.
ASan detecta:
- Estouro/subfluxo do buffer de pilha e heap
- Uso de pilha depois de livre
- Uso de pilha fora do escopo
- Duplo grátis/livre selvagem
ASan é executado em ARM de 32 bits e 64 bits, além de x86 e x86-64. A sobrecarga de CPU do ASan é aproximadamente 2x, a sobrecarga de tamanho de código está entre 50% e 2x e uma grande sobrecarga de memória (dependendo de seus padrões de alocação, mas na ordem de 2x).
O Android 10 e o ramo principal AOSP no AArch64 suportam ASan acelerado por hardware (HWASan) , uma ferramenta semelhante com menor sobrecarga de RAM e uma gama maior de bugs detectados. O HWASan detecta o uso da pilha após o retorno, além dos bugs detectados pelo ASan.
HWASan tem CPU semelhante e sobrecarga de tamanho de código, mas uma sobrecarga de RAM muito menor (15%). HWASan é não determinístico. Existem apenas 256 valores de tags possíveis, portanto, há uma probabilidade fixa de 0,4% de perder qualquer bug. O HWASan não tem zonas vermelhas de tamanho limitado do ASan para detectar estouros e quarentena de capacidade limitada para detectar uso após livre, então não importa para o HWASan quão grande é o estouro ou há quanto tempo a memória foi desalocada. Isso torna o HWASan melhor que o ASan. Você pode ler mais sobre o design do HWASan ou sobre o uso do HWASan no Android .
ASan detecta estouros de pilha/globais além de estouros de heap e é rápido com sobrecarga mínima de memória.
Este documento descreve como construir e executar partes/todas as partes do Android com ASan. Se você estiver criando um aplicativo SDK/NDK com ASan, consulte o Address Sanitizer .
Sanitizando executáveis individuais com ASan
Adicione LOCAL_SANITIZE:=address
ou sanitize: { address: true }
à regra de construção do executável. Você pode pesquisar no código exemplos existentes ou encontrar outros desinfetantes disponíveis.
Quando um bug é detectado, o ASan imprime um relatório detalhado na saída padrão e no logcat
e, em seguida, trava o processo.
Limpando bibliotecas compartilhadas com ASan
Devido à maneira como o ASan funciona, uma biblioteca criada com o ASan só pode ser usada por um executável criado com o ASan.
Para limpar uma biblioteca compartilhada usada em vários executáveis, nem todos construídos com ASan, você precisa de duas cópias da biblioteca. A maneira recomendada de fazer isso é adicionar o seguinte ao Android.mk
para o módulo em questão:
LOCAL_SANITIZE:=address LOCAL_MODULE_RELATIVE_PATH := asan
Isso coloca a biblioteca em /system/lib/asan
em vez de /system/lib
. Em seguida, execute seu executável com:
LD_LIBRARY_PATH=/system/lib/asan
Para daemons do sistema, adicione o seguinte à seção apropriada de /init.rc
ou /init.$device$.rc
.
setenv LD_LIBRARY_PATH /system/lib/asan
Verifique se o processo está usando bibliotecas de /system/lib/asan
quando presente lendo /proc/$PID/maps
. Se não estiver, pode ser necessário desativar o SELinux:
adb root
adb shell setenforce 0
# restart the process with adb shell kill $PID # if it is a system service, or may be adb shell stop; adb shell start.
Melhores rastreamentos de pilha
O ASan usa um desbobinador rápido baseado em ponteiro de quadro para registrar um rastreamento de pilha para cada evento de alocação e desalocação de memória no programa. A maior parte do Android é construída sem ponteiros de quadro. Como resultado, muitas vezes você obtém apenas um ou dois quadros significativos. Para corrigir isso, reconstrua a biblioteca com ASan (recomendado!), ou com:
LOCAL_CFLAGS:=-fno-omit-frame-pointer LOCAL_ARM_MODE:=arm
Ou defina ASAN_OPTIONS=fast_unwind_on_malloc=0
no ambiente do processo. O último pode exigir muito da CPU, dependendo da carga.
simbolização
Inicialmente, os relatórios ASan contêm referências a compensações em binários e bibliotecas compartilhadas. Há duas maneiras de obter o arquivo de origem e as informações de linha:
- Certifique-se de que o binário
llvm-symbolizer
esteja presente em/system/bin
.llvm-symbolizer
é construído a partir de fontes emthird_party/llvm/tools/llvm-symbolizer
. - Filtre o relatório por meio do script
external/compiler-rt/lib/asan/scripts/symbolize.py
.
A segunda abordagem pode fornecer mais dados (isto é, localizações file:line
) devido à disponibilidade de bibliotecas simbolizadas no host.
ASan em aplicativos
ASan não pode ver o código Java, mas pode detectar bugs nas bibliotecas JNI. Para isso, você precisa construir o executável com ASan, que neste caso é /system/bin/app_process( 32|64 )
. Isso habilita o ASan em todos os aplicativos do dispositivo ao mesmo tempo, o que é uma carga pesada, mas um dispositivo com 2 GB de RAM deve ser capaz de lidar com isso.
Adicione LOCAL_SANITIZE:=address
à regra de compilação app_process
em frameworks/base/cmds/app_process
. Ignore o destino app_process__asan
no mesmo arquivo por enquanto (se ainda estiver lá quando você ler isto).
Edite a seção service zygote
do arquivo system/core/rootdir/init.zygote( 32|64 ).rc
apropriado para adicionar as seguintes linhas ao bloco de linhas indentadas contendo a class main
, também indentado pelo mesmo valor:
setenv LD_LIBRARY_PATH /system/lib/asan:/system/lib setenv ASAN_OPTIONS allow_user_segv_handler=true
Construa, adb sync, fastboot flash boot e reinicie.
Usando a propriedade wrap
A abordagem da seção anterior coloca ASan em todos os aplicativos do sistema (na verdade, em todos os descendentes do processo Zygote). É possível executar apenas um (ou vários) aplicativos com ASan, trocando alguma sobrecarga de memória por uma inicialização mais lenta do aplicativo.
Isso pode ser feito iniciando seu aplicativo com o wrap.
propriedade. O exemplo a seguir executa o aplicativo Gmail em ASan:
adb root
adb shell setenforce 0 # disable SELinux
adb shell setprop wrap.com.google.android.gm "asanwrapper"
Nesse contexto, asanwrapper
reescreve /system/bin/app_process
para /system/bin/asan/app_process
, que é construído com o ASan. Ele também adiciona /system/lib/asan
no início do caminho de pesquisa da biblioteca dinâmica. Desta forma, as bibliotecas instrumentadas por ASan de /system/lib/asan
são preferidas às bibliotecas normais em /system/lib
ao executar com asanwrapper
.
Se um bug for encontrado, o aplicativo falha e o relatório é impresso no log.
SANITIZE_TARGET
O Android 7.0 e superior inclui suporte para construir toda a plataforma Android com ASan de uma só vez. (Se você estiver criando uma versão superior ao Android 9, o HWASan é uma escolha melhor.)
Execute os seguintes comandos na mesma árvore de construção.
make -j42
SANITIZE_TARGET=address make -j42
Nesse modo, userdata.img
contém bibliotecas extras e também deve ser atualizado no dispositivo. Use a seguinte linha de comando:
fastboot flash userdata && fastboot flashall
Isso cria dois conjuntos de bibliotecas compartilhadas: normal em /system/lib
(a primeira invocação de make) e instrumentada por ASan em /data/asan/lib
(a segunda invocação de make). Os executáveis da segunda compilação substituem os da primeira compilação. Os executáveis instrumentados pelo ASan obtêm um caminho de pesquisa de biblioteca diferente que inclui /data/asan/lib
antes de /system/lib
por meio do uso de /system/bin/linker_asan
em PT_INTERP
.
O sistema de construção destrói diretórios de objetos intermediários quando o valor $SANITIZE_TARGET
é alterado. Isso força a reconstrução de todos os destinos enquanto preserva os binários instalados em /system/lib
.
Alguns alvos não podem ser construídos com ASan:
- Executáveis vinculados estaticamente
-
LOCAL_CLANG:=false
-
LOCAL_SANITIZE:=false
não são ASan'd paraSANITIZE_TARGET=address
Executáveis como esses são ignorados na compilação SANITIZE_TARGET
e a versão da primeira chamada make é deixada em /system/bin
.
Bibliotecas como esta são construídas sem ASan. Eles podem conter algum código ASan das bibliotecas estáticas das quais dependem.