Errore: container-overflow
Errore di purificazione dell'indirizzo: overflow del contenitore
In Visual Studio 2022 versione 17.2 e successive, la libreria standard di Microsoft Visual C++ è parzialmente usata con AddressSanitizer. I tipi di contenitore seguenti includono annotazioni per rilevare i problemi di accesso alla memoria:
Tipo di contenitore standard | Disabilitare la macro delle annotazioni | Supportato nella versione |
---|---|---|
std::vector |
_DISABLE_VECTOR_ANNOTATION |
Visual Studio 2022 17.2 |
std::string |
_DISABLE_STRING_ANNOTATION |
Visual Studio 2022 17.6 |
Sono disponibili controlli per assicurarsi che non siano presenti violazioni ODR (One-Definition-Rule). Una violazione ODR si verifica quando un'unità di conversione annota un tipo standard, ad esempio vector
, con annotazioni ASan, ma un'altra unità di conversione non lo fa. In questo esempio, il linker potrebbe visualizzare contemporaneamente una dichiarazione di vector<int>::push_back
con annotazioni di pulizia degli indirizzi e un'altra dichiarazione di vector<int>::push_back
che non lo fa. Per evitare questo problema, ogni libreria statica e oggetto usato per collegare il file binario deve anche abilitare le annotazioni ASan. In effetti, è necessario compilare tali librerie statiche e oggetti con AddressSanitizer abilitato. La combinazione di codice con impostazioni di annotazione diverse causa un errore:
my_static.lib(my_code.obj) : error LNK2038: mismatch detected for 'annotate_vector': value '0' doesn't match value '1' in main.obj
Per risolvere questo errore, disabilitare le annotazioni in tutti i progetti che usano la macro corrispondente oppure compilare ogni progetto usando /fsanitize=address
e annotazioni abilitate. Le annotazioni sono abilitate per impostazione predefinita.
Esempio: Accedere alla memoria riservata in un 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;
}
Per compilare e testare questo esempio, eseguire i comandi seguenti in una finestra del prompt dei comandi per sviluppatori di Visual Studio 2022 versione 17.2 o successiva:
cl /EHsc example1.cpp /fsanitize=address /Zi
devenv /debugexe example1.exe
Risultato dell'errore di accesso alla memoria riservata in un std::vector
Allocatori personalizzati e overflow dei contenitori
I controlli dell'overflow dei contenitori di Sanitizer supportano gli allocatori nonstd::allocator
. Tuttavia, poiché AddressSanitizer non sa se un allocatore personalizzato è conforme ai requisiti AddressSanitizer, ad esempio l'allineamento delle allocazioni ai limiti a 8 byte o l'inserimento di dati tra la fine dell'allocazione e il limite a 8 byte successivo, potrebbe non essere sempre in grado di verificare che gli accessi alla fine di un'allocazione siano corretti.
AddressSanitizer contrassegna i blocchi di memoria in blocchi a 8 byte. Non può posizionare byte inaccessibili prima dei byte accessibili in un singolo blocco. È valido avere 8 byte accessibili in un blocco o 4 byte accessibili seguiti da 4 byte inaccessibili. Non è possibile seguire quattro byte inaccessibili da 4 byte accessibili.
Se la fine di un'allocazione da un allocatore personalizzato non è strettamente allineata alla fine di un blocco a 8 byte, AddressSanitizer deve presupporre che l'allocatore renda i byte tra la fine dell'allocazione e la fine del blocco disponibile per l'allocatore o l'utente in cui scrivere. Pertanto, non può contrassegnare i byte nel blocco finale a 8 byte come inaccessibile. Nell'esempio seguente di un oggetto vector
che alloca memoria usando un allocatore personalizzato, '?' fa riferimento a dati non inizializzati e '-' fa riferimento alla memoria inaccessibile.
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
Nell'esempio precedente, il blocco 3 ha 4 byte di memoria che si presuppone non siano accessibili perché rientrano tra la fine dell'allocazione dei 20 byte riservati (v.reserve(20)
) e la fine del terzo raggruppamento logico di 8 byte (tenere presente che AddressSanitizer contrassegna i blocchi di memoria in blocchi a 8 byte).
Idealmente, si contrassegna la memoria shadow, che Address Sanitizer mette da parte per ogni blocco di memoria a 8 byte per tenere traccia dei byte in quel blocco a 8 byte validi e quali non sono validi (e perché), tali che v.data() + [0, v.size())
sono accessibili e v.data() + [v.size(), v.capacity())
sono inaccessibili. Si noti l'uso della notazione di intervallo qui: '[' significa inclusivo di e ')' significa esclusivo di. Se l'utente usa un allocatore personalizzato, non sappiamo se la memoria dopo v.data() + v.capacity()
è accessibile o meno. Dobbiamo presupporre che sia. È preferibile contrassegnare tali byte come inaccessibili nella memoria shadow, ma è necessario contrassegnarli come accessibili in modo che rimanga possibile accedere a tali byte dopo l'allocazione.
std::allocator
usa la _Minimum_asan_allocation_alignment
variabile membro statica per indicare vector
e string
che può considerare attendibile l'allocatore per non inserire i dati subito dopo l'allocazione. In questo modo, l'allocatore non userà la memoria tra la fine dell'allocazione e la fine del blocco. In questo modo, la parte del blocco può essere contrassegnata inaccessibile da Address Sanitizer per rilevare i sovraccarichi.
Se si vuole che l'implementazione consideri attendibile che l'allocatore personalizzato gestisca la memoria tra la fine dell'allocazione e la fine del blocco in modo che possa contrassegnare tale memoria come inaccessibile e intercettare overrun, impostare sull'allineamento _Minimum_asan_allocation_alignment
minimo effettivo. Per il corretto funzionamento di AddressSanitizer, l'allineamento deve essere almeno 8.
Vedi anche
Panoramica di AddressSanitizer
Problemi noti di AddressSanitizer
Riferimento alla compilazione e al linguaggio AddressSanitizer
Informazioni di riferimento sul runtime AddressSanitizer
Byte ombreggiatura AddressSanitizer
AddressSanitizer cloud o test distribuiti
Integrazione del debugger AddressSanitizer
Esempi di errore addressSanitizer