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 estouro de buffer baseado em heap, uso após a liberação, e liberação dupla) e manter a performance. 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 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 o jemalloc ainda é usado. No ambiente de execução, todas as alocações e desalocações de heap nativas são atendidas pelo Scudo para todos os executáveis e dependências de biblioteca, e o processo é interrompido se uma corrupção ou 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 (link em inglês). O ambiente de execução do Scudo é fornecido como parte do conjunto de ferramentas do Android, e o suporte foi adicionado ao Soong e ao Make para permitir a ativação fácil 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:

  • Estático: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().
  • Dinâmico: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 blocos. Um valor menor pode reduzir o uso da memória, mas diminuir a eficácia da mitigação. Um valor negativo volta aos padrões. Definir esse e ThreadLocalQuarantineSizeKb como zero desativa a quarentena completamente.
QuarantineChunksUpToSize 2048 512 O tamanho (em bytes) até o qual os blocos podem ser colocados em quarentena.
ThreadLocalQuarantineSizeKb 64 16 O tamanho (em KB) do uso de cache por linha de execução para descarregar a quarentena global. Um valor menor pode reduzir o uso da memória, mas pode aumentar a contenção na quarentena global. Definir esse e QuarantineSizeKb como zero desativa a quarentena completamente.
DeallocationTypeMismatch false false Ativa a geração de relatórios de erros em malloc/delete, new/free, new/delete[]
DeleteSizeMismatch true true Ativa a geração de relatórios de erros em incompatibilidades entre os tamanhos de new e delete.
ZeroContents false false Ativa o conteúdo de bloco zero 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, outras alocações falham ou retornam null (dependendo do valor de allocator_may_return_null), até que o RSS volte a permitir novas alocações.
allocator_release_to_os_interval_ms 5000 N/A Afeta apenas um alocador de 64 bits. Se definido, tenta liberar a 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 chama 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, em seguida, encerrar o processo. Os rastreamentos de pilha que levam ao encerramento são adicionados ao registro do sistema. A saída geralmente começa com Scudo ERROR: seguido por um breve resumo do problema e quaisquer ponteiros.

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

  • corrupted chunk header: a verificação de soma de verificação do cabeçalho do bloco falhou. Isso provavelmente ocorre devido a uma destas duas coisas: 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 corrida ou falta geral de bloqueio ao realizar operações nesse bloco.
  • invalid chunk state: o bloco não está no estado esperado para uma determinada operação. Por exemplo, ele não é alocado ao tentar liberá-lo ou não é colocado 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 com rigor: 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 ajustar a esses 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 gerar problemas de segurança.
  • invalid sized delete: quando o operador de exclusão dimensionada C++14 é usado e a verificação opcional está ativada, há uma incompatibilidade entre o tamanho transmitido ao desalocar um bloco e o tamanho solicitado ao alocá-lo. Esse é normalmente um problema do compilador ou uma confusão de tipo 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 uma build do SO do HWASan. Se você estiver depurando uma falha em um app, também é possível usar uma build do app do HWASan.