Tech 정적코드분석/코딩표준

지역변수 주소 리턴 오류

함수 호출 시 스택 구조

초보 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() : %d\n Value from func2() : %d\n", *var1, *var2);
 return 0;
}

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

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

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

stack2

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

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

Leave a Reply

Leave a Reply

Your email address will not be published. Required fields are marked *