openlang

컴파일러가 부호 있는 오버플로 검사를 왜 지웠을까요?

부호 있는 int에 대한 if (x + 1 < x)는 오버플로 방어가 아니다. C에서 부호 있는 정수 오버플로는 정의되지 않은 동작이므로, 컴파일러는 x + 1이 절대 넘치지 않는다고 가정하고 조건을 거짓으로 결론 내린 뒤 그 분기를 지워도 된다. 주류 컴파일러들은 최적화를 켜면 실제로 그렇게 한다.

이게 보안 문제인 이유는 사라지는 검사가 대개 malloc이나 memcpy에 넘기는 길이 앞에 있기 때문이다. 리뷰에서 소스는 안전해 보인다. 바이너리에는 검사가 없다.

연산 전에 확인한다. 단순한 경우는 if (x > INT_MAX - 1). 일반적인 덧셈이라면 GCC와 Clang의 __builtin_add_overflow를 쓰거나, 툴체인이 C23을 지원하면 <stdckdint.h>의 ckd_add를 쓴다. -fwrapv는 그것을 지원하는 컴파일러에서 부호 있는 오버플로를 순환하게 만든다. 완화책으로는 괜찮지만 이식 가능한 C는 아니다. 그리고 size_t 같은 부호 없는 산술은 정의상 순환하는데, 그건 잘 정의된 틀린 길이를 얻는다는 뜻일 뿐이니 거기서도 한계값과 명시적으로 비교한다.

규칙은 표준 6.5절의 한 문장이다. 최적화기를 탓하는 버그 리포트 상당수가 그 문장으로 설명된다.

이어지는 대화 1

서로의 근거를 따라
  1. 고유 링크

    Swift는 반대로 갔고 저는 그게 좋아요. Int의 +는 오버플로가 나면 트랩을 걸어서, 잘못된 길이가 그냥 지나가는 대신 프로그램이 죽어요. 순환을 원하면 &+로 직접 말해야 하고, 죽는 대신 확인하고 싶으면 addingReportingOverflow가 결과와 플래그를 같이 돌려줘요. 크래시도 공짜는 아니에요. 그래도 조용히 사라진 검사보다는 훨씬 찾기 쉬워요. (누가 -Ounchecked로 빌드하지 않는다면요. 그러면 그 검사들이 빠져요.)

같은 질문의 다른 갈래

  1. Rego의 allow 규칙에 기본값 false를 둬야 할까요?

    {"approved": false}와 {}는 allow if input.approved == true에서 같은 답을 받습니다. 그 답은 false가 아니라 undefined입니다. 호스트는 선택적 불리언 필드 approved…

    답글 2regosql