기타 Lobsters · 2026-10-23

"C언어 UB 나봤자 세그폴트 뜨고 끝남" 하던 개발자들 뒷통수 후려치는 글.txt

핵심 요약
  • '정의되지 않은 동작(UB)은 기껏해야 세그멘테이션 오류로 끝난다'는 주장을 반박하기 위해 실제 C++ 코드와 컴파일 어셈블리 결과를 제시한 글이 화제가 되었습니다.
  • 컴파일러는 UB가 발생하면 함수 리턴을 생략해 호출하지도 않은 뒤쪽의 '폭발 함수'를 실행해버리거나 코드와 무관한 값을 쓰는 등 예측 불가능한 코드를 만들어냅니다.
  • 작성자는 컴파일러가 UB를 감지할 의무가 없으며 최적화 과정에서 코드를 멋대로 처리하므로 UB를 절대 가볍게 넘겨서는 안 된다고 강조했습니다.
요약 C/C++ 프로그래밍에서 '정의되지 않은 동작(Undefined Behavior, 이하 UB)'이 발생해도 고작해야 세그멘테이션 폴트(segfault)나 크래시 정도로 끝날 것이라 안일하게 생각하는 개발자들에게 경종을 울리는 실험 결과가 화제입니다. 작성자는 간단한 예제 코드를 컴파일러(GCC, Clang)로 빌드해 어셈블리 결과를 비교하며 UB가 얼마나 기괴한 파장을 낳는지 직접 입증했습니다. 첫 번째 예시로 작성자는 '핫초코를 따르는 함수(serve_hot_chocolate)' 안에 UB를 유발하는 `std::unreachable()`을 넣고, 바로 아래에 호출된 적 없는 '폭발 함수(explode)'를 두었습니다. 그런데 GCC 최적화(-O1)를 거치자 컴파일러는 함수 종료 명령(ret)을 생략한 채 그대로 메모리상 바로 뒤에 배치된 폭발 함수로 흘러 들어가 실행되도록 어셈블리를 생성했습니다. 전혀 호출하지 않은 위험한 코드가 컴파일러 최적화 과정에서 제멋대로 실행된 것입니다. 두 번째로 널 포인터 역참조(`*ptr = 99`)를 넣었을 때는 더욱 이상한 결과가 나왔습니다. 코드에 적힌 숫자 99는 프로그램 어디에도 반영되지 않았고, 대신 0번 주소에 0을 쓴 뒤 잘못된 명령어(ud2)를 실행해 강제 크래시를 유발했습니다. Clang으로 돌렸을 때는 앞선 예시처럼 다시 폭발 함수 코드가 연달아 실행되는 현상이 나타났습니다. 마지막으로 널 포인터에 10을 더해 역참조(`*(ptr + 10) = 99`)하는 3중 UB 코드를 넣었더니, 이번에는 컴파일러가 아무런 제재 없이 순진하게 해당 오프셋 주소(40번지)에 99를 쓰고 정상 리턴하는 어셈블리를 만들어냈습니다. 작성자는 컴파일러가 정지 문제(halting problem) 등의 이유로 모든 UB를 감지할 의무가 없으며, 개발자가 UB를 유발한 순간 컴파일러는 '이런 상황은 발생하지 않는다'고 가정해 마음대로 명령어를 생략하거나 엉뚱한 코드를 이어 붙인다고 지적했습니다. 같은 자리에서 발생한 UB라도 메모리 뒤쪽의 엉뚱한 코드를 실행하거나, 뜬금없는 값을 쓰고 뻗거나, 아무 일 없었다는 듯 동작하는 등 완전히 예측 불가능한 결과를 낳는다는 점을 보여주며 절대 UB를 가볍게 여겨서는 안 된다고 강조했습니다.
Sponsored · 광고