2. 시작하기 전에
이 PT는 C++ Now 2014의 발표를
번역했습니다.
https://www.youtube.com/w
atch?v=uHCLkb1vKaY
https://github.com/boostcon
/cppnow_presentations_2014
/blob/master/files/Undefined
-Behavior.pdf
오역 의역 넘쳐남…
영어를 발로 배웠어요…
3. 미정의 행동(Undefined Behavior)
C++ - N3691(C++ 14)에 보면
1.3.24 Undefined Behavior
behavior for which this International
Standard imposes no requirements
해석하자면
국제 표준에서 문제가 되는 요구사항을 강요하는 행동
-> 하지 말라는 행동
http://www.open-
4. 미정의 행동(UB) 의 예
• Program crashes
• Program이 기대하지 않은 결과를 냈다.
• 컴퓨터가 불이 났다.
• 고양이가 임신했다.
• 프로그램이 잘 동작한다.
• 답이 잘못된 것이 없다.
5. Example 1. 답이 잘못된게 없음
1
2
3
4
5
6
int main ()
{
int arr [] = { 0, 2, 4, 6, 8 };
int i = 1;
cout << i + arr[++i] + arr[i++] << endl;
}
컴파일 잘 됨 에러 안남 문법적인 에러는 없음
근데 컴파일러마다 값이 다름
같은 코드인데 값이 다름
고객에게 들고갔을때 값이 다를 수도 있다는 얘기
gcc ++ 10
clang c++ 10
6. 미정의 행동을 만드는 법
• 부호 있는 integer의
overflow(unsigned int 아님)
• Malloc 값이 0인 포인터나 NULL인 포
인터를 가리킴
• 피연산자의 값의 범위보다 같거나 더 큰
shift 연산
• 초기화 되지 않은 값 읽기
• 표현식을 한번 이상 수정하기
7. 미정의 행동을 만드는 법
• 버퍼 overflow
• 두개의 다른 데이터 구조체 포인터 비
교하기(ex. Array 랑 list랑 비교)
• 포인터 overflow
• Const 로 선언된 객체 수정하거나
string 문자열 수정하기
8. 미정의 행동을 만드는 법
• -INT_MIN(#define INT_MIN (-2147483647 - 1), bit연산시 –연산하면 값이 정
확하지 않을수 있음 참고 : http://stackoverflow.com/questions/8511598/large-negative-integer-
literals)
• Data race(멀티스레드가 같은 데이터를 이용하고 다른스레드에서 업데이트 할때 발생,
서로 다른 스레드에서 데이터 동기화에 실패함 그래서 락 걸고 접근해서 데이터 수정해야함)
• New랑 delete랑 쌍이 맞지 않음
• 파일을 읽기도 전에 씀
– 버퍼에 쓰기도 전에 값을 복사함
대부분은 컴파일 타임에 에러 발생, Data race 빼고
10. atomic is lock free
C++ - N3691(C++ 14)
20.9.2.5 shared_ptr atomic access
Concurrent access to a shared_ptr object from multiple
threads does not introduce a data race if the access is
done exclusively via the functions in this section and
the instance is passed as their first argument.
Atomic을 사용하면 Data Race를 방지할 수 있음
11. 산술연산
5 - 4 If during the evaluation of an expression, the result is not
mathematically defined or not in the range of representable
values for its type, the behavior is undefined
-> 컴파일 하는 동안 결과가 type의 범위를 넘어가는 경우는 미정의 행동이
다.
[ Note: most existing implementations of C++ ignore integer
overflows. Treatment of division by zero, forming a remainder
using a zero divisor, and all floating point exceptions vary among
machines, and is usually adjustable by a library function. — end
note ]
->C++에서는 int의 overflow를 무시하기 때문에 0으로 나눈다던지 제수가
0이라던지 부동 소수점의 예외처리는 시스템마다 다르기 때문에 사용하면
안된다.
12. Example 2. 답이 잘못된게 없음
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
#include <stdbool.h>
int _tmain(int argc, _TCHAR* argv[])
{
bool b;
if (b)
printf("truen");
if (!b)
printf("falsen");
return 0;
}
컴파일 잘 됨 에러 안남 (vs 는 남(똑똑))
근데 컴파일러마다 값이 다름
B가 뭐로 초기화 될지 모르기 때문
13. 왜 C++, C는 이모양이죠?
• 미 정의 행동을 하게 되면 컴파일러가
체크를 할 때 작은 코드를 생성할 여지
를 주게 된다.
• 미정의 행동을 하지 않으면 컴파일러
는 더 빠르고 간단하게 코드를 컴파일
할 수 있게 된다.
14. 미 정의 행동을 안 하는것이 왜 중요한데?
• 컴파일러가 미정의 행동이라는 사실을 알기
때문에, 그래서 최적화에 도움이 된다.
• 다른 컴퓨터에서 실행할 때 같은 결과를 낼
수 있다.
• 미정의 행동은 잘못된 답이다!!!
15. 다른 관점으로 생각하면
• 미정의 행동이 없으면 input이 뭐든
상관 없다.
• 어떤 미정의 행동은 모든 input을 처
리 할 수 있다.
• 미정의 행동은 input에 상관없이 항상
일어난다.
16. Example 3.
컴파일 잘 됨 에러 안남
Malloc 실패하면 어쩔껀데...??
1
2
3
4
5
6
7
8
9
10
11
12
int* do_someting(int *p)
{
printf("do_something %d n", *p);
if (!p)
{
// code here
p = (int *)malloc(...);
// more code here
}
return p;
}
17. Example 4.
컴파일 잘 됨 에러 안남
부호 있는 integer의 overflow는??
1
2
3
4
5
6
7
8
9
10
11
12
13
int _tmain(int argc, _TCHAR* argv[])
{
int i = 0x10000000;
int c = 0;
do
{
c++;
i += i;
printf("%dn", i);
} while (i > 0);
printf("%d iterationsn", c);
}
18. 왜 신경써야 하나?
• 미 정의 행동 코드를 짜기 엄청 쉽다.
• 컴파일러가 업그레이드 되거나 최적화
가 잘 되어 있으면 미정의 행동 코드는
잠깐은 잘 될 수도 있다.
• 미 정의 행동은 교묘한 코드에 나타나
니 자주 코드 보안을 체크할것
22. 만약에 이런 코드를 작성했다면
Bool WillThisOverflow(int a)
{ return a + 100 < a; }
컴파일러는 최적화를 이렇게 할것이다
Bool WillThisOverflow(int a)
{ return false; }
따라서 이렇게 작성하라
Bool WillThisOverflow(int a)
{ return a < (INT_MAX – 100); }
23. 미정의 행동을 찾아주는 tool
• 은 나타나기 시작했다.
• Clang은 –fsanitize = undefine를 사
용하면 된다.
– http://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html
• John Regehr's Integer Overflow
Checker
• STACK (this past summer from
MIT)