Scudo é um alocador dinâmico de memória de modo de usuário, ou alocador de heap , projetado para ser resiliente contra vulnerabilidades relacionadas a heap (como estouro de buffer baseado em heap, use after free e double free ) enquanto mantém o desempenho. Ele fornece as primitivas padrão de alocação e desalocação C (como malloc e free), bem como as primitivas 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, onde o jemalloc ainda é usado). No 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, e o processo é abortado 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 . O tempo de execução do Scudo é fornecido como parte da cadeia de ferramentas do Android e o suporte foi adicionado ao Soong e ao Make para facilitar a ativação do alocador em um binário.
Você pode habilitar ou desabilitar a mitigação extra dentro do alocador usando as opções descritas abaixo.
Costumizaçã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 retorna a string de opções a ser analisada. Esta função deve ter o seguinte protótipo:extern "C" const char *__scudo_default_options()
. - Dinamicamente: Use a variável de ambiente
SCUDO_OPTIONS
contendo a string de opções a ser analisada. As opções definidas dessa maneira substituem qualquer definição feita por meio de__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 usado para atrasar a desalocação real dos blocos. Um valor mais baixo pode reduzir o uso de memória, mas diminuir a eficácia da mitigação; um valor negativo retorna aos padrões. Definir this e ThreadLocalQuarantineSizeKb como zero desativa totalmente a quarentena. |
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 encadeamento para descarregar a quarentena global. Um valor mais baixo pode reduzir o uso de memória, mas pode aumentar a contenção na quarentena global. Definir this e QuarantineSizeKb como zero desativa totalmente a quarentena. |
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 na incompatibilidade entre os tamanhos de novo e de exclusão. |
ZeroContents | false | false | Habilita o conteúdo do bloco zero na alocação e desalocação. |
allocator_may_return_null | false | false | Especifica que o alocador pode retornar 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, as alocações posteriores falham ou retornam null (dependendo do valor de allocator_may_return_null ), até que o RSS volte a ser desativado para permitir novas alocações. |
allocator_release_to_os_interval_ms | N / D | 5000 | Afeta apenas um alocador de 64 bits. Se definido, tenta liberar memória não utilizada para o sistema operacional, mas não com mais frequência do que esse intervalo (em milissegundos). Se o valor for negativo, a memória não é liberada para o sistema operacional. |
abort_on_error | true | true | Se definido, a ferramenta chama abort() em vez de _exit() após imprimir a mensagem de erro. |
Validação
Atualmente, não há testes CTS especificamente para Scudo. Em vez disso, certifique-se de que os testes CTS sejam 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 for detectado um problema irrecuperável, o alocador exibirá 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 log do sistema. A saída geralmente começa com Scudo ERROR:
seguido por um breve resumo do problema junto com quaisquer ponteiros.
Aqui está uma lista das mensagens de erro atuais e suas possíveis causas:
-
corrupted chunk header
: a verificação da soma de verificação do cabeçalho do bloco falhou. Isso provavelmente se deve a uma de duas coisas: o cabeçalho foi substituído (parcial ou totalmente) ou o ponteiro passado para a função não é um bloco. -
race on chunk header
: Duas threads 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 executar operações nesse bloco. -
invalid chunk state
: O chunk não está no estado esperado para uma determinada operação, por exemplo, não está alocado ao tentar liberá-lo ou não está em quarentena ao tentar reciclá-lo. Um free duplo é o motivo típico desse erro. -
misaligned pointer
: requisitos básicos de alinhamento são fortemente aplicados: 8 bytes em plataformas de 32 bits e 16 bytes em plataformas de 64 bits. Se um ponteiro passado para nossas funções não couber nelas, o ponteiro passado para uma das funções está desalinhado. -
allocation type mismatch
: Quando esta opção está habilitada, uma função de desalocação chamada em um chunk deve corresponder ao tipo de função que foi chamada para alocá-lo. Esse tipo de incompatibilidade pode apresentar problemas de segurança. -
invalid sized delete
: Quando o operador de exclusão de tamanho C++14 é usado e a verificação opcional está habilitada, há uma incompatibilidade entre o tamanho passado ao desalocar um bloco e o tamanho solicitado ao alocá-lo. 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 sistema operacional, poderá usar uma compilação do sistema operacional HWASan . Se você estiver depurando uma falha em um aplicativo, também é possível usar uma compilação de aplicativo HWASan .
,Scudo é um alocador dinâmico de memória de modo de usuário, ou alocador de heap , projetado para ser resiliente contra vulnerabilidades relacionadas a heap (como estouro de buffer baseado em heap, use after free e double free ) enquanto mantém o desempenho. Ele fornece as primitivas padrão de alocação e desalocação C (como malloc e free), bem como as primitivas 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, onde o jemalloc ainda é usado). No 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, e o processo é abortado 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 . O tempo de execução do Scudo é fornecido como parte da cadeia de ferramentas do Android e o suporte foi adicionado ao Soong e ao Make para facilitar a ativação do alocador em um binário.
Você pode habilitar ou desabilitar a mitigação extra dentro do alocador usando as opções descritas abaixo.
Costumizaçã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 retorna a string de opções a ser analisada. Esta função deve ter o seguinte protótipo:extern "C" const char *__scudo_default_options()
. - Dinamicamente: Use a variável de ambiente
SCUDO_OPTIONS
contendo a string de opções a ser analisada. As opções definidas dessa maneira substituem qualquer definição feita por meio de__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 usado para atrasar a desalocação real dos blocos. Um valor mais baixo pode reduzir o uso de memória, mas diminuir a eficácia da mitigação; um valor negativo retorna aos padrões. Definir this e ThreadLocalQuarantineSizeKb como zero desativa totalmente a quarentena. |
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 encadeamento para descarregar a quarentena global. Um valor mais baixo pode reduzir o uso de memória, mas pode aumentar a contenção na quarentena global. Definir this e QuarantineSizeKb como zero desativa totalmente a quarentena. |
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 na incompatibilidade entre os tamanhos de novo e de exclusão. |
ZeroContents | false | false | Habilita o conteúdo do bloco zero na alocação e desalocação. |
allocator_may_return_null | false | false | Especifica que o alocador pode retornar 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, as alocações posteriores falham ou retornam null (dependendo do valor de allocator_may_return_null ), até que o RSS volte a ser desativado para permitir novas alocações. |
allocator_release_to_os_interval_ms | N / D | 5000 | Afeta apenas um alocador de 64 bits. Se definido, tenta liberar memória não utilizada para o sistema operacional, mas não com mais frequência do que esse intervalo (em milissegundos). Se o valor for negativo, a memória não é liberada para o sistema operacional. |
abort_on_error | true | true | Se definido, a ferramenta chama abort() em vez de _exit() após imprimir a mensagem de erro. |
Validação
Atualmente, não há testes CTS especificamente para Scudo. Em vez disso, certifique-se de que os testes CTS sejam 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 for detectado um problema irrecuperável, o alocador exibirá 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 log do sistema. A saída geralmente começa com Scudo ERROR:
seguido por um breve resumo do problema junto com quaisquer ponteiros.
Aqui está uma lista das mensagens de erro atuais e suas possíveis causas:
-
corrupted chunk header
: a verificação da soma de verificação do cabeçalho do bloco falhou. Isso provavelmente se deve a uma de duas coisas: o cabeçalho foi substituído (parcial ou totalmente) ou o ponteiro passado para a função não é um bloco. -
race on chunk header
: Duas threads 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 executar operações nesse bloco. -
invalid chunk state
: O chunk não está no estado esperado para uma determinada operação, por exemplo, não está alocado ao tentar liberá-lo ou não está em quarentena ao tentar reciclá-lo. Um free duplo é o motivo típico desse erro. -
misaligned pointer
: requisitos básicos de alinhamento são fortemente aplicados: 8 bytes em plataformas de 32 bits e 16 bytes em plataformas de 64 bits. Se um ponteiro passado para nossas funções não couber nelas, o ponteiro passado para uma das funções está desalinhado. -
allocation type mismatch
: Quando esta opção está habilitada, uma função de desalocação chamada em um chunk deve corresponder ao tipo de função que foi chamada para alocá-lo. Esse tipo de incompatibilidade pode apresentar problemas de segurança. -
invalid sized delete
: Quando o operador de exclusão de tamanho C++14 é usado e a verificação opcional está habilitada, há uma incompatibilidade entre o tamanho passado ao desalocar um bloco e o tamanho solicitado ao alocá-lo. 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 sistema operacional, poderá usar uma compilação do sistema operacional HWASan . Se você estiver depurando uma falha em um aplicativo, também é possível usar uma compilação de aplicativo HWASan .