컴파일러가 때로는 0xCD
and와 같은 특정 패턴으로 메모리를 초기화한다는 것을 알고 0xDD
있습니다. 내가 알고 싶은 것은 언제 그리고 왜 이런 일이 발생 하는지 입니다.
언제
사용 된 컴파일러에만 해당됩니까?
수행 malloc/new
및 free/delete
이와 관련하여 같은 방법으로 사용할 수 있습니까?
특정 플랫폼입니까?
Linux
또는 같은 다른 운영 체제에서도 발생 VxWorks
합니까?
왜
내 이해는 Win32
디버그 구성 에서만 발생하며 메모리 오버런을 감지하고 컴파일러가 예외를 포착하는 데 도움이됩니다.
이 초기화가 어떻게 유용한 지에 대한 실제적인 예를들 수 있습니까?
메모리를 할당 할 때 알려진 패턴으로 메모리를 초기화하는 것이 좋으며 특정 패턴이 인터럽트를 트리거 Win32
하여 디버거에 예외가 표시되는 것을 말하는 무언가를 읽은 것을 기억합니다 (Code Complete 2) .
이것은 얼마나 휴대 할 수 있습니까?
답변
디버그 모드 용으로 컴파일 될 때 다양한 비트의 소유되지 않은 / 초기화되지 않은 메모리에 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보다 높지 않습니다.
답변
채우기 값 0xCCCCCCCC에 대한 좋은 속성 중 하나는 x86 어셈블리에서 opcode 0xCC는 int3 opcode이며 소프트웨어 중단 점 인터럽트입니다. 따라서 해당 채우기 값으로 채워진 초기화되지 않은 메모리에서 코드를 실행하려고하면 즉시 중단 점이 발생하고 운영 체제에서 디버거를 연결하거나 프로세스를 종료시킬 수 있습니다.
답변
컴파일러 및 OS별로 다르며 Visual Studio는 다양한 종류의 메모리를 다른 값으로 설정하여 디버거에서 malloced 메모리, 고정 배열 또는 초기화되지 않은 객체로 오버런했는지 쉽게 확인할 수 있습니다. 내가 인터넷 검색하는 동안 누군가가 세부 정보를 게시 할 것입니다 …
답변
그것은 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을 편집해야 할 수도 있습니다.
답변
사용 된 컴파일러에만 해당됩니까?
실제로는 거의 항상 런타임 라이브러리 (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 기사 도 요약합니다. 일부 트릭은 사용중인 프로세서의 사양에 따라 다릅니다 (예 : 정렬 된 읽기 및 쓰기가 필요한지 여부 및 트랩 할 명령에 매핑되는 값). 메모리 덤프에서 눈에 띄는 큰 값과 비정상적인 값을 사용하는 것과 같은 다른 트릭은 더 이식성이 있습니다.
답변
이 문서에서는 비정상적인 메모리 비트 패턴 과 이러한 값이 발생할 경우 사용할 수있는 다양한 기술에 대해 설명 합니다.
답변
“이유”에 대한 명백한 이유는 다음과 같은 클래스가 있다고 가정하는 것입니다.
class Foo
{
public:
void SomeFunction()
{
cout << _obj->value << endl;
}
private:
SomeObject *_obj;
}
그런 다음 a를 인스턴스화 Foo
하고 호출 SomeFunction
하면 액세스 위반이 발생합니다.0xCDCDCDCD
합니다. 이것은 무언가를 초기화하는 것을 잊었 음을 의미합니다. 이것이 “왜 부분”입니다. 그렇지 않으면 포인터가 다른 메모리와 정렬되어 디버그하기가 더 어려울 수 있습니다. 액세스 위반이 발생한 이유를 알려줍니다. 이 경우는 매우 간단하지만 더 큰 클래스에서는 실수를 저지르기 쉽습니다.
AFAIK, 디버그 모드 인 경우에만 릴리스와 달리 Visual Studio 컴파일러에서만 작동합니다.