다음을 통해 공유


오류: container-overflow

삭제자 오류 해결: 컨테이너 오버플로

Visual Studio 2022 버전 17.2 이상에서는 Microsoft Visual C++ 표준 라이브러리(STL)가 AddressSanitizer와 함께 작동하도록 부분적으로 인식됩니다. 다음 컨테이너 유형에는 메모리 액세스 문제를 검색하는 주석이 있습니다.

표준 컨테이너 유형 주석 매크로 사용 안 함 버전에서 지원됨
std::vector _DISABLE_VECTOR_ANNOTATION Visual Studio 2022 17.2
std::string _DISABLE_STRING_ANNOTATION Visual Studio 2022 17.6

ODR(one-definition-rule) 위반이 없는지 확인하는 검사 있습니다. 한 번역 단위가 ASan 주석을 사용하여 표준 형식 vector에 주석을 달지만 다른 번역 단위는 주석을 달지 않을 때 ODR 위반이 발생합니다. 이 예제에서 링커는 주소 삭제기 주석이 있는 선언 하나 vector<int>::push_back 와 그렇지 않은 다른 선언 vector<int>::push_back 을 동시에 볼 수 있습니다. 이 문제를 방지하려면 이진 파일을 연결하는 데 사용되는 각 정적 라이브러리 및 개체도 ASan 주석을 사용하도록 설정해야 합니다. 실제로 AddressSanitizer를 사용하도록 설정된 정적 라이브러리 및 개체를 빌드해야 합니다. 코드를 다른 주석 설정과 혼합하면 오류가 발생합니다.

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

이 오류를 해결하려면 해당 매크로를 사용하는 모든 프로젝트에서 주석을 사용하지 않도록 설정하거나 각 프로젝트를 사용하여 /fsanitize=address 빌드하고 주석을 사용하도록 설정합니다. (주석은 기본적으로 사용하도록 설정됩니다.)

예: 에서 예약된 메모리 액세스 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;
}

이 예제를 빌드하고 테스트하려면 Visual Studio 2022 버전 17.2 이상 개발자 명령 프롬프트 창에서 다음 명령을 실행합니다.

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

에서 예약된 메모리 액세스의 오류 결과 std::vector

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

사용자 지정 할당자 및 컨테이너 오버플로

주소 삭제기 컨테이너 오버플로 검사 할당std::allocator자가 아닌 할당자를 지원합니다. 그러나 AddressSanitizer는 사용자 지정 할당자가 8바이트 경계에서 할당을 정렬하거나 할당 끝과 다음 8바이트 경계 사이에 데이터를 배치하지 않는 등 AddressSanitizer 요구 사항을 준수하는지 여부를 알지 못하기 때문에 할당의 후반부에 액세스하는 것이 올바른지 검사 수 없습니다.

AddressSanitizer는 메모리 블록을 8바이트 청크로 표시합니다. 액세스할 수 없는 바이트를 단일 청크로 액세스할 수 있는 바이트 앞에 배치할 수 없습니다. 청크에 액세스할 수 있는 바이트 8개 또는 액세스할 수 있는 바이트 4개와 액세스할 수 없는 바이트 4바이트가 있는 것은 유효합니다. 액세스할 수 없는 바이트 4개 뒤에는 액세스할 수 있는 바이트 4개가 뒤따를 수 없습니다.

사용자 지정 할당자의 할당 종료가 8바이트 청크의 끝과 엄격하게 일치하지 않는 경우 AddressSanitizer는 할당자가 할당의 끝과 청크의 끝 사이의 바이트를 할당자 또는 사용자가 쓸 수 있도록 한다고 가정해야 합니다. 따라서 마지막 8바이트 청크의 바이트를 액세스할 수 없는 것으로 표시할 수 없습니다. 사용자 지정 할당자를 사용하여 메모리를 할당하는 다음 예제 vector 에서 '?'는 초기화되지 않은 데이터를 참조하고 '-'는 액세스할 수 없는 메모리를 나타냅니다.

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

이전 예제에서 청크 3에는 예약된 20바이트의 할당 종료(v.reserve(20))와 8바이트의 세 번째 논리 그룹화 끝 사이에 있기 때문에 액세스할 수 없는 것으로 간주되는 4바이트의 메모리가 있습니다(AddressSanitizer는 메모리 블록을 8바이트 청크로 표시함).

이상적으로는 8바이트 블록에서 유효한 바이트를 추적하기 위해 메모리의 모든 8바이트 블록에 대해 Address Sanitizer가 따로 설정하는 섀도 메모리를 표시합니다. 이는 액세스가 가능하고 v.data() + [v.size(), v.capacity()) 액세스할 수 없는 유효하지 않은(및 이유)v.data() + [0, v.size())입니다. 여기에서 간격 표기법을 사용합니다. '['는 포괄을 의미하고 ')'는 배타적이라는 의미입니다. 사용자가 사용자 지정 할당자를 사용하는 경우 이후 메모리 v.data() + v.capacity() 에 액세스할 수 있는지 여부를 알 수 없습니다. 우리는 그것이라고 가정해야합니다. 이러한 바이트를 섀도 메모리에서 액세스할 수 없는 것으로 표시하려고 하지만 할당 후 해당 바이트에 액세스할 수 있도록 액세스 가능한 것으로 표시해야 기본.

std::allocator 에서는 _Minimum_asan_allocation_alignment 정적 멤버 변수를 사용하여 할당자에서 할당 직후에 데이터를 배치하지 않도록 신뢰할 수 있음을 알 vectorstring 립니다. 이렇게 하면 할당자가 할당 끝과 청크 끝 사이의 메모리를 사용하지 않습니다. 따라서 청크의 해당 부분은 오버런을 catch하기 위해 주소 삭제자에 액세스할 수 없음으로 표시될 수 있습니다.

구현에서 사용자 지정 할당자가 할당의 끝과 청크 끝 사이의 메모리를 처리하는 것을 신뢰하여 해당 메모리를 액세스할 수 없음으로 표시하고 오버런을 catch할 수 있도록 하려면 실제 최소 맞춤으로 설정합니다 _Minimum_asan_allocation_alignment . AddressSanitizer가 올바르게 작동하려면 맞춤이 8 이상이어야 합니다.

참고 항목

AddressSanitizer 개요
AddressSanitizer 알려진 문제
AddressSanitizer 빌드 및 언어 참조
AddressSanitizer 런타임 참조
AddressSanitizer 섀도 바이트
AddressSanitizer 클라우드 또는 분산 테스트
AddressSanitizer 디버거 통합
AddressSanitizer 오류 예제