📌 1. 기존 Allocator의 문제점

문제 1. 유효하지 않은 메모리 접근

vector<int> v{ 0, 1, 2, 3, 4 };
for (int i = 0; i < 5; i++) {
    int value = v[i];
    if (value == 1)
        v.clear();
}
  • v.clear() 이후에도 v[i]로 접근하게 되면 런타임 에러 발생
  • 다행히 이런 오류는 즉시 확인 가능

문제 2. Use-After-Free (해제된 메모리 접근)

Unit* unit = new Unit();
delete unit;
unit->_hp = 100; // ?? 오류 안 나지만 위험한 접근
  • 삭제된 메모리에 접근해도 오류가 발생하지 않음
  • 해당 메모리가 다른 곳에서 재사용되었을 경우, 치명적인 오염 발생 가능

문제 3. 잘못된 캐스팅으로 인한 메모리 침범

PUnit* p = new PUnit();
Unit* u = static_cast<Unit*>(p); // 실제로는 Unit이 아님
u->_hp = 10; // 오류 발생 가능
  • 부모 클래스 포인터를 자식으로 캐스팅하면 정의되지 않은 메모리 영역에 접근

🔥 2. Stomp Allocator의 핵심 아이디어

👊 목적

  • Use-After-Free 탐지
  • 메모리 Overflow 탐지
  • 디버깅 모드에서 메모리 오염 즉시 탐지

🧩 핵심 전략

  • Windows VirtualAlloc/VirtualFree API 사용
  • 할당 메모리를 페이지 단위(4KB) 로 정렬
  • 데이터는 끝 부분에 배치 → Overflow 탐지 유리

🏗️ 3. 구현 코드

📄 Allocator.h

class StompAllocator
{
	enum { PAGE_SIZE = 0x1000 }; // 4KB

public:
	static void* Alloc(int32 size);
	static void Release(void* ptr);
};

📄 Allocator.cpp

void* StompAllocator::Alloc(int32 size)
{
	const int64 pageCount = (size + PAGE_SIZE - 1) / PAGE_SIZE;
	const int64 dataOffset = pageCount * PAGE_SIZE - size;

	void* baseAddress = ::VirtualAlloc(NULL, pageCount * PAGE_SIZE, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
	return static_cast<void*>(static_cast<int8*>(baseAddress) + dataOffset);
}

void StompAllocator::Release(void* ptr)
{
	const int64 address = reinterpret_cast<int64>(ptr);
	const int64 baseAddress = address - (address % PAGE_SIZE);
	::VirtualFree(reinterpret_cast<void*>(baseAddress), 0, MEM_RELEASE);
}

⚙️ 4. 디버깅 전용 매크로

#ifdef _DEBUG
#define xxalloc(size)        StompAllocator::Alloc(size)
#define xxrelease(ptr)       StompAllocator::Release(ptr)
#else
#define xxalloc(size)        BaseAllocator::Alloc(size)
#define xxrelease(ptr)       BaseAllocator::Release(ptr)
#endif

이 매크로를 사용하면 xnew, xdelete에 자동 적용되므로 기존 코드 수정 없이 디버그 환경에서만 StompAllocator를 사용할 수 있다.


🧪 5. 예제 코드

class Knight
{
public:
	int _hp = 100;
};

int main()
{
	Knight* knight = static_cast<Knight*>(xxalloc(sizeof(Knight)));
	knight->_hp = 200;
	xxrelease(knight);

	knight->_hp = 300; // 💥 Use-After-Free 오류 즉시 발생!
}

🧠 6. Overflow 탐지를 위한 데이터 뒤 배치 전략

[       [ 객체 데이터 ]       ]
       ↑ 메모리 끝에 위치
  • 일반적인 할당 방식에서는 객체가 맨 앞에 위치
  • StompAllocator는 끝에 배치하여 Overflow 시 페이지 경계를 넘기게 하여 즉시 crash 유도

📌 7. 운영체제 기반 메모리 관리 기초

  • 가상 메모리는 실제 물리 메모리와 매핑됨
  • VirtualAlloc: 메모리 예약 및 실제 할당
  • VirtualFree: 메모리 완전 해제
  • 메모리는 4KB 페이지 단위로 관리
  • SYSTEM_INFO:
    SYSTEM_INFO info;
    ::GetSystemInfo(&info);
    cout << "Page Size: " << info.dwPageSize << endl; // 4096
    cout << "Granularity: " << info.dwAllocationGranularity << endl; // 65536

profile
李家네_공부방

0개의 댓글