Erro: container-overflow

Erro do sanitizador de endereço: estouro de contêiner

No Visual Studio 2022 versão 17.2 e posterior, a biblioteca padrão do Microsoft Visual C++ (STL) é parcialmente habilitada para trabalhar com o AddressSanitizer. Os seguintes tipos de contêiner têm anotações para detectar problemas de acesso à memória:

Tipo de contêiner padrão Desabilitar macro de anotações Com suporte na versão
std::vector _DISABLE_VECTOR_ANNOTATION Visual Studio 2022 17.2
std::string _DISABLE_STRING_ANNOTATION Visual Studio 2022 17.6

Há verificações para garantir que não haja violações de regra de definição única (ODR). Uma violação de ODR ocorre quando uma unidade de tradução anota um tipo padrão, como vector, com anotações ASan, mas outra unidade de tradução não. Neste exemplo, o vinculador pode ver simultaneamente uma declaração de que tem anotações de desinfetante de vector<int>::push_back endereço e outra declaração de vector<int>::push_back que não. Para evitar esse problema, cada biblioteca estática e objeto usado para vincular o binário também deve habilitar anotações ASan. Efetivamente, você deve compilar essas bibliotecas e objetos estáticos com o AddressSanitizer habilitado. A combinação de código com diferentes configurações de anotação causa um erro:

my_static.lib(my_code.obj) : error LNK2038: mismatch detected for 'annotate_vector': value '0' doesn't match value '1' in main.obj

Para resolver esse erro, desabilite as anotações em todos os projetos que usam a macro correspondente ou compile cada projeto usando /fsanitize=address e anotações habilitadas. (As anotações são habilitadas por padrão).

Exemplo: Acessar memória reservada em um std::vector

// Compile with: cl /EHsc /fsanitize=address /Zi
#include <vector>

int main() {   
    // Create a vector of size 10, but with a capacity of 20.    
    std::vector<int> v(10);
    v.reserve(20);

    // In versions prior to 17.2, MSVC ASan does NOT raise an exception here.
    // While this is an out-of-bounds write to 'v', MSVC ASan
    // ensures the write is within the heap allocation size (20).
    // With 17.2 and later, MSVC ASan will raise a 'container-overflow' exception:
    // ==18364==ERROR: AddressSanitizer: container-overflow on address 0x1263cb8a0048 at pc 0x7ff6466411ab bp 0x005cf81ef7b0 sp 0x005cf81ef7b8
    v[10] = 1;

    // Regardless of version, MSVC ASan DOES raise an exception here, as this write
    // is out of bounds from the heap allocation.
    v[20] = 1;
}

Para criar e testar este exemplo, execute os seguintes comandos em uma janela de prompt de comando do desenvolvedor do Visual Studio 2022 versão 17.2 ou posterior:

cl /EHsc example1.cpp /fsanitize=address /Zi
devenv /debugexe example1.exe

Resultado de erro do acesso reservado à memória em um std::vector

Screenshot of debugger displaying container-overflow error in example 1.

Alocadores personalizados e estouro de contêiner

As verificações destd::allocator estouro de contêiner do Address Sanitizer suportam não alocadores. No entanto, como o AddressSanitizer não sabe se um alocador personalizado está em conformidade com os requisitos do AddressSanitizer, como alinhar alocações em limites de 8 bytes ou não colocar dados entre o final da alocação e o próximo limite de 8 bytes, nem sempre é possível verificar se os acessos na última extremidade de uma alocação estão corretos.

AddressSanitizer marca blocos de memória em blocos de 8 bytes. Ele não pode colocar bytes inacessíveis antes de bytes acessíveis em um único bloco. É válido ter 8 bytes acessíveis em um bloco ou 4 bytes acessíveis seguidos por 4 bytes inacessíveis. Quatro bytes inacessíveis não podem ser seguidos por 4 bytes acessíveis.

Se o final de uma alocação de um alocador personalizado não estiver estritamente alinhado com o final de um bloco de 8 bytes, o AddressSanitizer deverá assumir que o alocador torna os bytes entre o final da alocação e o final do bloco disponíveis para o alocador ou o usuário para gravar. Portanto, ele não pode marcar os bytes no bloco final de 8 bytes como inacessíveis. No exemplo a seguir de um que aloca memória usando um vector alocador personalizado, '?' refere-se a dados não inicializados e '-' refere-se à memória que está inacessível.

std::vector<uint8_t, MyCustomAlloc<uint8_t>> v;
v.reserve(20);
v.assign({0, 1, 2, 3});
// the buffer of `v` is as follows:
//    | v.data()
//    |       | v.data() + v.size()
//    |       |                                     | v.data() + v.capacity()
//  [ 0 1 2 3 ? ? ? ? ][ ? ? ? ? ? ? ? ? ][ ? ? ? ? - - - - ]
//        chunk 1            chunk 2            chunk 3

No exemplo anterior, o bloco 3 tem 4 bytes de memória que são considerados inacessíveis porque ficam entre o final da alocação dos 20 bytes reservados () e o final do terceiro agrupamento lógico de 8 bytes (v.reserve(20)lembre-se de que o AddressSanitizer marca blocos de memória em blocos de 8 bytes).

Idealmente, marcaríamos a memória de sombra, que o Address Sanitizer reserva para cada bloco de memória de 8 bytes para rastrear quais bytes nesse bloco de 8 bytes são válidos e quais são inválidos (e por quê), tais que v.data() + [0, v.size()) são acessíveis e v.data() + [v.size(), v.capacity()) são inacessíveis. Observe o uso da notação de intervalo aqui: '[' significa inclusive de, e ')' significa exclusivo de. Se o usuário estiver usando um alocador personalizado, não sabemos se a memória depois v.data() + v.capacity() está acessível ou não. Temos de assumir que sim. Preferimos marcar esses bytes como inacessíveis na memória de sombra, mas devemos marcá-los como acessíveis para que continue sendo possível acessar esses bytes após a alocação.

std::allocatorusa a variável de membro estático para dizer vector e string que eles podem confiar no alocador para não colocar dados logo após a _Minimum_asan_allocation_alignment alocação. Isso garante que o alocador não use a memória entre o final da alocação e o final do bloco. Assim, essa parte do pedaço pode ser marcada como inacessível pelo Desinfetante de Endereço para capturar estouros.

Se você quiser que a implementação confie que seu alocador personalizado está manipulando a memória entre o final da alocação e o final do bloco para que ele possa marcar essa memória como inacessível e capturar saturações, defina _Minimum_asan_allocation_alignment como seu alinhamento mínimo real. Para que o AddressSanitizer funcione corretamente, o alinhamento deve ser de pelo menos 8.

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
Bytes de sombra de AddressSanitizer
Nuvem do AddressSanitizer ou teste distribuído
Integração do depurador do AddressSanitizer
Exemplos de erro do AddressSanitizer