본문 바로가기
Secure Coding Rule

[Secure Coding] Signed integer의 Overflow 를 방지하라.

by J.. 2019. 12. 13.

Signed integer의 오버플로우는 정의되지 않은 행위로, 

오버플로우가 발생한다 하더라도 프로그램은 동작이 되게 된다. 

그러므로 의도치 않은 오버플로우는 자칫 프로그램의 취약점이 될 수 있다. 

따라서 연산을 할 시 오버플로우를 고려하여 코드 작성 시 유의하여야 한다. 

그러나 모든 연산 과정에서 오버플로우를 고려하는 것은 아니다.

'+', '-', '++', '--', '/'와 같은 연산자의 경우 오버플로우를 고려해야 하지만 '&', '|', '||'와 같은 경우는 오버플로우가 발생하지 않는다.

 

다음으로 몇 예제를 가지고 왔다.



Addition

 

void Addition( int operator_1, int operator_2) {
  int sum = operator_1 + operator_2;
  /* NEXT CODE */
}

프로그램 내에 위와 같은 코드가 포함되어있다고 가정한다.

그리고 operator_1 + operator_2의 결과값이 int형의 최대값보다 크다고 하자. 

Visual Studio에서 위 코드를 동작시키게 되면 우리는 매우 큰 양의 정수값을 얻을것이라고 기대했지만,

실제로 매우 큰 음의 값을 가지게 된다.  결국 오버플로우가 되어 우리가 기대한 값과 전혀 다른 값이 도출되었음을 알 수 있다.

이러한 오버플로우를 방지하기 위해 위와 같은 코드를 아래와 같이 바꿀 수 있다.

 

void Addition( int operator_1, int operator_2) {
	int sum=0;
	if (((operator_2 > 0) && (operator_1 > (INT_MAX - operator_2)))
        ||((operator_2 < 0) && (operator_1 < (INT_MIN - operator_2)))) {
    	/*Detect Overflow */
    	}
    	else
    		sum = operator_1 + operator_2;
}

 

int 형의 덧셈 연산과정에서는 크게 양의 오버플로우, 음의 오버플로우가 발생할 수 있다.

하나의 값이 양의 값이라면 다른 하나의 값에 따라 양의 오버플로우가 발생할 가능성이 있으며,

반대로 하나의 값이 음수라면 다른 하나의 값에 따라 음의 오버플로우가 발생할 수 있다.

즉 두 개의 데이터가 (+),(+) 혹은 (-),(-)일 때 오버플로우가 발생할 수 있으며 (+),(-)의 경우 오버플로우는 발생하지 않는다. 그렇기 때문에 조건문으로 방금까지 설명한 내용을 수식으로 표현하면 위 코드와 같다.

다만 주의할 점은 (operator_1 > (INT_MAX - operator_2))의 부분이다. 만약 보기 좋게

(operator_1+operator_2)> (INT_MAX) 라고 썼다면 조건문 내부에서 오버플로우가 일어나 항상 False로 처리되어 해당 조건문이 작동되지 않게된다. 

 


Subtraction

 

void Subtraction( int operator_1, int operator_2) {
  int sum = operator_1 - operator_2;
  /* NEXT CODE */
}

 다음과 같이 뺄셈의 경우에도 Overflow를 방지하여야 한다.

위 소스코드의 경우 operator_2의 값이 양(+)의 값일 경우 operator_1의 값에 따라 int형의 최소값보다 더 작아져 오버플로우가 일어날 수 있다. 마찬가지로 operator_2의 값이 음(-)의 값일 경우 operator_1의 값에 따라 int형의 최대값보다 커지게 되어 오버플로우가 발생할 수 있다. 따라서 이러한 경우에도 적절한 조건식으로 오버플로우를 방지하여야 한다.

 

void Subtraction( int operator_1, int operator_2) {
	int sum=0;
	if (((operator_2 > 0) && (operator_1 < (INT_MIN + operator_2)))
        ||((operator_2 < 0) && (operator_1 > (INT_MAX + operator_2)))) {
    	/*Detect Overflow */
    	}
    	else
    		sum = operator_1 - operator_2;
}

이와 같이 방금 전의 취약한 소스코드를 보완할 수 있다. 

 

 


Multiplication

 

취약한 소스코드는 지금까지 위에서 설명한 것들과 유사하게 단순히 operator_1*operator_2를 반환하는 것이다.

그러한 소스코드의 오버플로우를 방지하고자 다음과 같이 코드를 구현할 수 있다.

 

void Multiplication(int operator_1,int operator_2) {
	int result;
	if (operator_1 > 0) {
		if (operator_2 > 0) { 
			if (operator_1 > INT_MAX / operator_2)      {/*Handle Error*/}
			else if (operator_1 < INT_MIN / operator_2) {/*Handle Error*/ }
		}
	}
	else { // operator_1<0
		if (operator_2 > 0) {if (operator_1 < (INT_MIN / operator_2)) {/* Handle error */}}
		else {if ((operator_1 != 0) && (operator_2 < (INT_MAX / operator_1))) {/* Handle error */}} 
	}
    result=operator_1*operator_2;
}

곱셈의 경우 만약 하나의 수가 양수일 경우, 양의 오버플로우, 음의 오버플로우가 모두 일어날 수 있으며,

하나의 수가 음수일 경우에도 양의 오버플로우, 음의 오버플로우가 모두 일어날 수 있으므로 총 4개의 경우를 고려하여 오버플로우를 방지하여야 한다. 위과 같이 오버플로우 조건을 걸어 방지할 수 있다.

 


Division, Reminder

 

Division, Reminder의 경우에도 취약한 부분이 존재한다. 물론 기본적으로 나눗셈을 할 때 0으로 나누거나 하는 것들을 일단 제외한다. unsigned 형을 제외한 정수형의 범위를 보게 되면 최소값의 절대값 크기는 최대값의 절대값 크기보다 1이 크다. 그러므로 만약 operator_1의 값이 정수형의 최소값을 나타내며, operator_2의 값이 -1을 가질 경우 operator_1/operator_2을 하게 될 경우 오버플로우가 일어나게 된다. 따라서 다음과 같이 코드를 수정하여야 한다.

 

void Division(int operator_1,int operator_2) {
	signed long result;
 	if ((operator_2 == 0 ) || ((operator_1 == LONG_MIN) && (operator_2 == -1))) {
    	/* Handle error */
  	}
    else {
    	result = operator_1 % operator_2;
  	} 
}