AddressSanitizer (ASan) é uma ferramenta rápida baseada em compilador para detectar erros de memória no código nativo.
ASan detecta:
- Estouro/subfluxo de buffer de pilha e heap
- Uso de pilha depois de gratuito
- Uso de pilha fora do escopo
- Duplo livre/livre livre
O ASan é executado em ARM de 32 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 (dependente de seus padrões de alocação, mas na ordem de 2x).
O Android 10 e o branch master AOSP no AArch64 suportam ASan acelerado por hardware (HWASan) , uma ferramenta semelhante com menor sobrecarga de RAM e uma variedade maior de bugs detectados. O HWASan detecta o uso da pilha após o retorno, além dos bugs detectados pelo ASan.
O HWASan tem uma sobrecarga de CPU e tamanho de código semelhante, mas uma sobrecarga de RAM muito menor (15%). HWASan é não determinístico. Existem apenas 256 valores de tag possíveis, portanto, há uma probabilidade fixa de 0,4% de perder algum bug. O HWASan não possui as zonas vermelhas de tamanho limitado do ASan para detectar estouros e a quarentena de capacidade limitada para detectar o uso após a liberação, portanto, não importa para o HWASan o tamanho do 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 .
O ASan detecta estouros de pilha/global, além de estouros de heap, e é rápido com sobrecarga mínima de memória.
Este documento descreve como construir e executar partes/todo o Android com ASan. Se você estiver criando um aplicativo SDK/NDK com ASan, consulte Address Sanitizer .
Sanitizando executáveis individuais com ASan
Adicione LOCAL_SANITIZE:=address
ou sanitize: { address: true }
à regra de compilação do executável. Você pode pesquisar o código para exemplos existentes ou para encontrar outros sanitizantes disponíveis.
Quando um bug é detectado, o ASan imprime um relatório detalhado tanto na saída padrão quanto no logcat
e, em seguida, trava o processo.
Higienizando bibliotecas compartilhadas com ASan
Devido à forma como o ASan funciona, uma biblioteca construída com ASan só pode ser usada por um executável construído com ASan.
Para limpar uma biblioteca compartilhada usada em vários executáveis, nem todos criados 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 presentes lendo /proc/$PID/maps
. Se não for, pode ser necessário desabilitar 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 ser muito intensivo em CPU, dependendo da carga.
Simbolização
Inicialmente, os relatórios ASan contêm referências a deslocamentos em binários e bibliotecas compartilhadas. Existem 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 (ou seja, localizações de file:line
) devido à disponibilidade de bibliotecas simbolizadas no host.
ASan em aplicativos
O 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á no momento em que você ler isso).
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 recuadas contendo class main
, também recuada pela mesma quantidade:
setenv LD_LIBRARY_PATH /system/lib/asan:/system/lib setenv ASAN_OPTIONS allow_user_segv_handler=true
Compilação, sincronização adb, inicialização flash fastboot e reinicialização.
Usando a propriedade wrap
A abordagem da seção anterior coloca o 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 ASan. Ele também adiciona /system/lib/asan
no início do caminho de pesquisa da biblioteca dinâmica. Desta forma, as bibliotecas instrumentadas ASan de /system/lib/asan
são preferidas às bibliotecas normais em /system/lib
quando executadas com asanwrapper
.
Se um bug for encontrado, o aplicativo trava 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, HWASan é uma escolha melhor.)
Execute os comandos a seguir na mesma árvore de compilação.
make -j42
SANITIZE_TARGET=address make -j42
Nesse modo, userdata.img
contém bibliotecas extras e também deve ser atualizado para o dispositivo. Use a seguinte linha de comando:
fastboot flash userdata && fastboot flashall
Isso cria dois conjuntos de bibliotecas compartilhadas: normal em /system/lib
(o primeiro faz invocação) e ASan-instrumentado em /data/asan/lib
(o segundo faz invocação). Os executáveis da segunda compilação substituem os da primeira compilação. Os executáveis instrumentados ASan obtêm um caminho de pesquisa de biblioteca diferente que inclui /data/asan/lib
antes /system/lib
através do uso de /system/bin/linker_asan
em PT_INTERP
.
O sistema de compilação sobrepõe os diretórios de objetos intermediários quando o valor $SANITIZE_TARGET
é alterado. Isso força uma reconstrução de todos os alvos enquanto preserva os binários instalados em /system/lib
.
Alguns destinos não podem ser criados com ASan:
- Executáveis vinculados estaticamente
-
LOCAL_CLANG:=false
-
LOCAL_SANITIZE:=false
não é ASan'd paraSANITIZE_TARGET=address
Executáveis como esses são ignorados na compilação SANITIZE_TARGET
, e a versão da primeira invocação do 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.