malloc / free / new / delete에서 컴파일러는 언제 그리고 왜 메모리를 0xCD, 0xDD 등으로 초기화합니까?


129

컴파일러가 때로는 0xCDand와 같은 특정 패턴으로 메모리를 초기화한다는 것을 알고 0xDD있습니다. 내가 알고 싶은 것은 언제 그리고 이런 일이 발생 하는지 입니다.

언제

사용 된 컴파일러에만 해당됩니까?

수행 malloc/newfree/delete이와 관련하여 같은 방법으로 사용할 수 있습니까?

특정 플랫폼입니까?

Linux또는 같은 다른 운영 체제에서도 발생 VxWorks합니까?

내 이해는 Win32디버그 구성 에서만 발생하며 메모리 오버런을 감지하고 컴파일러가 예외를 포착하는 데 도움이됩니다.

이 초기화가 어떻게 유용한 지에 대한 실제적인 예를들 수 있습니까?

메모리를 할당 할 때 알려진 패턴으로 메모리를 초기화하는 것이 좋으며 특정 패턴이 인터럽트를 트리거 Win32하여 디버거에 예외가 표시되는 것을 말하는 무언가를 읽은 것을 기억합니다 (Code Complete 2) .

이것은 얼마나 휴대 할 수 있습니까?

답변:


191

디버그 모드 용으로 컴파일 될 때 다양한 비트의 소유되지 않은 / 초기화되지 않은 메모리에 Microsoft 컴파일러가 사용하는 내용에 대한 간략한 요약 (지원은 컴파일러 버전에 따라 다름) :

Value     Name           Description 
------   --------        -------------------------
0xCD     Clean Memory    Allocated memory via malloc or new but never 
                         written by the application. 

0xDD     Dead Memory     Memory that has been released with delete or free. 
                         It is used to detect writing through dangling pointers. 

0xED or  Aligned Fence   'No man's land' for aligned allocations. Using a 
0xBD                     different value here than 0xFD allows the runtime
                         to detect not only writing outside the allocation,
                         but to also identify mixing alignment-specific
                         allocation/deallocation routines with the regular
                         ones.

0xFD     Fence Memory    Also known as "no mans land." This is used to wrap 
                         the allocated memory (surrounding it with a fence) 
                         and is used to detect indexing arrays out of 
                         bounds or other accesses (especially writes) past
                         the end (or start) of an allocated block.

0xFD or  Buffer slack    Used to fill slack space in some memory buffers 
0xFE                     (unused parts of `std::string` or the user buffer 
                         passed to `fread()`). 0xFD is used in VS 2005 (maybe 
                         some prior versions, too), 0xFE is used in VS 2008 
                         and later.

0xCC                     When the code is compiled with the /GZ option,
                         uninitialized variables are automatically assigned 
                         to this value (at byte level). 


// the following magic values are done by the OS, not the C runtime:

0xAB  (Allocated Block?) Memory allocated by LocalAlloc(). 

0xBAADF00D Bad Food      Memory allocated by LocalAlloc() with LMEM_FIXED,but 
                         not yet written to. 

0xFEEEFEEE               OS fill heap memory, which was marked for usage, 
                         but wasn't allocated by HeapAlloc() or LocalAlloc(). 
                         Or that memory just has been freed by HeapFree(). 

면책 조항 : 테이블은 내가 누워있는 메모 중 일부입니다 .100 % 정확하지 않거나 일관성이 없을 수 있습니다.

이러한 값 중 다수는 vc / crt / src / dbgheap.c에 정의되어 있습니다.

/*
 * The following values are non-zero, constant, odd, large, and atypical
 *      Non-zero values help find bugs assuming zero filled data.
 *      Constant values are good, so that memory filling is deterministic
 *          (to help make bugs reproducible).  Of course, it is bad if
 *          the constant filling of weird values masks a bug.
 *      Mathematically odd numbers are good for finding bugs assuming a cleared
 *          lower bit.
 *      Large numbers (byte values at least) are less typical and are good
 *          at finding bad addresses.
 *      Atypical values (i.e. not too often) are good since they typically
 *          cause early detection in code.
 *      For the case of no man's land and free blocks, if you store to any
 *          of these locations, the memory integrity checker will detect it.
 *
 *      _bAlignLandFill has been changed from 0xBD to 0xED, to ensure that
 *      4 bytes of that (0xEDEDEDED) would give an inaccessible address under 3gb.
 */

static unsigned char _bNoMansLandFill = 0xFD;   /* fill no-man's land with this */
static unsigned char _bAlignLandFill  = 0xED;   /* fill no-man's land for aligned routines */
static unsigned char _bDeadLandFill   = 0xDD;   /* fill free objects with this */
static unsigned char _bCleanLandFill  = 0xCD;   /* fill new objects with this */

또한 디버그 런타임이 알려진 값으로 버퍼 (또는 버퍼의 일부)를 채울 때도 있습니다 (예 : std::string할당시 '느슨한'공간 또는에 전달 된 버퍼) fread(). 이러한 경우 이름에 지정된 값 _SECURECRT_FILL_BUFFER_PATTERN(에서 정의 됨 crtdefs.h)을 사용합니다. 정확히 언제 도입되었는지는 확실하지 않지만 적어도 VS 2005 (VC ++ 8)에 의해 디버그 런타임에있었습니다.

처음에이 완충재를 채우는 데 사용 된 값은 0xFD사람의 땅이없는 경우와 동일한 값 이었습니다 . 그러나 VS 2008 (VC ++ 9)에서는 값이로 변경되었습니다 0xFE. 필자가 호출자가 너무 큰 버퍼 크기를 전달한 경우와 같이 채우기 작업이 버퍼 끝을지나 실행되는 상황이있을 수 있다고 가정합니다 fread(). 이 경우 0xFD버퍼 크기가 1만큼 너무 클 경우 채우기 값은 해당 카나리아를 초기화하는 데 사용 된 사람이없는 토지 값과 동일하므로이 오버런 감지를 트리거하지 않을 수 있습니다. 사람의 토지가 변하지 않으면 오버런이 감지되지 않습니다.

따라서 VS 2008에서는 채우기 값이 변경되어 무인 토지 카나리아가 변경되어 런타임에 의해 문제가 감지되었습니다.

다른 사람들이 지적했듯이, 이러한 값의 주요 속성 중 하나는 이러한 값 중 하나를 가진 포인터 변수가 역 참조되면 표준 32 비트 Windows 구성에서 사용자 모드 주소 때문에 액세스 위반이 발생한다는 것입니다 0x7fffffff보다 높지 않습니다.


1
MSDN에 있는지 모르겠습니다. 여기에서 함께 조각하거나 다른 웹 사이트에서 가져 왔습니다.
Michael Burr

2
오 예-DbgHeap.c의 CRT 소스에서 가져온 것입니다.
Michael Burr

그중 일부는 MSDN ( msdn.microsoft.com/en-us/library/bebs9zyz.aspx )에 있지만 전부는 아닙니다. 좋은 목록입니다.
sean e

3
@seane-참고로 링크가 죽은 것 같습니다. 새로운 하나 (텍스트가 향상되었습니다) 여기에 있습니다 : msdn.microsoft.com/en-us/library/974tc9t1.aspx
사이먼 Mourier

이 블록의 이름은 무엇입니까? 메모리 배리어, 멤바, 메모리 펜스 또는 펜스 명령어 ( en.wikipedia.org/wiki/Memory_barrier )입니까?
kr85

36

채우기 값 0xCCCCCCCC에 대한 좋은 속성 중 하나는 x86 어셈블리에서 opcode 0xCC는 int3 opcode이며 소프트웨어 중단 점 인터럽트입니다. 따라서 해당 채우기 값으로 채워진 초기화되지 않은 메모리에서 코드를 실행하려고하면 즉시 중단 점이 발생하고 운영 체제에서 디버거를 연결하거나 프로세스를 종료시킬 수 있습니다.


6
그리고 0xCD는 int명령이므로 0xCD 0xCD를 실행하면을 생성하고 int CD트랩됩니다.
Tad Marshall

2
오늘날의 세계에서 데이터 실행 방지는 CPU가 힙에서 명령어를 가져 오는 것을 허용하지 않습니다. 이 답변은 XP SP2 이후로 구식입니다.
MSalters

2
@MSalters : 그렇습니다. 기본적으로 새로 할당 된 메모리는 실행 불가능하지만 누군가 메모리를 쉽게 사용 VirtualProtect()하거나 mprotect()실행 가능하게 만들 수 있습니다 .
Adam Rosenfield

데이터 블록에서 코드를 실행할 수 없습니다. 이제까지. 다시 맞춰봐
Dan

9

컴파일러 및 OS별로 다르며 Visual Studio는 다양한 종류의 메모리를 다른 값으로 설정하여 디버거에서 malloced 메모리, 고정 배열 또는 초기화되지 않은 객체로 오버런했는지 쉽게 확인할 수 있습니다. 내가 인터넷 검색하는 동안 누군가가 세부 정보를 게시 할 것입니다 ...

http://msdn.microsoft.com/en-us/library/974tc9t1.aspx


내 생각 엔 문자열을 올바르게 종료하는 것을 잊었는지 확인하는 데 사용됩니다 (0xCD 또는 0xDD가 인쇄되기 때문에).
strager

0xCC = 초기화되지 않은 로컬 (스택) 변수 0xCD = 초기화되지 않은 클래스 (힙?) 변수 0xDD = 삭제 된 변수
FryGuy

@FryGuy 여기에 설명 된 것처럼 이러한 값을 지시하는 실용적인 이유가 있습니다 .
Glenn Slayden

4

그것은 OS가 아니라 컴파일러입니다. 동작을 수정할 수도 있습니다.이 게시물의 하단을 참조하십시오.

Microsoft Visual Studio는 스택 메모리를 0xCC로 미리 채우는 이진을 디버그 모드로 생성합니다. 또한 버퍼 오버 플로우를 감지하기 위해 모든 스택 프레임 사이에 공간을 삽입합니다. 이것이 유용한 곳의 매우 간단한 예는 다음과 같습니다 (실제로 Visual Studio는이 문제를 발견하고 경고를 발행합니다).

...
   bool error; // uninitialised value
   if(something)
   {
      error = true;
   }
   return error;

Visual Studio에서 변수를 알려진 값으로 미리 초기화하지 않은 경우이 버그를 찾기 어려울 수 있습니다. 사전 초기화 된 변수 (또는 사전 초기화 된 스택 메모리)를 사용하면 모든 실행에서 문제를 재현 할 수 있습니다.

그러나 약간의 문제가 있습니다. Visual Studio에서 사용하는 값은 TRUE입니다. 0을 제외한 모든 값이 적용됩니다. 실제로 릴리스 모드에서 코드를 실행할 때 unitialized 변수가 0을 포함하는 스택 메모리에 할당 될 수 있습니다. 이는 Release 모드에서만 나타납니다.

그것은 나를 귀찮게 했으므로 바이너리를 직접 편집하여 사전 채우기 값을 수정 하는 스크립트 를 작성하여 스택에 0이있을 때만 나타나는 초기화되지 않은 변수 문제를 찾을 수있었습니다. 이 스크립트는 스택 사전 채우기 만 수정합니다. 힙 프리필을 실험 해 본 적이 없지만 가능합니다. 런타임 DLL을 편집해야 할 수도 있습니다.


1
GCC와 같이 초기화되기 전에 값을 사용할 때 VS가 경고를 발행하지 않습니까?
strager

3
정적 분석에 의존하기 때문에 항상 그런 것은 아닙니다. 결과적으로 포인터 산술과 혼동하기 쉽습니다.
Airsource Ltd

3
"OS가 아닙니다. 컴파일러입니다." 실제로는 컴파일러가 아니며 런타임 라이브러리입니다.
Adrian McCarthy

디버깅 할 때 Visual Studio 디버거는 0 또는 1이 아닌 경우 bool 값을 true (204) 와 같이 표시 합니다 . 따라서 코드를 추적하면 이러한 종류의 버그를 쉽게 확인할 수 있습니다.
Phil1970

4

사용 된 컴파일러에만 해당됩니까?

실제로는 거의 항상 런타임 라이브러리 (C 런타임 라이브러리와 같은)의 기능입니다. 런타임은 일반적으로 컴파일러와 밀접한 상관 관계가 있지만 교환 할 수있는 몇 가지 조합이 있습니다.

Windows에서 디버그 힙 (HeapAlloc 등)은 malloc에서 나온 것과 다른 특수 채우기 패턴을 사용하며 디버그 C 런타임 라이브러리의 무료 구현을 사용합니다. 따라서 OS 기능 일 수도 있지만 대부분의 경우 언어 런타임 라이브러리 일뿐입니다.

malloc / new 및 free / delete가 이와 관련하여 동일한 방식으로 작동합니까?

new 및 delete의 메모리 관리 부분은 일반적으로 malloc 및 free로 구현되므로 new 및 delete로 할당 된 메모리 일반적으로 동일한 기능을 갖습니다.

특정 플랫폼입니까?

세부 사항은 런타임에 따라 다릅니다. 실제로 사용되는 값은 16 진 덤프를 볼 때 비정상적이고 명백해 보일뿐만 아니라 프로세서의 기능을 활용할 수있는 특정 속성을 갖도록 설계되었습니다. 예를 들어, 정렬 오류가 발생할 수 있으므로 홀수 값이 자주 사용됩니다. 초기화되지 않은 카운터에 루프하면 놀라운 지연이 발생하기 때문에 큰 값이 사용됩니다 (0과 반대). x86에서 0xCC는int 3 명령어이므로 초기화되지 않은 메모리를 실행하면 트랩됩니다.

Linux 또는 VxWorks와 같은 다른 운영 체제에서도 발생합니까?

주로 사용하는 런타임 라이브러리에 따라 다릅니다.

이 초기화가 어떻게 유용한 지에 대한 실제적인 예를들 수 있습니까?

위의 일부를 나열했습니다. 값은 일반적으로 메모리의 유효하지 않은 부분 (예 : 긴 지연, 트랩, 정렬 오류 등)을 수행하는 경우 비정상적인 일이 발생할 가능성을 높이기 위해 선택됩니다. 힙 관리자는 때때로 할당 간격에 특별한 채우기 값을 사용하기도합니다. 이러한 패턴이 변경되면 버퍼 오버런과 같은 잘못된 쓰기가 발생한 것입니다.

메모리를 할당 할 때 알려진 패턴으로 메모리를 초기화하는 것이 좋으며 특정 패턴은 Win32에서 인터럽트를 트리거하여 디버거에 예외가 표시되는 것을 읽었습니다 (Code Complete 2에서).

이것은 얼마나 휴대 할 수 있습니까?

솔리드 코드 작성 (및 아마도 코드 완성 )은 채우기 패턴을 선택할 때 고려해야 할 사항에 대해 설명합니다. 여기에 그중 일부를 언급했으며 Magic Number (프로그래밍) 에 대한 Wikipedia 기사 도 요약합니다. 일부 트릭은 사용중인 프로세서의 사양에 따라 다릅니다 (예 : 정렬 된 읽기 및 쓰기가 필요한지 여부 및 트랩 할 명령에 매핑되는 값). 메모리 덤프에서 눈에 띄는 큰 값과 비정상적인 값을 사용하는 것과 같은 다른 트릭은 더 이식성이 있습니다.



2

"이유"에 대한 명백한 이유는 다음과 같은 클래스가 있다고 가정하는 것입니다.

class Foo
{
public:
    void SomeFunction()
    {
        cout << _obj->value << endl;
    }

private:
    SomeObject *_obj;
}

그런 다음 a를 인스턴스화 Foo하고 호출 SomeFunction하면 액세스 위반이 발생합니다.0xCDCDCDCD 합니다. 이것은 무언가를 초기화하는 것을 잊었 음을 의미합니다. 이것이 "왜 부분"입니다. 그렇지 않으면 포인터가 다른 메모리와 정렬되어 디버그하기가 더 어려울 수 있습니다. 액세스 위반이 발생한 이유를 알려줍니다. 이 경우는 매우 간단하지만 더 큰 클래스에서는 실수를 저지르기 쉽습니다.

AFAIK, 디버그 모드 인 경우에만 릴리스와 달리 Visual Studio 컴파일러에서만 작동합니다.


귀하의 설명은 따르지 않습니다. 읽기에 대한 액세스 위반이 발생하기 0x00000000때문에 유용하거나 잘못된 주소 일 수 있습니다. 이 페이지에 대한 다른 의견에서 지적했듯이 0xCD(및 0xCC) 의 진정한 이유 는 소프트웨어 인터럽트를 트리거하는 해석 가능한 x86 opcode이며, 이는 하나의 특정적이고 드문 유형의 오류로 디버거로 정상적으로 복구 할 수 있기 때문입니다. 즉, CPU가 실수로 비 코드 영역에서 바이트를 실행하려고 할 때. 이 기능적 사용 이외에도 채우기 값은 참고로 조언 힌트 일뿐입니다.
Glenn Slayden

2

메모리가 초기 시작 값에서 변경되었음을 알 수 있습니다. 일반적으로 디버깅 중에는 때로는 릴리스 코드에서도 작동합니다. 프로세스가 실행되는 동안 디버거를 프로세스에 연결할 수 있기 때문입니다.

그것은 단지 메모리 일뿐 만 아니라 많은 디버거는 프로세스가 시작될 때 레지스터 내용을 센티넬 값으로 설정합니다 (일부 AIX 버전에서는 일부 레지스터 0xdeadbeef가 약간 유머러스합니다).


1

IBM XLC 컴파일러에는 자동 변수에 지정한 값을 지정하는 "initauto"옵션이 있습니다. 디버그 빌드에 다음을 사용했습니다.

-Wc,'initauto(deadbeef,word)'

초기화되지 않은 변수의 스토리지를 살펴보면 0xdeadbeef로 설정됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.