Secure coding-c-preprocessor-2

4
C 언어 언어 시큐어 시큐어 코딩 코딩 - Preprocessor-2 안전하지 안전하지 않은 않은 매크로 매크로 인자로 인자로 인한 인한 부작용 부작용 발생을 발생을 피한다 피한다 . 인자 하나를 이상 평가하거나 전혀 평가하지 않는 함수 형태 매크로는 안전하지 않다. 또한 할당, 증가, , 휘발성 액세스, 입출력 등의 부작용을 유발할 있는 함수 호출을 포함하는 매크로는 호출하지 않는다. 함수 형태 매크로 사용 전달 인자로 인한 부작용은 사용 방법에 대한 위험 요소이므로 책임은 매크로를 사용 프로그래머에게 있다. 하지만 이를 해결하기 위한 좋은 방법은 안전하지 않은 함수 형태의 매크로 작성을 하지 않는 것이다. 잘못된 잘못된 코드 코드 예제 예제 - 1 매크로 인자로 인한 부작용을 보여주는 잘못된 코드 예제: #define ABS(x) (((x) < 0) ? -(x) : (x)) void func(int n) { /* Validate that n is within the desired range */ int m = ABS(++n); /* ... */ } 예제에서 ABS() 매크로를 호출하면 다음과 같이 확장된다. m = (((++n) < 0) ? -(++n) : (++n)); n증가시키는 것을 확인할 있다. 올바른 올바른 코드 코드 예제 예제 - 1.1 n증가 연산을 매크로 호출 수행하는 올바른 코드 예제: #define ABS(x) (((x) < 0) ? -(x) : (x)) /* UNSAFE */ void func(int n) { /* Validate that n is within the desired range */ ++n; int m = ABS(n); /* ... */ }

Transcript of Secure coding-c-preprocessor-2

Page 1: Secure coding-c-preprocessor-2

C 언어언어 시큐어시큐어 코딩코딩 - Preprocessor-2

안전하지안전하지 않은않은 매크로매크로 인자로인자로 인한인한 부작용부작용 발생을발생을 피한다피한다.

인자 중 하나를 두 번 이상 평가하거나 전혀 평가하지 않는 함수 형태 매크로는 안전하지 않다. 또한 할당, 증가, 감소, 휘발성 액세스, 입출력 등의 부작용을 유발할 수 있는 함수 호출을 포함하는 매크로는 호출하지 않는다.

함수 형태 매크로 사용 시 전달 인자로 인한 부작용은 사용 방법에 대한 위험 요소이므로 그 책임은 매크로를 사용한 프로그래머에게 있다. 하지만 이를 해결하기 위한 좋은 방법은 안전하지 않은 함수 형태의 매크로 작성을 하지않는 것이다.

잘못된잘못된 코드코드 예제예제 - 1

매크로 인자로 인한 부작용을 보여주는 잘못된 코드 예제:

#define ABS(x) (((x) < 0) ? -(x) : (x)) void func(int n) { /* Validate that n is within the desired range */ int m = ABS(++n); /* ... */}

이 예제에서 ABS() 매크로를 호출하면 다음과 같이 확장된다.

m = (((++n) < 0) ? -(++n) : (++n));

n을 두 번 증가시키는 것을 확인할 수 있다.

올바른올바른 코드코드 예제예제 - 1.1

n의 증가 연산을 매크로 호출 전 수행하는 올바른 코드 예제:

#define ABS(x) (((x) < 0) ? -(x) : (x)) /* UNSAFE */ void func(int n) { /* Validate that n is within the desired range */ ++n; int m = ABS(n); /* ... */}

Page 2: Secure coding-c-preprocessor-2

추가로 ABS_UNSAFE()로 매크로 이름을 변경하여 매크로가 안전하지 않다는 것을 명확히하여 매크로를 사용하는다른 프로그래머들의 안전한 사용을 도울 수 있으며, 부호 있는 정수에 대한 연산이 오버 플로우를 발생하지 않도록한다.

올바른올바른 코드코드 예제예제 - 1.2

ABS() 매크로를 대체하는 인라인 함수 iabs()를 정의한다. 모든 유형의 피연산자가 동작하는 매크로와는 달리 함수는 데이터 형의 범위를 벗어난 인자를 자른다.

#include <complex.h>#include <math.h> static inline int iabs(int x) { return (((x) < 0) ? -(x) : (x));} void func(int n) { /* Validate that n is within the desired range */ int m = iabs(++n); /* ... */}

올바른올바른 코드코드 예제예제 - 1.3

보다 유연한 해결책은 _Generic을 사용하여 ABS() 매크로를 선언하는 것이다. 아래 코드는 인라인 함수를 사용하여모든 산술 데이터 형식을 지원하도록 작성되었다.

#include <complex.h>#include <math.h> static inline long long llabs(long long v) { return v < 0 ? -v : v;}static inline long labs(long v) { return v < 0 ? -v : v;}static inline int iabs(int v) { return v < 0 ? -v : v;}static inline int sabs(short v) { return v < 0 ? -v : v;}static inline int scabs(signed char v) { return v < 0 ? -v : v;}

Page 3: Secure coding-c-preprocessor-2

#define ABS(v) _Generic(v, signed char : scabs, \ short : sabs, \ int : iabs, \ long : labs, \ long long : llabs, \ float : fabsf, \ double : fabs, \ long double : fabsl, \ double complex : cabs, \ float complex : cabsf, \ long double complex : cabsl)(v) void func(int n) { /* Validate that n is within the desired range */ int m = ABS(++n); /* ... */}

올바른올바른 코드코드 예제예제(GCC) - 1.4

GCC의 __typeof를 사용하면 매크로 피연산자 값을 선언하고 동일한 유형을 임시 할당할 수 있다. 임시 할당하여계산을 수행하므로 피연산자는 한번만 평가된다.

#define ABS(x) __extension__ ({ __typeof (x) tmp = x; \ tmp < 0 ? -tmp : tmp; })

잘못된잘못된 코드코드 예제예제(assert()) - 2

assert() 매크로는 편리한 코드 테스트 메커니즘을 제공한다. assert() 매크로의 동작은 NDEBUG 매크로 정의를 따른다. NDEBUG 매크로가 정의되지 않은 경우 assert() 매크로는 인자를 평가 하기 위해 정의되며 결과가 0과 비교되면 abort() 함수를 호출한다. NDEBUG가 정의되면 assert는 ((void) 0)으로 확장되도록 정의된다.

아래 코드는 부작용이 있는 표현식(index++)이 포함된 assert() 매크로의 예이다.

#include <assert.h>#include <stddef.h> void process(size_t index) { assert(index++ > 0); /* Side effect */ /* ... */}

올바른올바른 코드코드 예제예제(assert()) - 2.1

부작용을 포함하는 표현식을 assert() 매크로 외부로 이동시켜 부작용을 방지한다.

Page 4: Secure coding-c-preprocessor-2

#include <assert.h>#include <stddef.h> void process(size_t index) { assert(index > 0); /* No side effect */ ++index; /* ... */}

출처 PRE31-C. Avoid side effects in arguments to unsafe macros