[C++] SIGSEGV (exit code 139) 오류 정리

2025. 5. 25. 15:50· 언어/C++
반응형

C++의 특성

C++을 초기에 사용할 때 가장 먼저 마주치는 오류인듯 한데요, Java 기반의 언어를 주로 다뤄왔기 때문에 메모리를 수동으로 관리하는 C의 특성을 잘 이해하지 못 해서 해당 오류를 마주치게 되었습니다.

 

C++의 경우, C 언어와 마찬가지로 메모리를 수동으로 관리하는 특성을 가지고 있는데요, 다음과 같이 포인터(*)를 선언하는 행위를 통해 메모리에 직접 접근할 수 있습니다.

struct FreeNode {
    FreeNode* next;
};

 

코드 예시

C++은 메모리를 수동으로 관리한다는 특성 때문에 많은 제약을 가지고 있는데요, 그 예시중 하나가 다음 메모리 할당 코드의 예시입니다.

// MemoryPool.cpp
class MemoryPool {
private:
    struct FreeNode {
        FreeNode* next;
    };

    alignas(alignof(T)) char buffer[sizeof(T) * PoolSize];
    FreeNode* freeList;
public:
    MemoryPool() {
        freeList = reinterpret_cast<FreeNode*>(buffer);
        FreeNode* current = freeList;
        for (std::size_t i = 1; i < PoolSize; ++i) {
            current->next = reinterpret_cast<FreeNode*>(buffer + i * sizeof(T));
            current = current->next;
        }
        current->next = nullptr;
    }

    T* allocate() {
        if (!freeList) throw std::bad_alloc();
        T* ptr = reinterpret_cast<T*>(freeList);
        freeList = freeList->next;
        return ptr;
    }

    void deallocate(T* ptr) {
        FreeNode* node = reinterpret_cast<FreeNode*>(ptr);
        node->next = freeList;
        freeList = node;
    }
};

// main.cpp
struct MyObject {
    int x;
    MyObject(int x) : x(x) {
        std::cout << "Constructed: " << x << std::endl;
    }
    ~MyObject() {
        std::cout << "Destructed: " << x << std::endl;
    }
};


int main() {
    MemoryPool<MyObject> pool;

    MyObject* obj = pool.allocate();
    new (obj) MyObject(42);

    std::cout << "Value: " << obj->x << std::endl;

    obj->~MyObject();
    pool.deallocate(obj);

    return 0;
}

 

코드 설명

해당 코드는 MemoryPool이라는 class로부터 특정 T 객체를 입력받아 메모리를 할당하는 예시인데요, main 메서드르를 보면 int 타입(4바이트)를 가진 MyObject 구조체를 MemoryPool에 넘겨서 메모리를 할당하도록 되어있습니다.

 

문제는, MemoryPool<MyObject> 가 선언이 될 때, T로 받은 타입의 크기(4바이트)를 buffer에 할당하게 되는데요, 이후 생성자 내부에서 reinterpret_cast<FreeNode*>(buffer)를 통해 명시적 캐스팅을 하는 시점에 FreeNode*(8바이트)를 할당하게 되면서 SIGSEGV이슈가 발생하게 되었습니다.

 

이슈 해결

해당 이슈의 경우, 할당받은 메모리(4바이트)보다 많은 메모리(8바이트)를 할당했기 때문에 발생한 SIGSEGV이슈인데요, 해결 방법은 다음과 같습니다.

  • reinterpret_cast<FreeNode*>를 써서 next를 쓰면 → 8바이트 오버런 발생
FreeNode* current = reinterpret_cast<FreeNode*>(buffer + i * sizeof(T));
current->next = ...;  // ❌ 잘못된 메모리 접근: T는 4바이트인데 8바이트 씀
  • union 변경으로 충분한 공간 확보 + 정렬 보장
union Node {
    Node* next;                         // 8바이트
    alignas(T) char data[sizeof(T)];    // 최소 sizeof(T)
};
  • 기존 구조: 위험한 reinterpret_cast
buffer = [T][T][T]...  (각각 sizeof(T), 예: 4B)
         ↑
         reinterpret_cast<FreeNode*>  → 8B write → overflow!
  • union 구조: 안전하게 공유
Node = union { next: 8B, data: T(4B) } → 전체 8B 이상 확보됨
nodes = [Node][Node][Node]...  (각 슬롯이 최소 8B)
         ↑
         safe to store next or T object

 

 

SIGSEGV가 발생할 수 있는 다른 상황 예시

1. 널 포인터 역참조

int* ptr = nullptr;
*ptr = 42;  // ❌ SIGSEGV
// 동적 할당 실수
// 리턴 값 확인 안 함

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

int* ptr = new int(5);
delete ptr;
*ptr = 10;  // ❌ 이미 해제된 메모리에 쓰기
// double delete, dangling pointer로 이어지기도 함

3. 배열 인덱스 초과 (Out of Bounds)

int arr[5];
arr[10] = 42;  // ❌ 경계 벗어남
// C++에서는 배열 범위 체크 안 해줍니다 → 직접 조심해야 함

4. 잘못된 포인터 캐스팅 (reinterpret_cast)

struct A { int a; };
struct B { double b; };

A a;
B* b = reinterpret_cast<B*>(&a);
std::cout << b->b << std::endl;  // ❌ alignment 문제로 SIGSEGV 가능

5. 스택 오버플로우 (무한 재귀 등)

void f() {
    f();  // ❌ 스택 한계 초과 → SIGSEGV
}

6. nullptr로 delete

int* ptr = nullptr;
delete ptr;  // ✅ delete nullptr은 안전하지만 이후에 다시 접근하면 ❌
// delete는 안전하지만, 그 후 *ptr 같은 접근은 위험

7. 스택 변수의 주소를 넘겼는데 이미 파괴된 경우 (Dangling reference)

int* getPtr() {
    int local = 10;
    return &local;  // ❌ local은 함수 종료 시 사라짐
}

int* p = getPtr();
std::cout << *p << std::endl;  // ❌ SIGSEGV 가능

8. misaligned access (정렬 위반)

alignas(8) struct A { double d; };

char buffer[sizeof(A)];
A* a = reinterpret_cast<A*>(buffer);  // ❌ buffer가 8바이트 정렬이 아닐 수 있음
a->d = 3.14;                          // ❌ SIGSEGV on ARM or strict architectures

9. 연산된 포인터가 잘못된 메모리로 가는 경우

int* arr = new int[5];
int* p = arr + 1000;
*p = 10;  // ❌ 접근 권한 없음 → SIGSEGV

10. 다중 스레드 환경에서의 데이터 레이스 → 잘못된 포인터 접근

 

  • 스레드 A가 delete한 포인터를 스레드 B가 사용 → 흔한 멀티스레드 버그
  • 디버깅 어렵고 간헐적으로 SIGSEGV 발생

 

반응형
LIST
iron_jin
iron_jin
배운 것에 대한 내 생각을 가지고 정리하자
學而不思則罔(학이불사즉망)배운 것에 대한 내 생각을 가지고 정리하자
iron_jin
學而不思則罔(학이불사즉망)
iron_jin
전체
오늘
어제
  • 전체 (163)
    • 도서 (10)
    • 생각 정리 (0)
    • 후기 모음 (14)
    • 언어 (20)
      • css (1)
      • java (9)
      • Kotlin (0)
      • javascript (0)
      • Solidity (3)
      • Python (3)
      • GO (3)
      • C++ (1)
    • Spring Framework (32)
      • spring (16)
      • JPA (6)
      • Error (4)
      • Settings (4)
    • 알고리즘 (62)
      • 이론 (0)
      • 연습문제 (58)
    • 인프라 (6)
      • 클라우드 (1)
      • 도커 (0)
      • AWS (4)
      • Elastic Search (0)
    • 쿠버네티스 (3)
      • 이론 (0)
      • 실습 (2)
      • 트러블슈팅 (1)
    • Nginx (2)
    • CS (4)
      • 서버 (0)
      • 리눅스 (2)
      • 네트워크 (0)
      • OAuth (2)
    • 형상관리 (3)
      • GIT (3)
    • Open API (3)
      • 카카오 API (1)
      • QGIS (2)
    • 보안 (0)
      • 알고리즘 (0)
    • 공통 (1)
      • 성능 관리 도구 (1)
    • Database (2)
      • MySQL (1)
      • Redis (1)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • 에릭 에반스
  • MySQL
  • 스프링
  • 2020 kakao
  • 코딩테스트
  • Java
  • SpringBoot
  • 항해플러스
  • 자바
  • Python
  • 2018 kakao
  • 2019 kakao
  • 2019 카카오
  • 2019 카카오 블라인드
  • 알고리즘
  • JPA
  • 도메인 주도 개발
  • Hibernate
  • ddd
  • 2020 카카오 블라인드
  • spring boot
  • 카카오 겨울 인턴십
  • 스프링 부트
  • 항해99
  • 백준
  • 프로그래머스
  • 2018 카카오 블라인드
  • spring
  • AWS
  • 2020 카카오

최근 댓글

최근 글

hELLO · Designed By 정상우.v4.2.2
iron_jin
[C++] SIGSEGV (exit code 139) 오류 정리
상단으로

티스토리툴바

개인정보

  • 티스토리 홈
  • 포럼
  • 로그인

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.