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.