AddressSanitizer (ASan) é uma ferramenta rápida baseada em compilador para detectar bugs de memória em código nativo.
ASan detecta:
- Estouro/underflow de buffer de pilha e heap
- Heap use depois de graça
- Uso de pilha fora do escopo
- Duplo grátis/selvagem grátis
ASan é executado em ARM de 32 e 64 bits, além de x86 e x86-64. A sobrecarga da CPU do ASan é aproximadamente 2x, a sobrecarga do tamanho do código está entre 50% e 2x e uma grande sobrecarga de memória (dependendo dos seus padrões de alocação, mas na ordem de 2x).
O Android 10 e o branch 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 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 zonas vermelhas de tamanho limitado do ASan para detectar estouros e quarentena de capacidade limitada para detectar uso após 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 overflows de pilha/globais, além de heap overflows, 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 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 tanto na saída padrão quanto no logcat
e então trava o processo.
Sanitizando bibliotecas compartilhadas com ASan
Devido à forma como o ASan funciona, uma biblioteca construída 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 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
. Caso contrário, 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
ASan usa um desenrolador 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. Este último pode consumir muito a CPU, dependendo da carga.
Simbolização
Inicialmente, os relatórios ASan contêm referências a compensações em binários e bibliotecas compartilhadas. Existem duas maneiras de obter informações de arquivo e linha de origem:
- 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 consegue 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 construçã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ê leu isto).
Edite a seção service zygote
do system/core/rootdir/init.zygote( 32|64 ).rc
apropriado para adicionar as seguintes linhas ao bloco de linhas recuadas contendo class main
, também recuadas pela mesma quantidade:
setenv LD_LIBRARY_PATH /system/lib/asan:/system/lib setenv ASAN_OPTIONS allow_user_segv_handler=true
Construir, 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 no ASan:
adb root
adb shell setenforce 0 # disable SELinux
adb shell setprop wrap.com.google.android.gm "asanwrapper"
Neste 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 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 trava e o relatório é impresso no log.
SANITIZE_TARGET
O Android 7.0 e superior inclui suporte para a construção de 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
Neste 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
(a primeira invocação make) e instrumentada por ASan em /data/asan/lib
(a segunda invocação 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
através do uso de /system/bin/linker_asan
em PT_INTERP
.
O sistema de construção elimina diretórios de objetos intermediários quando o valor $SANITIZE_TARGET
é alterado. Isso força uma reconstrução de todos os destinos, preservando 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 é ASan paraSANITIZE_TARGET=address
Executáveis como esses são ignorados na compilação SANITIZE_TARGET
e a versão da primeira invocação 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.