Scudo

O Scudo é um alocador de memória dinâmico no modo de usuário, ou alocador de heap, projetado para ser resiliente contra vulnerabilidades relacionadas a heap (como overflow de buffer baseado em heap, uso após liberação e liberação dupla) sem afetar o desempenho. Ele fornece as primitivas padrão de alocação e desalocação de C (como malloc e free), bem como as primitivas de C++ (como new e delete).

O Scudo é mais uma mitigação do que um detector de erros de memória completo, como o AddressSanitizer (ASan).

Desde o lançamento do Android 11, o scudo é usado para todo o código nativo (exceto em dispositivos com pouca memória, em que jemalloc ainda é usado). Em tempo de execução, todas as alocações e desalocações de heap nativas são atendidas pelo Scudo para todos os executáveis e suas dependências de biblioteca. O processo é interrompido se uma corrupção ou um comportamento suspeito for detectado no heap.

O Scudo é de código aberto e faz parte do projeto compiler-rt do LLVM. A documentação está disponível em https://llvm.org/docs/ScudoHardenedAllocator.html. O tempo de execução do Scudo é enviado como parte da cadeia de ferramentas do Android, e o suporte foi adicionado ao Soong e ao Make para permitir a fácil ativação do alocador em um binário.

É possível ativar ou desativar a mitigação extra no alocador usando as opções descritas abaixo.

Personalização

Alguns parâmetros do alocador podem ser definidos por processo de várias maneiras:

  • Estaticamente:defina uma função __scudo_default_options no programa que retorne a string de opções a ser analisada. Essa função precisa ter o seguinte protótipo: extern "C" const char *__scudo_default_options().
  • Dinamicamente:use a variável de ambiente SCUDO_OPTIONS que contém a string de opções a ser analisada. As opções definidas dessa forma substituem qualquer definição feita por __scudo_default_options.

As seguintes opções estão disponíveis:

Opção Padrão de 64 bits Padrão de 32 bits Descrição
QuarantineSizeKb 256 64 O tamanho (em KB) da quarentena usada para atrasar a desalocação real de pedaços. Um valor menor pode reduzir o uso de memória, mas diminuir a eficácia da mitigação. Um valor negativo volta aos padrões. Definir os dois como zero desativa a quarentena completamente.ThreadLocalQuarantineSizeKb
QuarantineChunksUpToSize 2048 512 O tamanho (em bytes) até o qual os fragmentos podem ser colocados em quarentena.
ThreadLocalQuarantineSizeKb 64 16 O tamanho (em KB) do uso do cache por linha de execução para descarregar a quarentena global. Um valor menor pode reduzir o uso da memória, mas aumentar a disputa na quarentena global. Definir os dois como zero desativa totalmente a quarentena.QuarantineSizeKb
DeallocationTypeMismatch false false Ativa relatórios de erros em malloc/delete, new/free, new/delete[]
DeleteSizeMismatch true true Ativa a geração de relatórios de erros em caso de incompatibilidade entre os tamanhos de "new" e "delete".
ZeroContents false false Ativa o conteúdo de zero blocos na alocação e desalocação.
allocator_may_return_null false false Especifica que o alocador pode retornar nulo quando ocorre um erro recuperável, em vez de encerrar o processo.
hard_rss_limit_mb 0 0 Quando o RSS do processo atinge esse limite, ele é encerrado.
soft_rss_limit_mb 0 0 Quando o RSS do processo atinge esse limite, as alocações subsequentes falham ou retornam null (dependendo do valor de allocator_may_return_null), até que o RSS volte a diminuir para permitir novas alocações.
allocator_release_to_os_interval_ms 5000 N/A Afeta apenas um alocador de 64 bits. Se definido, tenta liberar memória não utilizada para o SO, mas não com mais frequência do que esse intervalo (em milissegundos). Se o valor for negativo, a memória não será liberada para o SO.
abort_on_error true true Se definido, a ferramenta vai chamar abort() em vez de _exit() depois de imprimir a mensagem de erro.

Validação

No momento, não há testes do CTS especificamente para o Scudo. Em vez disso, verifique se os testes do CTS são aprovados com ou sem o Scudo ativado para um determinado binário para verificar se ele não afeta o dispositivo.

Solução de problemas

Se um problema não recuperável for detectado, o alocador vai mostrar uma mensagem de erro no descritor de erro padrão e encerrar o processo. Os rastreamentos de pilha que levam à rescisão são adicionados ao registro do sistema. A saída geralmente começa com Scudo ERROR:, seguida por um breve resumo do problema e dicas.

Confira uma lista das mensagens de erro atuais e as possíveis causas:

  • corrupted chunk header: falha na verificação da soma de verificação do cabeçalho do fragmento. Isso provavelmente ocorre devido a um destes dois motivos: o cabeçalho foi substituído (parcial ou totalmente), ou o ponteiro transmitido para a função não é um bloco.
  • race on chunk header: duas linhas de execução diferentes estão tentando manipular o mesmo cabeçalho ao mesmo tempo. Isso geralmente é sintomático de uma condição de disputa ou falta geral de bloqueio ao realizar operações nesse pedaço.
  • invalid chunk state: o bloco não está no estado esperado para uma determinada operação. Por exemplo, ele não está alocado ao tentar liberá-lo ou não está em quarentena ao tentar reciclá-lo. Uma liberação dupla é o motivo típico desse erro.
  • misaligned pointer: os requisitos básicos de alinhamento são aplicados de forma rigorosa: 8 bytes em plataformas de 32 bits e 16 bytes em plataformas de 64 bits. Se um ponteiro transmitido às nossas funções não se encaixar nesses requisitos, o ponteiro transmitido a uma das funções estará desalinhado.
  • allocation type mismatch: quando essa opção está ativada, uma função de desalocação chamada em um bloco precisa corresponder ao tipo de função que foi chamada para alocá-lo. Esse tipo de incompatibilidade pode causar problemas de segurança.
  • invalid sized delete: quando o operador de exclusão dimensionada do C++14 é usado e a verificação opcional está ativada, há uma incompatibilidade entre o tamanho que foi transmitido ao desalocar um bloco e o tamanho solicitado ao alocá-lo. Normalmente, isso é um problema do compilador ou uma confusão de tipos no objeto que está sendo desalocado.
  • RSS limit exhausted: o RSS máximo especificado opcionalmente foi excedido.

Se você estiver depurando uma falha no próprio SO, use um build do SO HWASan. Se você estiver depurando uma falha em um app, também é possível usar um build de app HWASan.