Fehler: container-overflow

Adressbereinigungsfehler: Containerüberlauf

In Visual Studio 2022, Version 17.2 und höher, ist die Microsoft Visual C++-Standardbibliothek (STL) teilweise optimiert, um mit dem AddressSanitizer zu arbeiten. Die folgenden Containertypen weisen Anmerkungen auf, um Speicherzugriffsprobleme zu erkennen:

Standardcontainertyp Anmerkungsmakro deaktivieren Unterstützt in Version
std::vector _DISABLE_VECTOR_ANNOTATION Visual Studio 2022 17.2
std::string _DISABLE_STRING_ANNOTATION Visual Studio 2022 17.6

Es gibt Überprüfungen, um sicherzustellen, dass keine 1-Definition-Regel (ODR)-Verletzungen vorhanden sind. Eine ODR-Verletzung tritt auf, wenn eine Übersetzungseinheit einen Standardtyp kommentiert, z vector. B. mit ASan-Anmerkungen, aber keine andere Übersetzungseinheit. In diesem Beispiel wird dem Linker möglicherweise gleichzeitig eine Deklaration mit vector<int>::push_back Adressbereinigungsanmerkungen angezeigt, und eine andere Deklaration davon vector<int>::push_back ist nicht vorhanden. Um dieses Problem zu vermeiden, müssen jede statische Bibliothek und jedes Objekt, das zum Verknüpfen der Binärdatei verwendet wird, auch ASan-Anmerkungen aktivieren. Effektiv müssen Sie diese statischen Bibliotheken und Objekte mit aktivierter AddressSanitizer erstellen. Das Mischen von Code mit unterschiedlichen Anmerkungseinstellungen verursacht einen Fehler:

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

Um diesen Fehler zu beheben, deaktivieren Sie anmerkungen in allen Projekten, die das entsprechende Makro verwenden, oder erstellen Sie jedes Projekt mit /fsanitize=address aktivierten Anmerkungen und Anmerkungen. (Anmerkungen sind standardmäßig aktiviert.)

Beispiel: Zugriff auf reservierten Speicher in einem 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;
}

Führen Sie zum Erstellen und Testen dieses Beispiels die folgenden Befehle in einem Visual Studio 2022 Version 17.2- oder höher-Eingabeaufforderungsfenster für Entwickler aus :

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

Fehlerergebnis des reservierten Speicherzugriffs in einem std::vector

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

Benutzerdefinierte Zuweisungen und Containerüberlauf

Adressüberlaufüberprüfungen des Bereinigungscontainers unterstützen Nicht-Allokatorenstd::allocator . Da AddressSanitizer jedoch nicht weiß, ob ein benutzerdefinierter Zuweisungsverteiler den Anforderungen von AddressSanitizer entspricht, z. B. das Ausrichten von Zuordnungen an 8-Byte-Begrenzungen oder das Einfügen von Daten zwischen dem Ende der Zuordnung und der nächsten Grenze von 8 Byte, ist es möglicherweise nicht immer möglich, zu überprüfen, ob der Zugriff auf das letzte Ende einer Zuordnung korrekt ist.

AddressSanitizer markiert Speicherblöcke in 8-Byte-Blöcken. Es kann keine nicht zugänglichen Bytes vor barrierefreien Bytes in einem einzelnen Abschnitt platzieren. Es ist gültig, 8 barrierefreie Bytes in einem Block oder 4 barrierefreie Bytes zu haben, gefolgt von 4 unzugänglichen Bytes. Auf vier nicht zugängliche Bytes kann nicht zugegriffen werden, gefolgt von 4 barrierefreien Bytes.

Wenn das Ende einer Zuordnung von einem benutzerdefinierten Allocator nicht streng am Ende eines 8-Byte-Blocks ausgerichtet ist, muss AddressSanitizer davon ausgehen, dass der Allocator die Bytes zwischen dem Ende der Zuordnung und dem Ende des Blocks für den Allocator oder den Benutzer zum Schreiben verfügbar macht. Daher kann er die Bytes im endgültigen 8-Byte-Block nicht als unzugänglich markieren. Im folgenden Beispiel eines vector Speichers, der Speicher mithilfe eines benutzerdefinierten Zuweisungsverteilers zuweist, bezieht sich "?" auf nicht initialisierte Daten, und "-" bezieht sich auf Speicher, auf den nicht zugegriffen werden kann.

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

Im vorherigen Beispiel weist Block 3 4 Bytes Arbeitsspeicher auf, die angenommen werden, dass auf sie nicht zugegriffen werden kann, da sie zwischen dem Ende der Zuordnung der reservierten 20 Bytes (v.reserve(20)) und dem Ende der dritten logischen Gruppierung von 8 Bytes liegen (denken Sie daran, dass AddressSanitizer Speicherblöcke in 8-Byte-Blöcken markiert).

Im Idealfall markieren wir den Schattenspeicher, den Address Sanitizer für jeden 8-Byte-Speicherblock auf den Weg legt, um nachzuverfolgen, welche Bytes in diesem 8-Byte-Block gültig sind und welche ungültig (und warum), wie v.data() + [0, v.size()) sie zugänglich sind und v.data() + [v.size(), v.capacity()) nicht zugänglich sind. Beachten Sie die Verwendung der Intervallnotation hier: "[" bedeutet einschließlich und ")" ausschließlich. Wenn der Benutzer einen benutzerdefinierten Zuweisungsserver verwendet, wissen wir nicht, ob auf den Speicher zugegriffen v.data() + v.capacity() werden kann oder nicht. Wir müssen davon ausgehen, dass dies der Grund ist. Wir möchten diese Bytes lieber als unzugänglich im Schattenspeicher markieren, aber wir müssen sie als barrierefrei markieren, damit sie nach der Zuordnung auf diese Bytes zugreifen können Standard.

std::allocator verwendet die _Minimum_asan_allocation_alignment statische Membervariable, um ihnen mitzuteilen vector und string zu bestätigen, dass sie dem Allocator vertrauen können, dass daten nicht direkt nach der Zuordnung abgelegt werden. Dadurch wird sichergestellt, dass der Zuweisungsverteiler den Speicher zwischen dem Ende der Zuordnung und dem Ende des Blocks nicht verwendet. Somit kann dieser Teil des Blockes durch den Adressbereinigungser als unzugänglich gekennzeichnet werden, um Überläufe abzufangen.

Wenn Sie möchten, dass die Implementierung darauf vertrauen soll, dass ihr benutzerdefinierter Zuweisungsverteiler den Speicher zwischen dem Ende der Zuordnung und dem Ende des Blockes verarbeitet, sodass dieser Speicher als nicht zugänglich und Überläufe erfasst werden kann, legen _Minimum_asan_allocation_alignment Sie die tatsächliche Mindestausrichtung fest. Damit AddressSanitizer ordnungsgemäß funktioniert, muss die Ausrichtung mindestens 8 sein.

Siehe auch

AddressSanitizer -Übersicht
Beheben bekannter Probleme mit demSanitizer
AddressSanitizer Build- und Sprachreferenz
AddressSanitizer-Laufzeitreferenz
AddressSanitizer-Schattenbytes
AddressSanitizer-Cloud oder verteilte Tests
AddressSanitizer Debugger-Integration
Beispiele für AddressSanitizer-Fehler