O AddressSanitizer (ASan) é uma ferramenta rápida baseada em compilador para detectar bugs de memória no código nativo.
O ASan detecta:
- Overflow/underflow do buffer de heap e pilha
- Uso de heap depois da liberação de memória
- Uso de pilha fora do escopo
- Double free/wild free
O ASan é executado em ARM de 32 e de 64 bits, além de x86 e x86-64. A sobrecarga de CPU do ASan é de aproximadamente duas vezes, a sobrecarga de tamanho do código está entre 50% e 100%, e a sobrecarga de memória é grande e depende dos padrões de alocação, mas é de cerca de duas vezes.
O Android 10 e a ramificação principal do AOSP no AArch64 oferecem suporte ao Limpador de endereços assistido 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 detectado pelo ASan.
O HWASan tem uma sobrecarga de tamanho de código e CPU semelhante, mas uma sobrecarga de RAM muito menor (15%). O HWASan não é determinista. Há somente 256 valores de tag possíveis, então há um valor fixo de 0,4% a probabilidade de perder bugs. O HWASan não tem zonas vermelhas de tamanho limitado do ASan para detectar overflows e quarentena de capacidade limitada para detectar uso após a liberação, portanto, não importa para o HWASan o tamanho do overflow ou há quanto tempo a memória foi desalocada. Isso torna o HWASan melhor que o ASan. Leia mais sobre o design do HWASan ou sobre o uso do HWASan no Android.
O ASan detecta overflows de pilha/globais e overflows de heap, além de ser rápido com sobrecarga de memória mínima.
Este documento descreve como criar e executar partes/todo o Android com ASan. Se você estiver criando um app SDK/NDK com o ASan, consulte Limpador de endereços como alternativa.
Limpar executáveis individuais com ASan
Adicione LOCAL_SANITIZE:=address
ou sanitize: { address: true }
à
regra de build do executável. Você pode pesquisar o código para encontrar exemplos ou
outros limpadores disponíveis.
Quando um bug é detectado, o ASan imprime um relatório detalhado na saída
padrão e no logcat
e, em seguida, interrompe o processo.
Limpar bibliotecas compartilhadas com o ASan
Devido à forma como o ASan funciona, uma biblioteca criada com ele só pode ser usada por um executável criado com o ASan.
Para limpar uma biblioteca compartilhada usada em vários executáveis, não em todos
que são compilados com o ASan, você precisa de duas cópias da biblioteca. A
maneira recomendada de fazer isso é adicionar o seguinte a 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 o 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, talvez seja necessário
para 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.
Stack traces melhores
O ASan usa um unwinder rápido baseado em ponteiro de frame para registrar um stack trace para cada evento de alocação e desalocação de memória no programa. A maior parte do Android é criada sem ponteiros de frame. Como resultado, muitas vezes você obtém apenas um ou dois frames significativos. Para corrigir isso, recrie 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. A última opção pode consumir muita CPU, dependendo
carga.
Simbolização
Inicialmente, os relatórios do ASan contêm referências a deslocamentos em arquivos binários e compartilhados bibliotecas. Há duas maneiras de conseguir informações sobre o arquivo de origem e a linha:
- Confira se o binário
llvm-symbolizer
está presente em/system/bin
. Ollvm-symbolizer
é criado a partir de fontes emthird_party/llvm/tools/llvm-symbolizer
. - Filtrar o relatório pelo
external/compiler-rt/lib/asan/scripts/symbolize.py
script.
A segunda abordagem pode fornecer mais dados (ou seja, file:line
locais) devido à
a disponibilidade de bibliotecas simbolizadas no host.
ASan em apps
O ASan não consegue acessar o código Java, mas pode detectar bugs nas bibliotecas
do JNI. Para isso, você precisa criar o executável com o ASan, que,
neste caso, é /system/bin/app_process(32|64)
. Isso
ativa o ASan em todos os apps do dispositivo ao mesmo tempo, o que é uma
sobrecarregada, mas um dispositivo com 2 GB de RAM consegue lidar com isso.
Adicione LOCAL_SANITIZE:=address
à
regra de build app_process
em frameworks/base/cmds/app_process
. Por enquanto, ignore
o destino app_process__asan
no mesmo arquivo (se ele ainda
estiver lá quando 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 que contêm class main
, também
recuotado pelo mesmo valor:
setenv LD_LIBRARY_PATH /system/lib/asan:/system/lib setenv ASAN_OPTIONS allow_user_segv_handler=true
Criação, sincronização do adb, inicialização de flash de fastboot e reinicialização.
Usar a propriedade de agrupamento
A abordagem na seção anterior coloca o ASan em cada aplicativo no sistema (na verdade, para cada descendente do Zigoto ou processo. É possível executar apenas um (ou vários) apps com o ASan, trocando parte da sobrecarga de memória por uma inicialização mais lenta do app.
Isso pode ser feito iniciando o app com a propriedade wrap.
.
O exemplo a seguir executa o app Gmail no 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 é criado com
ASan. Ele também adiciona /system/lib/asan
no início
o caminho de pesquisa da biblioteca dinâmica. Assim, o ASan instrumentou
são preferenciais as bibliotecas da /system/lib/asan
do que as bibliotecas normais
em /system/lib
quando executado com asanwrapper
.
Se um bug é encontrado, o app falha e o relatório é impresso no do registro.
SANITIZE_TARGET
O Android 7.0 e versões posteriores incluem suporte para a criação de toda a plataforma Android com ASan de uma vez. Se você estiver criando uma versão mais recente do que o Android 9, o HWASan é uma opção melhor.
Execute os comandos abaixo na mesma árvore de build.
make -j42
SANITIZE_TARGET=address make -j42
Nesse modo, userdata.img
contém bibliotecas extras e precisa 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 do Make) e instrumentado por ASan
/data/asan/lib
(a segunda invocação de make). Executáveis do
o segundo build vai substituir os do primeiro. Os executáveis
instrumentados pelo ASan recebem um caminho de pesquisa de biblioteca diferente que inclui
/data/asan/lib
antes de /system/lib
pelo uso de
/system/bin/linker_asan
em PT_INTERP
.
O sistema de build substitui diretórios de objetos intermediários quando o
valor $SANITIZE_TARGET
é alterado. Isso força uma recriação de todos
os destinos, preservando os binários instalados em /system/lib
.
Algumas metas não podem ser criadas com a ASan:
- Executáveis vinculados estaticamente
LOCAL_CLANG:=false
metaLOCAL_SANITIZE:=false
não está disponível paraSANITIZE_TARGET=address
Executáveis como esses são ignorados no build SANITIZE_TARGET
, e a
versão da primeira invocação de make é deixada em /system/bin
.
Bibliotecas como essa são criadas sem o ASan. Eles podem conter algum ASan das bibliotecas estáticas de que elas dependem.