Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
Tags
- Software Engineering
- Python
- agile
- ubuntu
- Malaysia
- hbase
- web
- Programming
- program
- Java
- Spain
- Italy
- history
- psychology
- MySQL
- django
- Book
- leadership
- Kuala Lumpur
- comic agile
- UK
- RFID
- Book review
- hadoop
- management
- QT
- programming_book
- Linux
- France
- erlang
Archives
- Today
- Total
메모리 디버깅을 위한 친구 본문
출처: 열씨미와 게을러의 리눅스 개발 노하우 탐험기
2009/08/17 - [Programming] - diff, find, md5sum, patch
2009/08/17 - [Programming] - find, grep, ctags, cscope, global
2009/08/17 - [Programming] - shared library, strace, ldconfig, ldd, gcc, strings, od, nm, c++filt, readelf
2009/09/14 - [Programming] - configure
2009/09/21 - [Programming] - 자동화된 빌드 시스템 구축
2009/09/21 - [Programming] - 자동화된 빌드 시스템 구축 (2)
2009/09/21 - [Programming] - 숨겨진 1인치의 의존성을 찾아서 - make
rmalloc 개요
http://www.hexco.de/rmdebug
실행방법
rmalloc이 잡아내는 문제점 5가지
rmalloc package의 Makefile을 이용해 make를 하면 rtest.c의 예제를 사용할 수 있음
1. 동적 메모리 1byte overflow(off-by-one) 오류
주로 동적 메모리를 할당한 후 문자열 크기 계산 과정에서 실수하는 경우 발생
흔히 '\0'으로 끝나는 것을 잊어버리고 문자열을 사용할 경우 발생
Test1에서 3byte('0', '1', '\0') str에 4byte('0', '1', '2', '\0') 문자열을 copy
2. 동적 메모리 overflow 오류
할당한 메모리를 넘어서는 문자열을 복사하는 경우 발생
Test2에서 2byte str에 8byte의 문자열을 copy
3. 동적 메모리 포인터 색인 overflow(1byte 영역을 할당한 후 범위를 초과)
Test3에서 배열을 1개만 잡고 index를 1로 넘김
4. 동일한 메모리 두 번 해제
Test4에서 bla[0]을 두 번 해제
NULL을 해제할 경우를 찾아내고 싶다면 ALLOW_FREE_NULL macro로 제어 가능
5. 잘못된 메모리 번지 해제
Test5에서 0x12345678이라는 잘못된 값으로 free 시도
rmalloc test 결과
1. 동적 메모리 1byte overflow(off-by-one) 오류
buffer 손상을 감지하기 위해 rmalloc library가 달아놓은 앞쪽 a5a5a5a5가 00a5a5a5로 변한 것으로 감지
2. 동적 메모리 overflow 오류
사례 1과 유사한 방법
3. 동적 메모리 포인터 색인 overflow(1byte 영역을 할당한 후 범위를 초과)
사례 1, 2와 유사
4. 동일한 메모리 두 번 해제
double or false delete 감지
5. 잘못된 메모리 번지 해제
사례 4와 유사
rmalloc이 제공하는 switch
macro 설정을 rmalloc.c에서 변경한 후 다시 build해 rmalloc.o와 대상 program을 다시 link
rmalloc에서 제공하는 추가 macro
이 macro를 적용하기 위해서는 원본 source code를 수정해야 한다
RM_TEST
모든 할당받은 블록에 대해 새로 점검을 수행
이 macro를 사용하기 위해서는 RM_TEST_DEPTH를 1이상으로 설정해야 한다
RM_STAT
모든 할당 받은 블록에 대해 새로 점검을 수행한 후 표준 오류로 할당받은 블록 통계를 출력
이 macro를 사용하기 위해서는 RM_TEST_DEPTH를 1이상으로 설정해야 한다
RM_RETAG
종종 메모리 영역을 받아서 몇몇 기본값을 설정한 다음 사용자에게 돌려주는 함수를 사용하는 경우가 있다. 이런 특별한 함수를 사용할 경우 rmalloc이 메모리 추적에 어려움을 겪을 가능성이 있으므로, macro를 수행한 시점으로 위치를 초기화한다
RM_SET
할당 받은 메모리 영역에 특별한 flag를 붙임
현재 RM_STATIC과 RM_STRING을 사용할 수 있다
RM_STATIC: 해제하지 않을 정적으로 고정된 메모리를 의미
결론
1. malloc, calloc, realloc, free와 같은 기본 memory management function 숙지
2. rmalloc은 만능이 아니며, 기본적인 memory management function을 사용할 때 발생하는 dynamic memory allocation 관련 문제만 찾을 수 있다. 즉 stack area에 잡히는 auto variable 관련 문제는 찾지 못한다
3. 아주 복잡하거나 timing이 중요한 program에서 rmalloc이 문제를 일으키는 경우도 있다
4. rmalloc에서 찾는 5가지 문제는 일상적으로 발생할 수 있는 것이므로 사전 예방을 위해 code를 검토하는 습관이 필요
5. {}, () matching이 틀려 발생할 수 있는 문제를 예방하기 위해 coding convention이 중요
참고
리눅스 디버깅과 성능 튜닝, 박재호/이해영 역, 에이콘 출판사 2006년: 4장
월간 임베디드 월드 2006년 7월호 '리눅스 개발자를 위한 디버깅 기법: 메모리 관리 디버깅 기법 소개', 박재호 기고
추가적인 메모리 할당 디버깅 라이브러리
MEMWATCH: http://www.linkdata.se/sourcecode.html
YAMD: http://www.cs.hmc.edu/~nate/yamd/
Electric Fence: http://perens.com/FreeSoftware/
Valgrind: http://valgrind.org/
Dmalloc: http://dmalloc.com/
2009/08/17 - [Programming] - diff, find, md5sum, patch
2009/08/17 - [Programming] - find, grep, ctags, cscope, global
2009/08/17 - [Programming] - shared library, strace, ldconfig, ldd, gcc, strings, od, nm, c++filt, readelf
2009/09/14 - [Programming] - configure
2009/09/21 - [Programming] - 자동화된 빌드 시스템 구축
2009/09/21 - [Programming] - 자동화된 빌드 시스템 구축 (2)
2009/09/21 - [Programming] - 숨겨진 1인치의 의존성을 찾아서 - make
rmalloc 개요
http://www.hexco.de/rmdebug
실행방법
- package(rmalloc.tgz) download
- rmalloc.c를 -c option과 함께 gcc로 compile
- debugging을 원하는 code에 #define MALLOC_DEBUG 추가, #include "rmalloc.h" 추가, link시 rmalloc.o link
- program 실행
- rmalloc.h에 일반적인 memory management function인 malloc, calloc, realloc, free, strdup function들을 감싸는 macro가 정의되어, rmalloc.h를 include한 code에서는 libc 대신 debugging function을 호출하게 됨. debugging function은 allocated buffer의 직전과 직후에 특별한 문자열로 표시를 해 돌려주고, 나중에 이 부분이 손상이 되면 문제가 된 file name과 line number를 출력하고 강제로 종료
- gdb에서 사용하는 것도 가능
rmalloc이 잡아내는 문제점 5가지
rmalloc package의 Makefile을 이용해 make를 하면 rtest.c의 예제를 사용할 수 있음
1. 동적 메모리 1byte overflow(off-by-one) 오류
주로 동적 메모리를 할당한 후 문자열 크기 계산 과정에서 실수하는 경우 발생
흔히 '\0'으로 끝나는 것을 잊어버리고 문자열을 사용할 경우 발생
Test1에서 3byte('0', '1', '\0') str에 4byte('0', '1', '2', '\0') 문자열을 copy
static void Test1(void)
{
char *str = strdup("01");
strcpy(str, "012"); /* wrong! */
free(str);
}
{
char *str = strdup("01");
strcpy(str, "012"); /* wrong! */
free(str);
}
2. 동적 메모리 overflow 오류
할당한 메모리를 넘어서는 문자열을 복사하는 경우 발생
Test2에서 2byte str에 8byte의 문자열을 copy
static void Test2(void)
{
char *str = strdup("0");
strcpy(str, "012long"); /* wrong! */
free(str);
}
{
char *str = strdup("0");
strcpy(str, "012long"); /* wrong! */
free(str);
}
3. 동적 메모리 포인터 색인 overflow(1byte 영역을 할당한 후 범위를 초과)
Test3에서 배열을 1개만 잡고 index를 1로 넘김
static void Test3(void)
{
char **arr = malloc(1*sizeof(char *));
char *bla = strdup("Kaputt!");
arr[1] = bla; /* wrong! */
free(arr); /* last chance to find */
free(bla);
}
{
char **arr = malloc(1*sizeof(char *));
char *bla = strdup("Kaputt!");
arr[1] = bla; /* wrong! */
free(arr); /* last chance to find */
free(bla);
}
4. 동일한 메모리 두 번 해제
Test4에서 bla[0]을 두 번 해제
NULL을 해제할 경우를 찾아내고 싶다면 ALLOW_FREE_NULL macro로 제어 가능
static void Test4(void)
{
unsigned int u;
int i;
char *bla[TEST_SIZE];
unsigned int count = TEST_SIZE;
char *foo[20];
void *pending;
memset(bla, 0, sizeof(bla));
srand(time(NULL));
while (count > 0) {
u = (u + rand()) % TEST_SIZE;
if (bla[u] == NULL) {
bla[u] = malloc(u+1);
assert(bla[u] != NULL);
count--;
}
}
for (i = TEST_SIZE-1; i >= 0; i--) {
bla[i] = realloc(bla[i], 2*i+2);
}
pending = bla[0];
count = TEST_SIZE;
while (count > 0) {
u = (u + rand()) % TEST_SIZE;
if (bla[u] != NULL) {
free(bla[u]);
bla[u] = NULL;
count--;
}
}
free(pending);
}
{
unsigned int u;
int i;
char *bla[TEST_SIZE];
unsigned int count = TEST_SIZE;
char *foo[20];
void *pending;
memset(bla, 0, sizeof(bla));
srand(time(NULL));
while (count > 0) {
u = (u + rand()) % TEST_SIZE;
if (bla[u] == NULL) {
bla[u] = malloc(u+1);
assert(bla[u] != NULL);
count--;
}
}
for (i = TEST_SIZE-1; i >= 0; i--) {
bla[i] = realloc(bla[i], 2*i+2);
}
pending = bla[0];
count = TEST_SIZE;
while (count > 0) {
u = (u + rand()) % TEST_SIZE;
if (bla[u] != NULL) {
free(bla[u]);
bla[u] = NULL;
count--;
}
}
free(pending);
}
5. 잘못된 메모리 번지 해제
Test5에서 0x12345678이라는 잘못된 값으로 free 시도
static void Test5(void)
{
free((void *)0x12345678);
}
{
free((void *)0x12345678);
}
rmalloc test 결과
1. 동적 메모리 1byte overflow(off-by-one) 오류
buffer 손상을 감지하기 위해 rmalloc library가 달아놓은 앞쪽 a5a5a5a5가 00a5a5a5로 변한 것으로 감지
------------------
Running test 1...
------------------
<MALLOC_DEBUG> Corrupted block end (possibly written past the end)
should be: a5a5a5a5 5b5b5b5b abababab aa55aa55
is: 00a5a5a5 5b5b5b5b abababab aa55aa55
block was allocated in rtest.c:152 [3 Bytes, generation 1]
error was detected in rtest.c:154
Looks like string allocated one byte too short
(forgetting the nul byte)
Running test 1...
------------------
<MALLOC_DEBUG> Corrupted block end (possibly written past the end)
should be: a5a5a5a5 5b5b5b5b abababab aa55aa55
is: 00a5a5a5 5b5b5b5b abababab aa55aa55
block was allocated in rtest.c:152 [3 Bytes, generation 1]
error was detected in rtest.c:154
Looks like string allocated one byte too short
(forgetting the nul byte)
2. 동적 메모리 overflow 오류
사례 1과 유사한 방법
------------------
Running test 2...
------------------
<MALLOC_DEBUG> Corrupted block end (possibly written past the end)
should be: a5a5a5a5 5b5b5b5b abababab aa55aa55
is: 326c6f6e 67005b5b abababab aa55aa55
block was allocated in rtest.c:171 [2 Bytes, generation 1]
error was detected in rtest.c:173
Looks somewhat like a too long string,
ending with "2long"
Running test 2...
------------------
<MALLOC_DEBUG> Corrupted block end (possibly written past the end)
should be: a5a5a5a5 5b5b5b5b abababab aa55aa55
is: 326c6f6e 67005b5b abababab aa55aa55
block was allocated in rtest.c:171 [2 Bytes, generation 1]
error was detected in rtest.c:173
Looks somewhat like a too long string,
ending with "2long"
3. 동적 메모리 포인터 색인 overflow(1byte 영역을 할당한 후 범위를 초과)
사례 1, 2와 유사
------------------
Running test 3...
------------------
<MALLOC_DEBUG> Corrupted block end (possibly written past the end)
should be: a5a5a5a5 5b5b5b5b abababab aa55aa55
is: 203c0608 5b5b5b5b abababab aa55aa55
block was allocated in rtest.c:191 [4 Bytes, generation 1]
error was detected in rtest.c:195
First 4 bytes of overwritten memory can be interpreted
as a pointer to a block allocated in:
rtest.c:192 [8 Bytes, generation 2]
Running test 3...
------------------
<MALLOC_DEBUG> Corrupted block end (possibly written past the end)
should be: a5a5a5a5 5b5b5b5b abababab aa55aa55
is: 203c0608 5b5b5b5b abababab aa55aa55
block was allocated in rtest.c:191 [4 Bytes, generation 1]
error was detected in rtest.c:195
First 4 bytes of overwritten memory can be interpreted
as a pointer to a block allocated in:
rtest.c:192 [8 Bytes, generation 2]
4. 동일한 메모리 두 번 해제
double or false delete 감지
------------------
Running test 4...
------------------
<MALLOC_DEBUG> Double or false delete
Heap adress of block: 0x8073ad0
Detected in rtest.c:250
Trying identification (may be incorrect!):
Allocated in rtest.c:235 [2 Bytes]
Running test 4...
------------------
<MALLOC_DEBUG> Double or false delete
Heap adress of block: 0x8073ad0
Detected in rtest.c:250
Trying identification (may be incorrect!):
Allocated in rtest.c:235 [2 Bytes]
5. 잘못된 메모리 번지 해제
사례 4와 유사
------------------
Running test 5...
------------------
<MALLOC_DEBUG> Double or false delete
Heap adress of block: 0x12345678
Detected in rtest.c:268
Running test 5...
------------------
<MALLOC_DEBUG> Double or false delete
Heap adress of block: 0x12345678
Detected in rtest.c:268
rmalloc이 제공하는 switch
macro 설정을 rmalloc.c에서 변경한 후 다시 build해 rmalloc.o와 대상 program을 다시 link
switch name | 설정값 | 설명 |
RM_TEST_DEPTH | 0, 1, 2 | 0: 최소, 1: 통계 정보 제공/메모리 해제 시점에 검사, 2: malloc 계열 함수 호출 시점에 검사 |
GENERATIONS | ON/OFF | debugger에서 rmalloc_generation() 중단점을 걸어 어떤 function stack이 memory allocation을 하는지 추적하는 flag |
ELOQUENT | ON/OFF | 확장 메모리 할당 정보 출력 유무 |
WITH_FLAGS | ON/OFF | 다음에 설명할 RM_SET 활성 유무 |
ALLOW_REALLOC_NULL | ON/OFF | realloc(NULL) 허용 유무 |
ALLOW_FREE_NULL | ON/OFF | free(NULL) 허용 유무 |
BREAK_GENERATION_COND | 외부 환경 | GENERATIONS를 활성화했을 경우 rmalloc_generation() |
변수 참조 | 호출을 수행하기 위한 조건 정의 | |
MAX_STAT_GENERATIONS | 숫자 | GENERATIONS를 활성화했을 경우 통계에서 출력할 최대 generation 횟수 |
rmalloc에서 제공하는 추가 macro
이 macro를 적용하기 위해서는 원본 source code를 수정해야 한다
RM_TEST
모든 할당받은 블록에 대해 새로 점검을 수행
이 macro를 사용하기 위해서는 RM_TEST_DEPTH를 1이상으로 설정해야 한다
RM_TEST; // heap에 있는 모든 memory 요소를 점검
RM_STAT
모든 할당 받은 블록에 대해 새로 점검을 수행한 후 표준 오류로 할당받은 블록 통계를 출력
이 macro를 사용하기 위해서는 RM_TEST_DEPTH를 1이상으로 설정해야 한다
RM_STAT; // 할당 받은 메모리를 보여줌
RM_RETAG
종종 메모리 영역을 받아서 몇몇 기본값을 설정한 다음 사용자에게 돌려주는 함수를 사용하는 경우가 있다. 이런 특별한 함수를 사용할 경우 rmalloc이 메모리 추적에 어려움을 겪을 가능성이 있으므로, macro를 수행한 시점으로 위치를 초기화한다
struct complicated* cpointer = get_new_complicated_struct(any_arg);
RM_RETAG(cpointer); // file 위치를 여기에 설정
orRM_RETAG(cpointer); // file 위치를 여기에 설정
struct complicated* cpointer = RM_RETAG(get_new_complicated_struct(any_arg));
RM_SET
할당 받은 메모리 영역에 특별한 flag를 붙임
현재 RM_STATIC과 RM_STRING을 사용할 수 있다
RM_STATIC: 해제하지 않을 정적으로 고정된 메모리를 의미
static char* buffer = NULL;
static int length = 0;
...
if ( newlength > length ) {
if ( buffer == NULL ) {
buffer = malloc(length = newlength);
RM_SET(buffer, RM_STATIC); // 결코 해제되지 않음을 명시
}
else {
buffer = realloc(buffer, length = newlength);
}
if ( buffer == NULL ) {
error(NOMEM);
}
}
RM_STRING: 문자열을 담는 메모리를 의미. ELOQUENT mode에서만 통계에 잡히며, strdup는 자동으로 이 flag를 설정한다static int length = 0;
...
if ( newlength > length ) {
if ( buffer == NULL ) {
buffer = malloc(length = newlength);
RM_SET(buffer, RM_STATIC); // 결코 해제되지 않음을 명시
}
else {
buffer = realloc(buffer, length = newlength);
}
if ( buffer == NULL ) {
error(NOMEM);
}
}
struct numstr {
int number;
char* name;
};
...
struct numstr* foo = malloc(sizeof(struct numstr));
foo->name = calloc(1, 2);
foo->name[0] = 'X';
RM_SET(foo->name, RM_STRING);
int number;
char* name;
};
...
struct numstr* foo = malloc(sizeof(struct numstr));
foo->name = calloc(1, 2);
foo->name[0] = 'X';
RM_SET(foo->name, RM_STRING);
결론
1. malloc, calloc, realloc, free와 같은 기본 memory management function 숙지
2. rmalloc은 만능이 아니며, 기본적인 memory management function을 사용할 때 발생하는 dynamic memory allocation 관련 문제만 찾을 수 있다. 즉 stack area에 잡히는 auto variable 관련 문제는 찾지 못한다
3. 아주 복잡하거나 timing이 중요한 program에서 rmalloc이 문제를 일으키는 경우도 있다
4. rmalloc에서 찾는 5가지 문제는 일상적으로 발생할 수 있는 것이므로 사전 예방을 위해 code를 검토하는 습관이 필요
5. {}, () matching이 틀려 발생할 수 있는 문제를 예방하기 위해 coding convention이 중요
참고
리눅스 디버깅과 성능 튜닝, 박재호/이해영 역, 에이콘 출판사 2006년: 4장
월간 임베디드 월드 2006년 7월호 '리눅스 개발자를 위한 디버깅 기법: 메모리 관리 디버깅 기법 소개', 박재호 기고
추가적인 메모리 할당 디버깅 라이브러리
MEMWATCH: http://www.linkdata.se/sourcecode.html
YAMD: http://www.cs.hmc.edu/~nate/yamd/
Electric Fence: http://perens.com/FreeSoftware/
Valgrind: http://valgrind.org/
Dmalloc: http://dmalloc.com/
Comments