Erro: container-overflow
Erro do sanitizador de endereço: estouro de contêiner
No Visual Studio 2022 versão 17.2 e posterior, a STL (biblioteca padrão) do Microsoft Visual C++ é 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 ODR (regra de definição única). 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 que tem anotações do vector<int>::push_back
Address Sanitizer e outra declaração que vector<int>::push_back
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, desative as anotações em todos os projetos que usam a macro correspondente ou crie cada projeto usando /fsanitize=address
as 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 compilar 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 do acesso reservado à memória em um std::vector
Alocadores personalizados e estouro de contêiner
As verificações de estouro de contêiner do Address Sanitizer suportam nãostd::allocator
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, ele nem sempre pode verificar se os acessos na última extremidade de uma alocação estão corretos.
O 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 se alinhar estritamente com o final de uma parte de 8 bytes, o AddressSanitizer deverá assumir que o alocador disponibiliza os bytes entre o final da alocação e o final da parte para o alocador ou o usuário gravar. Portanto, ele não pode marcar os bytes na parte final de 8 bytes como inacessíveis. 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 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, a parte 3 tem 4 bytes de memória que são considerados inacessíveis porque 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 de que AddressSanitizer marca blocos de memória em partes 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())
são inacessíveis. Observe o uso da notação de intervalo aqui: '[' significa inclusivo de e ')' significa exclusivo de. Se o usuário estiver usando um alocador personalizado, não saberemos se a memória depois v.data() + v.capacity()
está acessível ou não. Devemos supor que sim. Preferimos marcar esses bytes como inacessíveis na memória de sombra, mas devemos marcá-los como acessíveis para que seja possível acessá-los após a alocação.
std::allocator
usa a _Minimum_asan_allocation_alignment
variável de membro estático para informar 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 da parte. Assim, essa parte da parte pode ser marcada como inacessível pelo Address Sanitizer para capturar excessos.
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 excessos, 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