본문 바로가기

시스템 소프트웨어 개발을 위한 Arm 아키텍처의 구조와 원리/11장: AAPCS(함수 호출 규약)

[Arm][AAPCS] 스택 오염(Stack Corruption)이란

스택에 저장된 레지스터나 변수의 값이 예상치 않은 값으로 변경되는 현상을 스택 오염이라고 합니다. 스택 오염이 발생하는 단계를 설명하기 전에 정상적인 과정으로 스택에 저장된 데이터를 저장하고 로딩하는 단계를 알아보겠습니다. 함수를 호출할 때는 일반적으로 다음과 같은 동작을 수행합니다. 

 링크 레지스터의 값을 스택 공간에 저장  
 함수에 위치한 어셈블리 명령어 실행
 스택에 저장된 링크 레지스터의 값을 읽어 PC에 다시 로딩

만약 스택 공간에 저장된 링크 레지스터 값이 예상 밖의 다른 값으로 바뀌면 어떻게 동작할까요? 바뀐 다른 값(주소)을 PC에 로딩하게 되며, 정상적으로 서브 루틴으로 분기하는 대신 이상한 주소로 점프를 하게 됩니다. 스택 오염이 발생하는 단계을 세분화하면 다음과 같습니다.

 링크 레지스터의 값을 스택 공간에 저장
 함수에 위치한 어셈블리 명령어 실행
 스택 오염이 발생해 스택 공간에 저장된 링크 레지스터의 값이 바뀜 
 스택에 저장된 오염된 링크 레지스터의 값을 읽어 PC에 다시 로딩

일반적으로 위와 같은 상황에서 스택 오염이 발생하는데, 실전 프로젝트에서 이런 문제를 많이 만나게 됩니다. 다음 그림을 보면서 스택 오염에 대해서 더 알아봅시다.

 

 

먼저 '함수 호출 직전'이라고 표기된 그림의 왼쪽 부분은 함수가 처음 실행한 시점의 프로세스의 스택과 스택에 저장된 데이터를 나타냅니다. 0xc000cff0--0xc000cffc 구간의 주소를 보면 AAPCS와 연관된 레지스터의 값이 보입니다. 여기서 가장 중요한 데이터는 0xc000cff8 주소에 저장됐는데, 0xc000cff8 주소에 있는 0x80138f10는 함수를 실행한 다음에 복귀할 주소입니다.

이어서 프로세스의 스택이 오염된 상태를 나타내는 그림의 오른쪽 부분을 보겠습니다. 그림에서 회색으로 표시된 부분을 보면 0xc000cfe8--0xc000cffc 구간의 주소에 있던 값이 0x0으로 바뀌었습니다. 스택 오염으로 프로세스의 스택에 저장된 중요한 데이터가 바뀐 것입니다.

위 그림과 같이 프로세스의 스택 공간에 저장된 링크 레지스터의 값을 포함한 중요 데이터가 0x0으로 바뀌는 이유는 무엇일까요? 주로 함수 내에서 지역 변수로 선언된 배열이 정해진 사이즈를 벗어난 메모리 복사 연산을 수행하면 스택 오염이 발생합니다. 이 밖에도 포인터나 배열을 사용한 연산을 잘못할 경우에도 스택 오염이 발생합니다. 

 


[정보]
정해진 사이즈를 벗어난 메모리 복사를 흔히 Out of bound라고 부는데, 대표적인 메모리 복사를 수행하는 라이브러리 함수는 memcpy()와 memset() 함수입니다.



그렇다면 지역 변수로 선언된 배열에 대한 메모리 복사를 잘못 수행하면 스택 오염이 일어나는 이유는 무엇일까요? 바로 지역 변수로 선언된 배열은 프로세스의 스택 공간을 사용하기 때문입니다.

함수에 지역 변수로 선언된 배열에 메모리 복사로 데이터를 이동시키는 연산은 메모리에 직접 접근하는 저수준 동작입니다. 만약 메모리 복사를 잘못 수행하면 시스템 오류를 유발할 수 있습니다. 그래서 지역 변수로 선언된 배열에 memcpy() 와 같은 메모리 연산을 수행하면 프로세스의 스택 공간에 직접 접근해 데이터를 쓰거나 읽을 수 있으므로 메모리 복사 연산을 수행하는 코드를 작성할 때는 주의를 기울여야 합니다.