Scudo

O Scudo é um alocador de memória dinâmico no modo de usuário ou alocador de heap, projetado para ser resilient contra vulnerabilidades relacionadas a heap (como overflow de buffer baseado em heap, uso após a liberação e liberação dupla), mantendo 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 totalmente desenvolvido, 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 o jemalloc ainda é usado. No momento da execução, todas as alocações e desalocações de heap nativas são atendidas pelo Scudo para todos os executáveis e as dependências de biblioteca. O processo é abortado se um comportamento corrompido ou suspeito for detectado no heap.

O Scudo tem 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 ambiente 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 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:

  • 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().
  • Dinâmica:use a variável de ambiente SCUDO_OPTIONS que contém a string de opções a ser analisada. As opções definidas dessa maneira 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 fragmentos. Um valor menor pode reduzir o uso de memória, mas diminuir a eficácia da mitigação. Um valor negativo retorna aos padrões. Definir esse valor e ThreadLocalQuarantineSizeKb como zero desativa a quarentena por completo.
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 do cache por linha de execução para reduzir a quarentena global. Um valor menor pode reduzir o uso da memória, mas aumentar a contenção na quarentena global. Definir esse valor e QuarantineSizeKb como zero desativa a quarentena completamente.
DeallocationTypeMismatch false false Ativa o relatório de erros em malloc/delete, new/free, new/delete[]
DeleteSizeMismatch true true Ativa o relatório de erros em incompatibilidade entre os tamanhos de novo e exclusão.
ZeroContents false false Ativa o conteúdo de blocos nulos na alocação e desalocação.
allocator_may_return_null false false Especifica que o alocador pode retornar um valor nulo quando ocorrer um erro recuperável, em vez de encerrar o processo.
hard_rss_limit_mb 0 0 Quando o RSS do processo atinge esse limite, o processo é 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 cair para permitir novas alocações.
allocator_release_to_os_interval_ms N/A 5000 Afeta apenas um alocador de 64 bits. Se definido, tenta liberar a memória não utilizada para o SO, mas não com uma frequência maior 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() após imprimir a mensagem de erro.

Validação

No momento, não há testes CTS específicos 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 para o descritor de erro padrão e 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:, seguida por um breve resumo do problema e por qualquer indicação.

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

  • corrupted chunk header: a verificação da soma de verificação do cabeçalho do fragmento falhou. Isso provavelmente se deve a uma destas duas coisas: o cabeçalho foi sobrescrito (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 é um sintoma 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 é a causa comum desse erro.
  • misaligned pointer: requisitos básicos de alinhamento são bastante aplicados: 8 bytes em plataformas de 32 bits e 16 bytes em plataformas de 64 bits. Se um ponteiro transmitido para nossas funções não se encaixar nelas, o ponteiro transmitido para uma das funções estará fora do alinhamento.
  • allocation type mismatch: quando essa opção está ativada, uma função de dealocação chamada em um bloco precisa corresponder ao tipo de função que foi chamada para alocar. Esse tipo de incompatibilidade pode causar problemas de segurança.
  • invalid sized delete: quando o operador de exclusão de tamanho C++14 é usado e a verificação opcional é ativada, há uma incompatibilidade entre o tamanho que foi transmitido ao desalocar um bloco e o tamanho que foi solicitado ao alocar. Isso geralmente é 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, poderá usar um build do SO HWASan. Se você estiver depurando uma falha em um app, também é possível usar um build de app HWASan.