C 언어의 정의되지 않은 동작, 45개 제거되다

Reducing undefined behavior in the C language

C 언어의 정의되지 않은 동작(undefined behavior)은 컴파일러가 임의로 최적화할 수 있어 오랜 논란을 낳았다. Kernel Recipes 2026에서 Martin Uecker는 C23 표준이 여러 문제적 기능을 제거했고, C2y 초안이 약 100개 중 45개의 UB를 없앴다고 밝혔다. 그는 C가 여전히 살아있는 언어이며 메모리 안전성도 점진적으로 개선될 수 있다고 전했다.

표준은 정의되지 않은 동작에 대해 컴파일러가 코를 파괴하는 악마를 소환하는 것까지 포함해 무엇이든 할 수 있도록 허용한다.
  1. pizlonator

    Fil-C를 언급한 건 멋진 일이지만, 동시에 그 가치를 과소평가하고 있기도 하다. TFA도 CHERI를 과소평가한다. Fil-C는 단순히 "많은 시간 안전성(temporal-safety)" 버그를 "찾는" 게 아니다. 익스플로잇 작성자로부터 메모리 안전성 버그(공간 및 시간)를 원천 차단하고, 언어 전체에 엄격한 의미론을 부여한다. CHERI는 몇 가지 다른 트레이드오프를 가지지만, 역시 메모리 안전성 익스플로잇이 작동하지 않을 만큼 충분히 엄격한 의미론을 제공한다. CHERI와 Fil-C 모두 Rust보다 더 포괄적인데, ABI 수준에서 문제를 공략하기 때문이다(따라서 새로운 언어의 안전한 서브셋으로 다시 작성된 부분에만 보호가 적용되는 문제가 발생하지 않는다). Rust는 컴파일 타임이 있다는 점에서 더 낫다고 주장할 수도 있지만, 의미론의 정의됨(definedness)이나 익스플로잇 가능성을 걱정한다면 그건 큰 차이를 만들지 않는다.

  2. chasil

    "예를 들어 일부 Honeywell 기계는 9비트 바이트를 사용했다."

    OS 2200은 36비트 워드를 사용한다. 여전히 지원되는 플랫폼이다.

    https://en.wikipedia.org/wiki/UNIVAC_1100/2200_series

    이 플랫폼은 최초의 SMP UNIX 구현이었다:

    "Sperry가 공급한 모든 구성, 다중 프로세서 구성을 포함하여, UNIX 시스템을 실행할 수 있다."

    https://www.nokia.com/bell-labs/about/dennis-m-ritchie/other...

  3. vrighter

    나는 사실 UB를 진짜로 UB로 만드는 장난 프로젝트를 시작할까 생각 중이었다. "실제로는 대부분의 최신 CPU에서 부호 있는 오버플로가 모두 동일하게 처리된다"는 그런 종류가 아니라. RNG와 컴파일러 플러그인 시스템을 만들어서 U에 맞춰 새로운 B들을 도입하는 거다.

  4. hn_submit

    내 생각에 이건 잘못된 나무를 짖는 격이다. 왜냐하면 내 생각에 C는 시스템 프로그래밍을 위한 "고수준 어셈블리"일 뿐이기 때문이다. 정의되지 않은 동작(UB)에 대항하기 위해 런타임 동작을 추가하는 순간 실행 시간이 폭발한다. 그리고 정적 분석은 컴파일 시간을 폭발시키지 않고는 한계가 있다.

    C는 "적재적소의 도구"이며, 운영체제와 초당 수천 번 호출되는 코드에 쓰인다. 그런 코드에서는 런타임 검사를 단 하나도 감당할 수 없다. 개발자는 자신이 무엇을 하는지 알아야 하며, 아니면 부엌에서 나가야 한다.

    우리는 애플리케이션 프로그래밍에서 C 사용을 장려하지 말고, 애플리케이션 개발자들을 Rust나 Go 같은 메모리 안전 언어로 유도해야 한다.

    그리고 UB에 관한 한 Rust가 이 경우를 해결하는지조차 확신하지 못하겠다.

  5. rwmj

    https://en.wikipedia.org/wiki/CompCert를 최소한 언급해야 한다고 생각한다. 자유 소프트웨어는 아니지만("소스 공개" 라이선스). 안전 필수 산업에서 대규모 C 코드베이스를 가진 회사들이 코드를 검증하는 실제 방법이다.

  6. vbezhenar

    C의 UB에 대한 나의 주된 문제는 그것이 조용하다는 점이다.

    컴파일러가 이상한 짓을 하는 건 괜찮다, 뭐든 좋다. 음, 괜찮지는 않지만 이 미친 세상에서는 받아들일 수 있다.

    하지만 나는 큰 소리로 경고를 받고 싶다! 예를 들어 WARNING: 이전의 0으로 나누기가 UB이기 때문에 이 조건 연산자가 한쪽 분기로 축소되었습니다. 그러면 나는 그것을 알아차리고 코드를 다시 쓰거나 그 조건을 제거할 수 있다.

    이 코드가 매크로 확장의 결과일 수 있다는 걸 이해한다. 괜찮다. 매크로는 특정 진단을 일시적으로 비활성화하는 프래그마를 포함하거나, 사용자가 매크로를 편집할 수 없다면 매크로 사용을 이런 프래그마로 감싸야 한다. 다른 경고들에서도 이미 일어나는 일이다.

    아니면 컴파일러가 매크로 확장과 정직한 사용자 실수를 구별할 만큼 똑똑할 수도 있겠다, 모르겠다.

    C++ 컴파일러가 내가 간단한 무한 루프를 작성한 함수의 에필로그를 그냥 제거했던 기억이 난다. 정말 미친 짓이었다. 그래서 무한 루프에 들어가는 대신, 내 프로그램은 그 아래에 링크된 함수를 계속 실행했다. 그걸 디버깅한다고 상상해 보라. 진단은 전혀 없었다.

  7. 1vuio0pswjnm7

    1790620504 | Reducing undefined behavior in the C language | https://lwn.net/SubscriberLink/1095811/b9325731ea9b61e0/ | https://news.ycombinator.com/item?id=49882419 | 15 comments

    1790673092 | Reducing undefined behavior in the C language | https://lwn.net/SubscriberLink/1095811/efcdbcf080cfa4c6/ | https://news.ycombinator.com/item?id=49890290 | 0 comments

  8. nine_k

    나는 솔직히 UB를 둘러싼 상황이 이해가 안 된다. 내가 알기로 이 개념은 확실한 의미가 없는 것들을 번역하거나 특정 아키텍처에서 예측 불가능하게 동작하던 아주 빈약한 컴파일러 시절에 태어났다. 그런데 왜 50년이 지난 지금도 여전히 존재하는가?

    컴파일러가 UB를 탐지할 수 있다면, 내 생각에 합리적인 유일한 방법은 프로그램을 즉시 종료하는 것이다. "코에서 나오는 악마"를 풀어놓는 게 아니라. (그리고 물론 많은 버그는 기본값이 되어야 할 -Wall에 부딪혀 박살 난다.)

    물론 모든 UB를 컴파일 타임에 탐지할 수 있는 건 아니다. 기사에서 언급된 구조체의 패딩 바이트를 읽는 것처럼. 모든 UB를 임의적이지만 예측 가능한 방식으로(즉시 크래시해도 좋다) 옵트아웃할 수 있는 방법이 있었으면 좋겠다. -fsanitize 같은 것이지만 런타임에 발생할 수 있는 모든 UB에 대한 것이다. 꽤 중요한 코드에서는 성능 저하를 감수할 의향이 있다. 그 후폭풍이나 RCE 익스플로잇을 처리하는 것보다 더 저렴할 수 있다. 하지만 완전히 현실적이지는 않다는 걸 안다.

이 날의 다른 글

2026-10-09