AddressSanitizer

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. Sobrecarga de CPU do ASan é aproximadamente o dobro, o overhead de tamanho do código está entre 50% e o dobro e uma grande sobrecarga de memória (dependendo dos seus padrões de alocação, mas na ordem de duas vezes).

Android 10 e a ramificação principal do AOSP no AArch64 oferecem suporte a AddressSanitizer assistido por hardware (HWASan), com uma ferramenta similar, com menor overhead de RAM 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 as zonas vermelhas de tamanho limitado do ASan para a detecção de estouros e quarentena de capacidade limitada para detectar então não importa para o HWASan o tamanho do estouro ou há quanto tempo a memória foi desalocado. Isso torna o HWASan melhor do que o ASan. Você pode ler mais sobre a design de HWASan ou sobre o uso do HWASan no Android.

O ASan detecta estouros globais/de pilha além dos estouros de heap, e é rápida com sobrecarga mínima de memória.

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 o ASan

Adicionar LOCAL_SANITIZE:=address ou sanitize: { address: true } a a regra de build do executável. Você pode pesquisar no código exemplos ou encontrar os outros desinfetantes disponíveis.

Quando um bug é detectado, o ASan gera um relatório detalhado na imagem padrão saída e para logcat e, em seguida, causa uma falha no processo.

Limpar bibliotecas compartilhadas com o ASan

Devido à forma como o ASan funciona, uma biblioteca criada com ASan 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

Verificar 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 frames para gravar uma pilha para cada evento de alocação e desalocação de memória no programa. Mais frequentes 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 processo de nuvem. 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 receber informações de linha e arquivo de origem:

  • Confira se o binário llvm-symbolizer está presente em /system/bin. llvm-symbolizer é criado a partir de origens em third_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 no JNI bibliotecas. 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.

Adicionar LOCAL_SANITIZE:=address a a regra de build app_process no frameworks/base/cmds/app_process. Ignorar o destino app_process__asan no mesmo arquivo por enquanto (se for ainda estava presente no momento em que você leu isto).

Edite a seção service zygote do arquivo system/core/rootdir/init.zygote(32|64).rc apropriado para adicionar o linhas seguintes para o bloco de linhas recuadas contendo class main, também recuado com o 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 flash do fastboot e reinicialização.

Usar a propriedade wrap

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 somente um (ou vários) apps com o ASan, trocando parte da sobrecarga de memória pela inicialização mais lenta do app.

Para fazer isso, inicie seu 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 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. Instrumentação de ASan os executáveis recebem um caminho de pesquisa de biblioteca diferente, que inclui /data/asan/lib antes de /system/lib com o uso de /system/bin/linker_asan em PT_INTERP.

Os clobbers do sistema de build afetam os diretórios de objetos intermediários quando o O valor de $SANITIZE_TARGET mudou. Isso força a reconstrução de todos destino, preservando os binários instalados em /system/lib.

Alguns destinos não podem ser criados com o ASan:

  • Executáveis vinculados estaticamente
  • LOCAL_CLANG:=false meta
  • LOCAL_SANITIZE:=false não foram cancelados em SANITIZE_TARGET=address

Executáveis como esses são ignorados no build SANITIZE_TARGET, e o A versão da primeira invocação do 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.

Documentação de apoio