Compartilhar via


Bytes de sombra do AddressSanitizer

Resumimos brevemente o conceito de bytes de sombra e como eles podem ser usados pela implementação de runtime de /fsanitize=address. Para mais detalhes, confira o artigo seminal e o algoritmo AddressSanitizer.

Conceito principal

Cada 8 bytes no espaço de endereço virtual do aplicativo pode ser descrito usando um byte de sombra.

Um byte de sombra descreve quantos bytes estão acessíveis no momento da seguinte maneira:

  • 0 significa todos os 8 bytes
  • 1-7 significa um a sete bytes
  • Os números negativos codificam o contexto para o runtime a ser usado para o diagnóstico de relatório.

Legenda do byte de sombra

Considere esta legenda de bytes de sombra em que todos os números negativos são definidos:

Screenshot of the AddressSanitizer shadow-byte legend.

Mapeamento: descrevendo seu espaço de endereço

Cada 8 bytes no espaço de endereço virtual do aplicativo alinhado a "0-mod-8" pode ser mapeado para o byte de sombra que descreve esse slot no espaço de endereço virtual. Esse mapeamento pode ser feito com uma ação simples de deslocar e adicionar.

Em x86:

char shadow_byte_value = *((Your_Address >> 3) + 0x30000000)

Em x64:

char shadow_byte_value = *((Your_Address >> 3) + _asan_runtime_assigned_offset)

Geração de código: testes

Considere como bytes de sombra específicos podem ser gravados, seja pelo código gerado pelo compilador, pelos dados estáticos ou pelo runtime. Este pseudocódigo mostra como é possível gerar uma verificação que precede qualquer carregamento ou armazenamento:

ShadowAddr = (Addr >> 3) + Offset;
if (*ShadowAddr != 0) {
    ReportAndCrash(Addr);
}

Ao instrumentar uma referência de memória com menos de 8 bytes de largura, a instrumentação é um pouco mais complexa. Se o valor da sombra for positivo (o que significa que apenas os primeiros k bytes na palavra de 8 bytes podem ser acessados), precisamos comparar os últimos 3 bits do endereço com k.

ShadowAddr = (Addr >> 3) + Offset;
k = *ShadowAddr;
if (k != 0 && ((Addr & 7) + AccessSize > k)) {
    ReportAndCrash(Addr);
}

O runtime e o código gerado pelo compilador gravam bytes de sombra. Esses bytes de sombra permitem ou revogam o acesso quando os escopos terminam ou o armazenamento é liberado. As verificações acima leem bytes de sombra que descrevem "slots" de 8 bytes no espaço de endereço do aplicativo, em um determinado momento da execução do programa. Além dessas verificações geradas explicitamente, o runtime também verifica bytes de sombra depois de interceptar (ou "capturar") muitas funções no CRT.

Para mais informações, confira a lista de funções interceptadas.

Como definir bytes de sombra

O código gerado pelo compilador e o runtime do AddressSanitizer podem gravar bytes de sombra. Por exemplo, o compilador pode definir bytes de sombra para permitir o acesso de tamanho fixo a locais de pilha definidos em um escopo interno. O runtime pode colocar variáveis globais na seção de dados entre bytes de sombra.

Confira também

Visão geral do AddressSanitizer
Problemas conhecidos do AddressSanitizer
Referência de linguagem e build do AddressSanitizer
Referência de runtime do AddressSanitizer
Nuvem do AddressSanitizer ou teste distribuído
Integração do depurador do AddressSanitizer
Exemplos de erro do AddressSanitizer