기본 콘텐츠로 건너뛰기

라벨이 Secure C인 게시물 표시

Secure-Coding-C-DCL-1

Secure-Coding-C-DCL-1 C 언어 시큐어코딩 - Declare and Initialization 1 적절한 저장 기간을 가진 객체 선언 모든 객체는 수명을 결정하는 저장 기간이 있다. : static, thread, automatic, or allocated. 객체 수명은 실행 중 저장 장치에 예약된 기간 동안 보장된다. 객체가 존재하고, 상수 주소를 가지며, 수명 기간 동안 저장된 값을 유지한다. 하지만 수명 기간이 지나고 발생하는 참조의 동작에 대한 정의는 없으며 포인터가 가리키는 객체의 수명이 끝나면 포인터의 값은 불확실해진다. 객체의 수명 기간 지난 후 접근을 시도해서는 안된다. 그러한 시도는 정의되지 않은 동작으로 악용될 수 있는 취약점으로 이어질 수 있다. 잘못된 코드 예제 (다른 저장 기간) 이 코드 예제는 자동 저장 기간을 갖는 변수 c_str 의 주소를 정적 저장 기간을 갖는 변수 p 에 할당한다. 할당 자체는 유효하지만 dont_do_this() 의 끝에서와 같이 p 가 주소를 보유하고 있는 동안에 c_str 이 범위를 벗어나는 것은 유효하지 않다. # include <stdio.h> const char * p ; void dont_do_this ( void ) { const char c_str [ ] = "This will change" ; p = c_str ; /* Dangerous */ } void innocuous ( void ) { printf ( "%s\n" , p ) ; } int main ( void ) { dont_do_this ( ) ; innocuous ( ) ; return 0 ; } 올바른 코드 예제 (동일한 저장 기간) 이 예제에서 p 는 c_str 과 동일한 저장 기간으로 선언되므로 p 는 this_is_OK()

Secure-Coding-C-preprocessor-3

Secure-Coding-C-preprocessor-3 C 언어 시큐어 코딩 - Preprocessor-3 함수 형태 매크로 호출 시 전처리 지시문을 사용하지 않는다. 매크로 인자 값에 #define , #ifdef , #include 와 같은 전처리 지시문을 포함하지 않는다. 이 규칙은 매크로를 사용하여 함수가 구현되었는지 여부를 알 수 없는 함수에 대한 인수에서 전처리 지시문을 사용하는 경우에도 적용된다. 예를 들어, memcpy() , printf() , assert() 같은 표준 라이브러리 함수는 매크로로 구현 될 수 있다. 잘못된 코드 예제 예제(GCC Bug)에서 전처리 지시문을 사용하여 memcpy()에 대한 플랫폼 별 인수를 지정한다. 그러나 memcpy() 함수가 매크로를 사용하여 구현된 경우 코드는 정의되지 않은 동작을 유발한다. # include <string.h> void func ( const char * src ) { /* Validate the source string; calculate size */ char * dest ; /* malloc() destination string */ memcpy ( dest , src , # ifdef PLATFORM1 12 # else 24 # endif ) ; /* ... */ } 올바른 코드 예제 예제에서 memcpy() 함수 호출 방법은 호출 외부에서 결정된다. # include <string.h> void func ( const char * src ) { /* Validate the source string; calculate size */ char * dest ; /* malloc() destination string */ # ifdef PLATFORM1

Secure-Coding-C-preprocessor-2

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 ) ; /* ... */ } 추가로 ABS_UNSAFE()로 매크로 이름을 변경

Secure-Coding-C-preprocessor-1

C 언어 시큐어코딩 - Preprocessor 1 Universal character name을 연결을 통해서 생성하지 않는다. C 에서는 식별자, 문자 상수 및 문자열 리터럴에서 기본 문자 집합에 없는 universal character names의 사용을 지원한다. Universal character name \Unnnnnnnn 은 8 자리의 문자 nnnnnnnn 로 나타낸다. 유사하게 \Unnnn universal character name은 nnnn ( 0000nnnn )으로 나타낸다. 리터럴 값 그 자체로, 고정된 값을 의미한다. 예를 들어 const int b = 8; 에서 b는 상수이고 8은 리터럴이다. The C Standard, 5.1.1.2, paragraph 4 에 따르면 토큰 연결을 통해 생성한 universal character name의 동작은 정의되지 않는 것 으로 서술되어 있다. 이에 반드시 필요한 경우를 제외하고는 universal character names를 통한 식별자 정의를 피한다. 잘못된 코드 예제 #define assign(uc1, uc2, val) uc1##uc2 = val void func( void ) { int \u0401; /*...*/ assign(\u04, 01 , 4 ); /*...*/ } 상세 정보 이 코드는 Microsoft Visual Studio 2013을 사용하여 컴파일 및 실행되며 예상대로 변수에 4가 할당된다. Linux에서 GCC 4.8.1은 이 코드를 컴파일하는 것을 거부한다. universal character를 참조하는 assign 매크로에서 ‘프로그램 이탈’을 반환한다. 올바른 코드 예제 Universal character name를 사용하지만 토큰 연결을 사용하여 universal charater name를 생성하지 않는다. #define assign(ucn,