의미
기억클래스란 변수의 값이 어떤 종류의 메모리에 저장되는지를 지정하는 것을 의미한다. auto, register, static으로 구성되어있다.
auto 변수 | 함수, 블록 내부에서 사용되고 흔희 local 변수라고 불린다. 기억 클래스가 명시되지 않고 선언된 변수는 모두 자동 변수이다. 스택 공간을 사용하며, 함수나 블록에서 기억 영역이 확보되고, 벗어나면 소거됩니다. ** return 문에서 return 되는 값은 외부에서 기억영역을 호가보하게된다. |
register 변수 | CPU가 연산 시 데이터를 임시로 저장하는데 사용하는 작고 빠른 기억장소이다. 2개까지만 선언 가능하고, 초과된 변수는 AUTO 변수로 지정된다. 레지스터에는 주소가 없으므로 참조가 불가능하다. |
static 변수 | 프로그램이 종료될 때까지 값을 유지하며 처음 실행 시 가장 먼저 컴파일되고 초기화가 없으면 0으로 초기화 된다. 스택이 아닌 정적 데이터 영역을 사용한다. |
global 변수 | 함수 외부에서 선언되고 프로그램 전체의 영역에서 유효하다. 스택이 아닌 정적 데이터 영역을 사용한다. |
동작 방식
어떤 기억 클래스를 사용했을 때, 프로그램 내부 변수들이 데이터 영역, 스택 영역, 힙 영역, 텍스트 영역 (Read only 영역) 등 어디에 할당되는지 그 동작 방식을 이해하면서 프로그램을 작성해야 원하지 않는 행동들이 유발되는 상황을 줄일 수 있다.
문제 코드 1. 적절한 기억클래스를 사용하라
출력을 예상해보기
#include <stdio.h>
#include <string.h>
void TestFunction();
void innocuous();
const char *p;
int main()
{
TestFunction();
innocuous();
printf("p=%s\n", p);
return 0;
}
void TestFunction()
{
const char str[] = "This will change";
p = str;
printf("p=%s : str=%s\n", p, str);
}
void innocuous()
{
const char str[] = "Surprise, surprise";
printf("p=%s : str=%s\n", p, str);
}
출력 :
p=This will change : str=This will change
p=Surprise, surprise : str=Surprise, surprise
p=Surprise, surpri�#
위 코드를 보면 3번째에 원하지 않은 출력 (동작)을 하는 것을 볼 수 있다.
취약점 분석 및 해결 코드 1
분석 : const char를 가리키는 p 포인터가 TestFunction 내부 스택 영역에서 생성되는 str 문자열은 데이터 영역에 값이 써지고 str 이라는 변수는 TestFunction이 끝날 때 수명을 다한다. 하지만 p는 수명이 다한 str이 써놓은 (This will change 의 가장 첫번째 주소 ) 데이터 영역을 그대로 가리키고 있어서 정의되지 않은 행동을 유발하게 되었다.
-> 또한 수명을 다한 데이터를 가리키는 포인터로 데이터에 접근하는 일은 보안 상에서도 악용될 수 있는 큰 취약점이므로 반드시 해결을 해야한다.
*** 블록 밖에서 선언된 const 키워드 상수는 data 영역에 저장되고
*** 블록 안에서 선언된 const 키워드 상수는 문자열의 경우 data 영역, ( int, double, float ) 형 등의 경우 stack 영역에 저장된다.
#include <stdio.h>
#include <string.h>
void TestFunction();
void innocuous();
const char *p;
int main()
{
TestFunction();
innocuous();
printf("p=%s\n", p);
return 0;
}
void TestFunction()
{
const char str[] = "This will change";
p = str;
printf("p=%s : str=%s\n", p, str);
p = NULL;
}
void innocuous()
{
const char str[] = "Surprise, surprise";
printf("p=%s : str=%s\n", p, str);
}
str의 수명이 끝나는 지점에서 그 str 변수를 가리키던 전역 변수 포인터 p가 NULL을 가리키도록 바꾸고 함수를 마무리함으로 해결한다.
혹은, p를 사용하는 위치에서만 auto 변수 즉, local 변수로 선언하여서 해결할 수도 있다.
POINT
- 프로그램 내부 변수들이 데이터 영역, 스택 영역, 힙 영역, 텍스트 영역 (Read only 영역) 등 어디에 할당되는지 확인하자. 헷갈린다면 바로 구글에서 찾아보자.
- 수명을 다한 데이터를 가리키는 포인터는 정상적이지 않은 동작을 한다는 것을 명심하자.
참조
Rec. 02. Declarations and Initialization (DCL)
wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87151966
'C, C++ > 선언과 초기화' 카테고리의 다른 글
[CERT C/선언과 초기화] (2) 가변인자 함수 사용 시 주의할 점 (0) | 2021.01.22 |
---|---|
[CERT C/선언과 초기화] (1) 코드의 가독성을 높이기 위해 타입 정의를 사용하라 (0) | 2021.01.22 |