[2026] C++ Heap Corruption | 힙 손상 가이드

[2026] C++ Heap Corruption | 힙 손상 가이드

이 글의 핵심

C++ Heap Corruption: 힙 손상 가이드. Heap Corruption이란?·발생 원인.

Heap Corruption이란?

힙 메모리 관리 구조가 손상되는 문제 다음은 간단한 cpp 코드 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

// ❌ 힙 손상 예시
int* arr = new int[10];
arr[10] = 42;  // 범위 초과 (힙 손상)
delete[] arr;

발생 원인

다음은 cpp를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// 1. 버퍼 오버플로우
int* arr = new int[10];
arr[15] = 42;  // 범위 초과
// 2. 이중 delete
int* ptr = new int(10);
delete ptr;
delete ptr;  // 이중 해제
// 3. 잘못된 delete
int* arr = new int[10];
delete arr;  // delete[] 사용해야 함
// 4. Use After Free
int* ptr = new int(10);
delete ptr;
*ptr = 42;  // 해제 후 사용

실전 예시

예시 1: 버퍼 오버플로우

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#include <iostream>
#include <cstring>
// ❌ 버퍼 오버플로우
void bufferOverflow() {
    char* buffer = new char[10];
    strcpy(buffer, "This is too long");  // 범위 초과
    delete[] buffer;
}
// ✅ 안전한 복사
void safeBuffer() {
    char* buffer = new char[20];
    strncpy(buffer, "This is safe", 19);
    buffer[19] = '\0';
    delete[] buffer;
}
// ✅ std::string 사용
void useString() {
    std::string str = "This is safe";
}

예시 2: 이중 delete

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#include <memory>
// ❌ 이중 delete
void doubleFree() {
    int* ptr = new int(10);
    delete ptr;
    delete ptr;  // 크래시
}
// ✅ nullptr 설정
void safeFree() {
    int* ptr = new int(10);
    delete ptr;
    ptr = nullptr;
    delete ptr;  // 안전 (무시됨)
}
// ✅ 스마트 포인터
void smartPointer() {
    auto ptr = std::make_unique<int>(10);
    // 자동 해제
}

예시 3: 잘못된 delete

다음은 cpp를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// ❌ delete vs delete[] 혼용
void wrongDelete() {
    int* arr = new int[10];
    delete arr;  // 힙 손상
}
void wrongDelete2() {
    int* ptr = new int(10);
    delete[] ptr;  // 힙 손상
}
// ✅ 올바른 delete
void correctDelete() {
    int* arr = new int[10];
    delete[] arr;
    
    int* ptr = new int(10);
    delete ptr;
}
// ✅ 스마트 포인터
void smartPointer() {
    auto arr = std::make_unique<int[]>(10);
    auto ptr = std::make_unique<int>(10);
}

예시 4: Use After Free

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#include <iostream>
// ❌ 해제 후 사용
void useAfterFree() {
    int* ptr = new int(10);
    delete ptr;
    std::cout << *ptr << std::endl;  // 위험
}
// ✅ 해제 전 사용
void safeUse() {
    int* ptr = new int(10);
    std::cout << *ptr << std::endl;
    delete ptr;
}
// ✅ 스마트 포인터
void smartPointer() {
    auto ptr = std::make_unique<int>(10);
    std::cout << *ptr << std::endl;
}

힙 손상 탐지

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// 1. AddressSanitizer
// g++ -fsanitize=address -g program.cpp
// 2. Valgrind
// valgrind --tool=memcheck ./program
// 3. Windows Debug Heap
#ifdef _DEBUG
#include <crtdbg.h>
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
#endif
// 4. 커스텀 가드
class HeapGuard {
public:
    static void* allocate(size_t size) {
        const size_t guardSize = 16;
        void* ptr = malloc(size + guardSize * 2);
        
        // 앞뒤에 가드 패턴
        memset(ptr, 0xAA, guardSize);
        memset((char*)ptr + guardSize + size, 0xBB, guardSize);
        
        return (char*)ptr + guardSize;
    }
    
    static void deallocate(void* ptr) {
        if (!ptr) return;
        
        // 가드 검사
        char* realPtr = (char*)ptr - 16;
        // 검사 로직
        free(realPtr);
    }
};

자주 발생하는 문제

문제 1: 배열 경계 초과

다음은 cpp를 활용한 상세한 구현 코드입니다. 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// ❌ 범위 초과
int* arr = new int[10];
for (int i = 0; i <= 10; i++) {  // <= 10은 오류
    arr[i] = i;
}
delete[] arr;
// ✅ 올바른 범위
int* arr = new int[10];
for (int i = 0; i < 10; i++) {
    arr[i] = i;
}
delete[] arr;
// ✅ std::vector
std::vector<int> vec(10);
for (int i = 0; i < vec.size(); i++) {
    vec[i] = i;
}

문제 2: 메모리 재할당

다음은 cpp를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// ❌ realloc 오용
int* arr = new int[10];
arr = (int*)realloc(arr, 20 * sizeof(int));  // new와 혼용 불가
delete[] arr;
// ✅ 새로 할당
int* arr = new int[10];
int* newArr = new int[20];
std::copy(arr, arr + 10, newArr);
delete[] arr;
delete[] newArr;
// ✅ std::vector
std::vector<int> vec(10);
vec.resize(20);

문제 3: 포인터 산술

아래 코드는 cpp를 사용한 구현 예제입니다. 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// ❌ 잘못된 포인터 산술
int* arr = new int[10];
int* ptr = arr + 15;  // 범위 초과
*ptr = 42;
delete[] arr;
// ✅ 범위 검사
int* arr = new int[10];
int index = 5;
if (index < 10) {
    arr[index] = 42;
}
delete[] arr;

문제 4: 구조체 멤버

다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

struct Node {
    int* data;
    Node* next;
};
// ❌ 멤버 해제 누락
void deleteNode(Node* node) {
    delete node;  // data 해제 안됨
}
// ✅ 멤버 먼저 해제
void deleteNode(Node* node) {
    delete[] node->data;
    delete node;
}
// ✅ 소멸자 사용
struct NodeSafe {
    std::unique_ptr<int[]> data;
    std::unique_ptr<NodeSafe> next;
};

방지 방법

다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// 1. 스마트 포인터
auto ptr = std::make_unique<int>(10);
auto arr = std::make_unique<int[]>(10);
// 2. 컨테이너 사용
std::vector<int> vec(10);
std::string str = "Hello";
// 3. RAII 패턴
class Resource {
    int* data;
public:
    Resource(int size) : data(new int[size]) {}
    ~Resource() { delete[] data; }
    
    Resource(const Resource&) = delete;
    Resource& operator=(const Resource&) = delete;
};
// 4. 범위 검사
template<typename T>
class SafeArray {
    std::vector<T> data;
public:
    T& operator {
        if (index >= data.size()) {
            throw std::out_of_range("인덱스 초과");
        }
        return data[index];
    }
};

디버깅 도구

아래 코드는 bash를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

# AddressSanitizer
g++ -fsanitize=address -g program.cpp
./a.out
# Valgrind
valgrind --leak-check=full --show-leak-kinds=all ./program
# Dr. Memory (Windows)
drmemory.exe -- program.exe
# Electric Fence
LD_PRELOAD=libefence.so ./program

FAQ

Q1: Heap Corruption은 언제?

A:

  • 버퍼 오버플로우
  • 이중 delete
  • 잘못된 delete
  • Use After Free

Q2: 탐지 방법은?

A:

  • AddressSanitizer
  • Valgrind
  • Windows Debug Heap

Q3: 방지 방법은?

A:

  • 스마트 포인터
  • 컨테이너
  • RAII 패턴

Q4: 증상은?

A:

  • 크래시
  • 예측 불가능한 동작
  • 메모리 손상

Q5: delete vs delete[]?

A:

  • delete: 단일 객체
  • delete[]: 배열

Q6: Heap Corruption 학습 리소스는?

A:

  • “Effective C++”
  • AddressSanitizer 문서
  • Valgrind 문서

같이 보면 좋은 글 (내부 링크)

이 주제와 연결되는 다른 글입니다.

관련 글

... 996 lines not shown ... Token usage: 63706/1000000; 936294 remaining Start-Sleep -Seconds 3