Desinfetantes LLVM

LLVM, a infraestrutura do compilador usada para construir o Android, contém vários componentes que realizam análises estáticas e dinâmicas. Desses componentes, os sanitizantes — especificamente AddressSanitizer e UndefinedBehaviorSanitizer — podem ser usados ​​extensivamente para analisar o Android. Sanitizers são componentes de instrumentação baseados em compilador contidos em external/compiler-rt que podem ser usados ​​durante o desenvolvimento e teste para eliminar bugs e melhorar o Android. O conjunto atual de desinfetantes do Android pode descobrir e diagnosticar muitos erros de uso indevido de memória e comportamento indefinido potencialmente perigoso.

É uma prática recomendada que as compilações do Android sejam inicializadas e executadas com sanitizadores habilitados, como AddressSanitizer e UndefinedBehaviorSanitizer. Esta página apresenta AddressSanitizer, UndefinedBehaviorSanitizer e KernelAddressSanitizer, mostra como eles podem ser usados ​​no sistema de compilação do Android e dá exemplos de arquivos Android.mk e Android.bp que compilam componentes nativos com esses sanitizadores ativados.

Endereço Sanitizer

AddressSanitizer (ASan) é um recurso de instrumentação baseado em compilador que detecta muitos tipos de erros de memória no código C/C++ em tempo de execução. O ASan pode detectar muitas classes de erros de memória, incluindo:

  • Acesso à memória fora dos limites
  • Duplo grátis
  • Use-após-livre

O Android permite instrumentação ASan no nível de compilação completa e no nível do aplicativo com asanwrapper.

O AddressSanitizer combina a instrumentação de todas as chamadas de função relacionadas à memória — incluindo alloca, malloc e free — e preenche todas as variáveis ​​e regiões de memória alocadas com memória que aciona um retorno de chamada ASan quando é lido ou gravado.

A instrumentação permite que o ASan detecte erros de uso de memória inválidos, incluindo double-free e use-after scope, return e free, enquanto o preenchimento da região de memória detecta leituras ou gravações fora dos limites. Se ocorrer uma leitura ou gravação nessa região de preenchimento, o ASan a captura e emite informações para ajudar a diagnosticar a violação de memória, incluindo a pilha de chamadas, o mapa de memória sombra, o tipo de violação de memória, o que foi lido ou escrito, a instrução que causou o erro. violação e o conteúdo da memória.

pixel-xl:/ # sanitizer-status                                                                                            
=================================================================
==14164==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x0032000054b0 at pc 0x005df16ffc3c bp 0x007fc236fdf0 sp 0x007fc236fdd0
WRITE of size 1 at 0x0032000054b0 thread T0
    #0 0x5df16ffc3b in test_crash_malloc sanitizer-status/sanitizer-status.c:36:13
    #1 0x5df17004e3 in main sanitizer-status/sanitizer-status.c:76:7
    #2 0x794cf665f3 in __libc_init (/system/lib64/libc.so+0x1b5f3)
    #3 0x5df16ffa53 in do_arm64_start (/system/bin/sanitizer-status+0xa53)

0x0032000054b0 is located 0 bytes to the right of 32-byte region [0x003200005490,0x0032000054b0)
allocated by thread T0 here:
    #0 0x794d0bdc67 in malloc (/system/lib64/libclang_rt.asan-aarch64-android.so+0x74c67)
    #1 0x5df16ffb47 in test_crash_malloc sanitizer-status/sanitizer-status.c:34:25
    #2 0x5df17004e3 in main sanitizer-status/sanitizer-status.c:76:7
    #3 0x794cf665f3 in __libc_init (/system/lib64/libc.so+0x1b5f3)
    #4 0x5df16ffa53 in do_arm64_start (/system/bin/sanitizer-status+0xa53)
    #5 0x794df78893  (<unknown module>)

SUMMARY: AddressSanitizer: heap-buffer-overflow sanitizer-status/sanitizer-status.c:36:13 in test_crash_malloc

Às vezes, o processo de descoberta de bugs pode parecer não determinístico, especialmente para bugs que exigem configuração especial ou técnicas mais avançadas, como heap priming ou exploração de condição de corrida. Muitos desses bugs não são imediatamente aparentes e podem surgir milhares de instruções longe da violação de memória que foi a causa raiz real. O ASan instrumenta todas as funções relacionadas à memória e preenche os dados com áreas que não podem ser acessadas sem acionar um retorno de chamada do ASan. Isso significa que as violações de memória são detectadas no instante em que ocorrem, em vez de esperar por uma corrupção indutora de falhas. Isso é extremamente útil na descoberta de bugs e diagnóstico de causa raiz.

Para verificar se o ASAN está funcionando em um dispositivo de destino, o Android incluiu o executável asan_test. O executável asan_test testa e valida a funcionalidade ASAN em um dispositivo de destino, fornecendo mensagens de diagnóstico com o status de cada teste. Ao usar uma versão ASAN Android, ela está localizada em /data/nativetest/asan_test/asan_test ou /data/nativetest64/asan_test/asan_test por padrão.

UndefinedBehaviorSanitizer

UndefinedBehaviorSanitizer (UBSan) executa instrumentação em tempo de compilação para verificar vários tipos de comportamento indefinido. Enquanto o UBSan é capaz de detectar muitos comportamentos indefinidos , o Android suporta alinhamento, bool, limites, enum, float-cast-overflow, float-divide-by-zero, integer-divide-by-zero, nonnull-attribute, null, return, return-nonnull-attribute, shift-base, shift-expponent, sign-integer-overflow, unreachable, unsigned-integer-overflow e vla-bound. unsigned-integer-overflow, embora não seja tecnicamente um comportamento indefinido, está incluído no desinfetante e usado em muitos módulos do Android, incluindo os componentes do mediaserver, para eliminar quaisquer vulnerabilidades latentes de estouro de inteiros.

Implementação

No sistema de compilação do Android, você pode habilitar o UBSan globalmente ou localmente. Para habilitar o UBSan globalmente, defina SANITIZE_TARGET em Android.mk. Para habilitar o UBSan em um nível por módulo, defina LOCAL_SANITIZE e especifique os comportamentos indefinidos que você deseja procurar em Android.mk. Por exemplo:

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_CFLAGS := -std=c11 -Wall -Werror -O0

LOCAL_SRC_FILES:= sanitizer-status.c

LOCAL_MODULE:= sanitizer-status

LOCAL_SANITIZE := alignment bounds null unreachable integer
LOCAL_SANITIZE_DIAG := alignment bounds null unreachable integer

include $(BUILD_EXECUTABLE)

O sistema de compilação do Android ainda não oferece suporte a diagnósticos tão detalhados em arquivos de blueprint quanto em makefiles. Aqui está o equivalente mais próximo escrito como um blueprint (Android.bp):

cc_binary {

    cflags: [
        "-std=c11",
        "-Wall",
        "-Werror",
        "-O0",
    ],

    srcs: ["sanitizer-status.c"],

    name: "sanitizer-status",

    sanitize: {
        misc_undefined: [
            "alignment",
            "bounds",
            "null",
            "unreachable",
            "integer",
        ],
        diag: {
            undefined : true
        },
    },

}

Atalhos do UBSan

O Android também possui dois atalhos, integer e default-ub , para habilitar um conjunto de desinfetantes ao mesmo tempo. integer habilita integer-divide-by-zero , signed-integer-overflow e unsigned-integer-overflow . default-ub habilita as verificações que têm problemas mínimos de desempenho do compilador: bool, integer-divide-by-zero, return, return-nonnull-attribute, shift-expponent, unreachable e vla-bound. A classe de limpeza inteira pode ser usada com SANITIZE_TARGET e LOCAL_SANITIZE, enquanto default-ub só pode ser usada com SANITIZE_TARGET.

Melhor relatório de erros

A implementação padrão do UBSan do Android invoca uma função especificada quando um comportamento indefinido é encontrado. Por padrão, esta função é abortada. No entanto, a partir de outubro de 2016, o UBSan no Android tem uma biblioteca de tempo de execução opcional que fornece relatórios de erros mais detalhados, incluindo o tipo de comportamento indefinido encontrado, informações de arquivo e linha de código-fonte. Para ativar esse relatório de erros com verificações de números inteiros, adicione o seguinte a um arquivo Android.mk:

LOCAL_SANITIZE:=integer
LOCAL_SANITIZE_DIAG:=integer

O valor LOCAL_SANITIZE habilita o desinfetante durante a compilação. LOCAL_SANITIZE_DIAG ativa o modo de diagnóstico para o sanitizador especificado. É possível definir LOCAL_SANITIZE e LOCAL_SANITIZE_DIAG para valores diferentes, mas apenas as verificações em LOCAL_SANITIZE são habilitadas. Se uma verificação não for especificada em LOCAL_SANITIZE, mas for especificada em LOCAL_SANITIZE_DIAG, a verificação não será habilitada e as mensagens de diagnóstico não serão fornecidas.

Aqui está um exemplo das informações fornecidas pela biblioteca de tempo de execução UBSan:

pixel-xl:/ # sanitizer-status ubsan
sanitizer-status/sanitizer-status.c:53:6: runtime error: unsigned integer overflow: 18446744073709551615 + 1 cannot be represented in type 'size_t' (aka 'unsigned long')

Desinfetante de endereço do kernel

Semelhante aos sanitizadores baseados em LLVM para componentes de espaço do usuário, o Android inclui o Kernel Address Sanitizer (KASAN). O KASAN é uma combinação de modificações de kernel e tempo de compilação que resultam em um sistema instrumentado que permite a descoberta de bugs e a análise de causa raiz mais simples.

O KASAN pode detectar muitos tipos de violações de memória no kernel. Ele também pode detectar leituras e gravações fora do limite na pilha, heap e variáveis ​​globais e pode detectar liberações de uso após a liberação e duplas.

Semelhante ao ASAN, o KASAN usa uma combinação de instrumentação de função de memória em tempo de compilação e memória sombra para rastrear acessos à memória em tempo de execução. No KASAN, um oitavo do espaço de memória do kernel é dedicado à memória sombra, que determina se um acesso à memória é válido ou não.

O KASAN é compatível com as arquiteturas x86_64 e arm64. Ele faz parte do kernel upstream desde a versão 4.0 e foi portado para kernels baseados no Android 3.18. KASAN foi testado em kernels Android compilados com gcc com base em 4.9.2.

Além do KASAN, o kcov é outra modificação do kernel que é útil para testes. O kcov foi desenvolvido para permitir testes fuzz guiados por cobertura no kernel. Ele mede a cobertura em termos de entradas syscall e é útil com sistemas fuzzing, como syzkaller .

Implementação

Para compilar um kernel com KASAN e kcov habilitados, adicione os seguintes sinalizadores de compilação à configuração de compilação do kernel:

CONFIG_KASAN 
CONFIG_KASAN_INLINE 
CONFIG_TEST_KASAN 
CONFIG_KCOV 
CONFIG_SLUB 
CONFIG_SLUB_DEBUG 
CONFIG_CC_OPTIMIZE_FOR_SIZE

E removendo o seguinte:

CONFIG_SLUB_DEBUG_ON 
CONFIG_SLUB_DEBUG_PANIC_ON 
CONFIG_KASAN_OUTLINE 
CONFIG_KERNEL_LZ4

Em seguida, construa e atualize seu kernel como de costume. O kernel KASAN é consideravelmente maior que o original. Se aplicável, modifique quaisquer parâmetros de inicialização e configurações do carregador de inicialização para levar isso em consideração.

Depois de atualizar o kernel, verifique os logs de inicialização do kernel para ver se o KASAN está ativado e em execução. O kernel iniciará com informações do mapa de memória para KASAN, como:

...
[    0.000000] c0      0 Virtual kernel memory layout:
[    0.000000] c0      0     kasan   : 0xffffff8000000000 - 0xffffff9000000000   (    64 GB)
[    0.000000] c0      0     vmalloc : 0xffffff9000010000 - 0xffffffbdbfff0000   (   182 GB)
[    0.000000] c0      0     vmemmap : 0xffffffbdc0000000 - 0xffffffbfc0000000   (     8 GB maximum)
[    0.000000] c0      0               0xffffffbdc0000000 - 0xffffffbdc3f95400   (    63 MB actual)
[    0.000000] c0      0     PCI I/O : 0xffffffbffa000000 - 0xffffffbffb000000   (    16 MB)
[    0.000000] c0      0     fixed   : 0xffffffbffbdfd000 - 0xffffffbffbdff000   (     8 KB)
[    0.000000] c0      0     modules : 0xffffffbffc000000 - 0xffffffc000000000   (    64 MB)
[    0.000000] c0      0     memory  : 0xffffffc000000000 - 0xffffffc0fe550000   (  4069 MB)
[    0.000000] c0      0       .init : 0xffffffc001d33000 - 0xffffffc001dce000   (   620 KB)
[    0.000000] c0      0       .text : 0xffffffc000080000 - 0xffffffc001d32284   ( 29385 KB)
...

E é assim que um bug ficará:

[   18.539668] c3      1 ==================================================================
[   18.547662] c3      1 BUG: KASAN: null-ptr-deref on address 0000000000000008
[   18.554689] c3      1 Read of size 8 by task swapper/0/1
[   18.559988] c3      1 CPU: 3 PID: 1 Comm: swapper/0 Tainted: G        W      3.18.24-xxx #1
[   18.569275] c3      1 Hardware name: Android Device
[   18.577433] c3      1 Call trace:
[   18.580739] c3      1 [<ffffffc00008b32c>] dump_backtrace+0x0/0x2c4
[   18.586985] c3      1 [<ffffffc00008b600>] show_stack+0x10/0x1c
[   18.592889] c3      1 [<ffffffc001481194>] dump_stack+0x74/0xc8
[   18.598792] c3      1 [<ffffffc000202ee0>] kasan_report+0x11c/0x4d0
[   18.605038] c3      1 [<ffffffc00020286c>] __asan_load8+0x20/0x80
[   18.611115] c3      1 [<ffffffc000bdefe8>] android_verity_ctr+0x8cc/0x1024
[   18.617976] c3      1 [<ffffffc000bcaa2c>] dm_table_add_target+0x3dc/0x50c
[   18.624832] c3      1 [<ffffffc001bdbe60>] dm_run_setup+0x50c/0x678
[   18.631082] c3      1 [<ffffffc001bda8c0>] prepare_namespace+0x44/0x1ac
[   18.637676] c3      1 [<ffffffc001bda170>] kernel_init_freeable+0x328/0x364
[   18.644625] c3      1 [<ffffffc001478e20>] kernel_init+0x10/0xd8
[   18.650613] c3      1 ==================================================================

Além disso, se os módulos estiverem habilitados em seu kernel, você pode carregar o módulo test_kasan do kernel para testes adicionais. O módulo tenta acessos de memória fora dos limites e uso após livre e é útil para testar o KASAN em um dispositivo de destino.