Undefined behavior (UB)

Complier는 최적화를 위해서 다양한 기법을 적용하는데, 이 결과로 기대한 결과와 다른 결과가 나올 수가 있다. 이러한 프로그램은 잘못 짠 프로그램으로 간주하고 compiler는 특별한 행동을 할 필요는 없다.

overflow

Singed overflow 문제로, compiler는 x+1 > x를 비교해서 return 하는 것이 아니라 그냥 true를 return 한다.

int foo(int x) {
    return x+1 > x; // either true or UB due to signed overflow
}
foo(int):                                # @foo(int)
        movl    $1, %eax
        retq

Pointer arithmetic overflow 문제로, compiler는 p+a & p+b를 비교하는 것이 아니라 a와 b만 비교한다. gcc와 llvm의 차이가 있지만, 결과는 같다.

int overflow(int*p, int a, int b) {
   return (p + a >  p + b ? a : b);
}
overflow((int*)0xffffffff, 1, 0);
.clang
overflow(int*, int, int):                            # @overflow(int*, int, int)
        movl    %edx, %eax
        cmpl    %edx, %esi
        cmovgel %esi, %eax
        retq
.gcc
test(int*, int, int):
        movslq  %esi, %rsi
        movslq  %edx, %rcx
        movq    %rsi, %rax
        salq    $2, %rcx
        salq    $2, %rsi
        cmpq    %rcx, %rsi
        cmovle  %edx, %eax
        ret

demo

Poison value: Deferred UB

p가 0xFFFFFFFF 일지라도 n=0이라면 문제가 없는 코드이다. 하지만 아래 코드와 같이 최적화를 할경우 undefined behavior가 발생한다. 따라서 이런 당장 발생하지 않는 변수에 대해서 poision value라고 하고 eventually undefined behavior를 발생시킬 수 있다.

int n = 0;
for (auto i = 0; i < n; ++i){
  a[i] = p + 0x1;
}
// optimized
int n = 0;
auto temp = p + 0x1;
for (auto i = 0; i < n; ++i){
  a[i] = temp;
}