Address Sanitizer 를 이용하여 힙 메모리 오류 디버깅하기

Address Sanitizer 를 이용하여 힙 메모리 오류 디버깅하기

C/C++ 언어로 작성된 프로그램은 다양한 유형의 오류가 발생할 수 있습니다. 대부분의 경우 디버거의 브레이크 포인트, Memory Watch Point 등의 기능으로 쉽게 디버깅할 수 있지만, 스레드간 경합에 의한 오류, 힙 메모리 오염 등은 디버깅하기가 쉽지 않습니다. 특히 동적 메모리 할당 시 사용되는 힙 메모리가 오염된다면 프로그램은 전혀 상관 없는 위치에서 크래시할 수 있기 때문에 원인을 찾아 수정하기가 매우 어렵습니다.

힙 메모리가 망가진 경우 프로그래머는 대개 힙 메모리를 망가트린 곳이 아니라 망가진 힙 메모리를 접근하여 크래시하는 위치에서 디버깅을 시작합니다. 프로그래머는 엉뚱한 코드를 분석하며 긴 시간을 허비하기 십상입니다. 이 글에서는 힙 메모리 오염을 만드는 위치를 찾아주는 Address Sanitizer 라는 메모리 오류 탐지기를 이용하여 힙을 망가트리는 대표적인 원인 중 하나인 Double-Free 문제를 빠르게 디버깅하는 방법을 소개합니다.

Address Sanitizer 는 Ubuntu 와 Centos 에서 패키지로 제공되므로 패키지 매니저를 통해서 설치하실 수 있습니다.

Ubuntu 사용시

$ sudo apt-get install libasan2

Centos 사용시

 $ sudo yum install libasan

Address Sanitizer 는 다른 메모리 할당자 라이브러리와 다르게 라이브러리 링크만으로 사용할 수 없습니다. 링크 단계 뿐만 아니라 컴파일 단계에서도 컴파일러에게 기능을 켜도록 알려주기 위해서 GCC 플래그 “-fsanitize=address -lasan” 를 추가하셔야 됩니다. Address Sanitizer 가 단순한 라이브러리가 아니라 컴파일러의 도움을 받기 때문입니다.

$ gcc -c my_source.cc -fsanitize=address

$ gcc -o my_executable my_source.o -fsanitize=address -lasan

사용 예시: Double-Free 오류 찾기

아래 코드는 “new []” 로 할당한 메모리를 “delete []” 로 두 번 해제하는 오류를 가지고 있으며 힙 메모리를 망가트립니다. 

p1

<코드 1>

이 프로그램을 실행하면 힙 메모리가 망가지며, 메모리 할당자에 따라 다른 결과를 만들어냅니다. Address Sanitizer 를 사용했다면 프로그램은 실행 후 즉시 크래시하고 아래 내용이 콘솔에 출력됩니다.

p2

<그림 1>


출력된 내용은 이 코드를 디버깅하기 위한 3 가지 정보를 제공합니다.

1. 메모리 할당 위치

3

<그림 2>

22~27 번째 줄은 메모리가 할당된 위치와 스레드를 보여줍니다. <코드 1> 의 8 번째 줄에 해당하는 호출 스택을 볼 수 있습니다. (출력된 스레드 이름은 “pthread_setname_np()” 등을 함수로 붙여지며, 이 예제는 iFunEngine 으로 작성되었고 “event” 라는 이름을 갖는 스레드에서 실행됐습니다.)

2. 메모리 해제 위치

2

<그림 3>

12~19 번째 줄은 포인터 p 를 해제한 위치와 스레드를 출력합니다. <코드 1> 의 4 번째 줄에 해당하는 호출 스택을 볼 수 있습니다.

3. 두 번째 메모리 해제 위치(Double-Free)

1

<그림 4>

1~8 번째 줄은 프로그램이 <코드 1> 의 8 번째 줄에서 할당한 포인터 p 를 두 번째 해제를 시도한 위치와 스레드가 출력됩니다. <코드 1> 의 10 번째 줄에 해당하는 호출 스택을 볼 수 있습니다.

Address Sanitizer 가 출력한 정보를 토대로 프로그래머는 메모리 할당 위치, 해제 위치, 그리고 문제의 Double-Free 가 발생하는 두 번째 해제 위치를 찾을 수 있습니다. 이는 프로그램을 어떻게 수정해야할지 결정하는데 충분합니다.

기본 할당자나 Tcmalloc, Jemalloc 과 같이 성능에 초점을 맞춘 할당자의 경우 아무런 정보도 주지 않거나, 두 번째 해제 위치 정도만 알 수 있을 것입니다. (단, 디버그 기능을 활성화 한 경우 조금 더 많은 정보를 얻을 수 있습니다.) VisualStudio, GDB 와 같은 인터랙티브 디버거는 이러한 문제를 해결할 직접적인 기능을 제공하지 않으며 Valgrind memcheck 와 같은 전문적인 도구는 성능 저하가 심하다는 단점이 있습니다. 이는 컴파일 타임에 정보 수집을 위한 코드를 삽입하여 성능 저하가 적은 Address Sanitizer 의 큰 장점 중 하나 입니다.

Address Sanitizer 는 위에서 설명한 Double-Free 외에도 메모리 릭, 해제된 메모리 접근, 배열 인덱스 오류 등 거의 모든 메모리 오류를 찾을 수 있는 기능을 제공합니다. 최신 컴파일러에서 기본으로 지원하고 있어서 쉽게 사용할 수 있습니다. 더 자세한 설명은 https://github.com/google/sanitizers/wiki/AddressSanitizer 를 참고하시기 바랍니다.

답글 남기기

댓글을 게시하려면 다음의 방법 중 하나를 사용하여 로그인 하세요:

WordPress.com 로고

WordPress.com의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Google photo

Google의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Twitter 사진

Twitter의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Facebook 사진

Facebook의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

%s에 연결하는 중

This site uses Akismet to reduce spam. Learn how your comment data is processed.