김종하 파수닷컴 선임

▲ 김종하 파수닷컴 선임 [PA사업팀 EA팀] http://story.wisedog.net/

[아이티데일리] 초보 C 프로그래머가 흔히 저지르는 실수 중 하나가 바로 지역변수 주소를 리턴하는 행위일 것입니다. 다른 유형보다 문자열(char*)을 리턴하면서 이런 실수를 하는데요, 아래 코드를 참고하겠습니다.

char* foo(int bar)
{
char temp[5];
...
return temp;
}

void bar(void)
{
int index;
char *string = foo(5);
}

실제로 모 프로래머가 질문한 내용인데요, 위의 코드의 경우 제대로 된 동작을 보장할 수 없습니다. 이유를 알아볼까요?

함수 호출의 구조

컴퓨터 내부에 대해서 지식이 있으신 분들은 함수 호출 시, 스택의 변화에 대해서 알고 계실 것입니다. 깊게 얘기하자면 한 섹션 분량이 나오기 때문에 간략하게 설명하도록 합니다.

어떤 함수A를 호출하면 스택 구조에 함수A의 수행이 끝나면 돌아갈 주소와 지역변수를 위한 공간을 할당합니다. 그리고 함수A를 수행하며, 수행이 끝나면 수행 전 지역변수를 위해 할당한 스택 공간을 해제하고 돌아갈 주소를 스택에서 꺼내와서 함수A를 호출한 함수로 복귀합니다.

위 코드의 경우 함수 foo()를 호출하면 스택이 아래와 같이 구성됩니다. (EIP, EBP 내용은 생략합니다)

 

그렇습니다. 함수 foo()가 실행을 마치면 스택에 쌓아둔 char *temp의 주소는 더 이상 유효하지 않은 것이죠. 운이 좋으면 별 문제 없이 수행될 수도 있습니다. 아래의 코드를 보겠습니다.

#include <stdio.h>
int* func1(){
int foo = 0;
return &foo;
}

int* func2(){
int bar = 1;
return &bar;
}

int main()
{
int* var1 = func1();
int* var2 = func2();
printf("Value from func1() : %dn Value from func2() : %dn", *var1, *var2);
return 0;
}

위 코드의 결과는 아래와 같습니다.

Value from func1() : 1
Value from func2() : 1

func1()과 func2()를 연이어 실행시켰기 때문에 다행히 프로그램이 비정상 종료되지 않았습니다. func1()에서 지역변수 foo 의 주소에 0을 대입한 후 함수가 종료되었습니다. main() 의 var1은 지역변수 foo가 할당되었던 스택의 주소를 그대로 들고 있는 상태구요.

 

이 상황에서 func2()를 호출하면 과거 foo가 할당되었고 var1이 가리키는 주소에 지역변수 bar의 할당하고 여기에 1을 대입했습니다. 결과적으로 main()의 var2는 var1의 가리키는 주소와 동일한 주소를 가리키게 된 것이죠.

따라서 주소에 대입되어 있는 값을 출력하면 마지막으로 호출한 함수 func2()의 지역변수 bar의 값을 출력하게 되는 것입니다. 이 경우에는 정말 운이 좋은 케이스 입니다. 다른 함수를 호출해서 스택 값이 전혀 다르게 변하면 프로그램은 비정상 종료될 가능성이 매우 높습니다.

스패로우에서는 RETURN_LOCAL 체커로 해당 유형의 오류에 대해서 탐지가 가능합니다. 파수닷컴 스패로우는 시맨틱(실행의미 기반)엔진을 바탕으로 프로그램 실행 시 발생할 수 있는 오류까지 정확히 검출하는 국내 최고 기술력의 실행의미 기반 분석도구입니다.

스패로우는 작년 2월 GS인증과 이번 ISO26262인증을 통해 품질과 기술력을 인정받았으며, 올해 국내 업계 최초로 소스코드 보안약점 분석도구 CC인증을 완료하며 시큐어코딩이라는 보안 이슈까지 해결할 수 있는 최적의 솔루션입니다.


 

해당 글은 파수닷컴에서 아이티데일리 게재용으로 제공한 콘텐츠입니다.
저작권은 파수닷컴 측에 있으며, 무단전제 및 복제를 금합니다.
원문출처 : http://blog.fasoo.com/80207109463

저작권자 © 아이티데일리 무단전재 및 재배포 금지