Partilhar via


Erro: container-overflow

Endereço Sanitizer Error: Estouro de contêiner

Observações

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

Tipo de contentor padrão Desativar macro de anotações Suportado 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 uma regra de definição (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 vector<int>::push_back sanitizer de 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 construir essas bibliotecas estáticas e objetos com o AddressSanitizer ativado. Misturar 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, desative as anotações em todos os projetos que usam a macro correspondente ou crie cada projeto usando /fsanitize=address e anotações habilitadas. (As anotações são ativadas 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 do 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 de acesso à memória reservada em um std::vector

Captura de tela do depurador exibindo erro de estouro de contêiner no exemplo 1.

Alocadores personalizados e estouro de contêiner

Endereço Sanitizer verificações de estouro de contêiner suportam não-alocadoresstd::allocator . 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 disponibiliza os bytes entre o final da alocação e o final do bloco para o alocador ou o usuário gravar. Portanto, ele não pode marcar os bytes no bloco final de 8 bytes como inacessível. No exemplo a seguir de um vector que aloca memória usando um alocador personalizado, '?' refere-se a dados não inicializados e '-' refere-se à memória que é 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 eles estão entre o final da alocação dos 20 bytes que foram reservados (v.reserve(20)) e o final do terceiro agrupamento lógico de 8 bytes (lembre-se que 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ê), de modo que v.data() + [0, v.size()) são acessíveis e v.data() + [v.size(), v.capacity()) inacessíveis. Observe o uso da notação de intervalo aqui: '[' significa incluir 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::allocator usa a _Minimum_asan_allocation_alignment 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 alocação. Isso garante que o alocador não usará 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 Sanitizer de Endereço para capturar estourações.

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 da parte para que ele possa marcar essa memória como inacessível e capturar saturações, defina _Minimum_asan_allocation_alignment para seu alinhamento mínimo real. Para que o AddressSanitizer funcione corretamente, o alinhamento deve ser pelo menos 8.

Ver também

Visão geral do AddressSanitizer
AddressSanitizer problemas conhecidos
de compilação e referência de linguagem AddressSanitizer
de referência de tempo de execução AddressSanitizer
AddressSanitizer shadow bytes
AddressSanitizer na nuvem ou de testes distribuídos
de integração do depurador AddressSanitizer
Exemplos de erro AddressSanitizer