NULL POINTER DEREFERENCE 란?


잘 소개된 블로그를 링크함

NULL POINTER DEREFERENCE 란?

아래 내용의 출처는 http://story.wisedog.net/null-pointer-dereference-%EB%9E%80/

Null Pointer Dereference 란?

이번 포스팅에는 소프트웨어 에러 유형 중 하나인 ‘Null Pointer Dereference(널 포인터 역참조)’ 에 대해서 이야기하고자 합니다. ‘Null Pointer Dereference’는 C, C++, Java, .Net 등의 언어에서 발생할 수 있는 에러인데요, 널 포인터에 어떠한 값을 대입할 때 발생하는 에러입니다. 실행 중인 소프트웨어가 죽는 원인 중 많은 부분이 이 에러 때문입니다. 부끄럽지만 이 글을 쓰는 저도 종종 이 에러를 겪곤 합니다.

C 나 C++ 에서 널 포인터는 ‘어떠한 것도 가리키지 않는 포인터’, ‘할당되지 않은’ 것을 의미하는 특별한 포인터로 쓰입니다. 대부분의 운영체제에서는 이 널 포인터를 메모리의 첫 페이지 주소 0(0x00000000)로 사용하고 있습니다. 따라서 여기에 접근하면 보통 프로그램이 비정상적으로 종료합니다.

Null Pointer Dereference 발생 예제

1
2
3
4
void null_pointer_dereference_ex1() {
int *p = 0; // NULL 대입
*p = 1; // Null pointer dereference!
}

위 샘플 코드는 직접적으로 Null Pointer Dereference 가 발생하는 예제입니다. int 형 포인터 p에 주소 0에 접근하는 포인터를 생성하고, 여기에 값을 할당하려고 시도했습니다. 이 경우 많은 운영체제에서는 세그멘테이션 결함이 발생하면서 프로그램이 종료됩니다.

그런데 절대다수의 코드에서 Null Pointer Dereference 는 이렇게 눈에 빤히 보이는 코드에서 잘 발생하지 않습니다. 보통 라이브러리를 잘못 다룰 때 많이 쓰이죠. 많은 라이브러리 함수는 사용자가 의도한 값이 없거나, 기타 에러 등의 이유로 널 포인터를 리턴하는 경우가 많습니다. 이 리턴된 값을 확인 없이 바로 무언가를 쓸 때 Null Pointer Dereference 가 종종 발생합니다. 아래 코드를 보시죠.

1
2
3
4
5
6
7
8
9
void my_strcpy(char *dst, char *src) {
    if (!src)
        return;
    dst[0] = src[0];
    }
    void npd_func(int length, char *arg) {
        char *buffer = (char*) malloc(length+1); //여기서 NULL이 리턴될 수 있음
    my_strcpy(buffer, "Hello"); // Null Pointer Dereference 발생가능
}

7번째 줄에서 malloc 함수를 호출 시 메모리가 부족하면 널 포인터가 리턴됩니다. 이 널 포인터를 그대로 my_strcpy()의 인자로 넘겨주고 my_strcpy 함수에서는 이 메모리 주소에 무언가를 write 하려하기 때문에 Null Pointer Dereference가 발생할 수 있죠. 이해를 돕기위해 malloc 함수만 예로 들었지만 레퍼런스 상에서 널 포인터를 리턴할 수 있다고 명시한 모든 라이브러리 리턴 값을 잘못 다루거나 인지 값을 잘못 넘겨주면 언제든지 Null Pointer Dereference가 발생할 수 있습니다.

Null Pointer Dereference 가 발생하면

Null Pointer Dereference 에러 메시지
보통 소프트웨어가 Null Pointer Dereference 로 사망할 때 윈도우 운영체제에서 사용자에게 띄우는 에러 메시지. 메모리 0x00000000 을 참조했다는 문구가 나온다.

일반적으로 Null Pointer Dereference 가 발생하면 실행 중인 프로그램이 종료됩니다. 또한 보안적으로 취약해질 수 있습니다. 공격자가 의도적으로 Null Pointer Dereference 를 실행하는 경우, 그 결과 발생하는 예외 사항을 추후 공격을 계획하는데 악용할 수 있기 때문입니다.

Null Pointer Dereference를 막으려면

왕도가 없습니다. 모든 포인터나 레퍼런스 혹은 널 포인터가 할당될 가능성이 있는 포인터나 레퍼런스에 대해서 사용전 널 포인터 체크를 하고 사용해야 합니다. 아래 소스 코드는 Null Pointer Dereference 가 발생할 수 있는 코드입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFSIZE 100
int main()
{
    char *p = NULL;
    char cgi_home[BUFSIZE];
    p = getenv("CGI_HOME");
    strncpy(cgi_home, p, BUFSIZE -1);
    cgi_home[BUFSIZE-1] = '\0';
    return 0;
}

10번째 줄을 보겠습니다. “CGI_HOME”이라는 환경 변수가 없는 경우 getenv 함수는 널 포인터를 리턴합니다. 이 값을 널 포인터 확인 없이 그대로 strncpy ()에 사용하면 Null Pointer Dereference가 발생합니다. 앞으로 아래처럼 프로그래밍해야겠죠?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFSIZE 100
int main()
{
    char *p = NULL;
    char cgi_home[BUFSIZE];
    p = getenv("CGI_HOME");
    if(p == NULL)
    {
        exit(1);
    }
    strncpy(cgi_home, p, BUFSIZE -1);
    cgi_home[BUFSIZE-1] = '\0';
    return 0;
}

현명한 개발자 & 회사의 해결책!

Null Pointer Dereference 를 막는 방법은 알았면 짜증이 밀려들고 부아가 치밀 사람들이 많을 것으로 사료됩니다. 100줄, 200줄로 작성된 간단한 프로그램이 아닌 이상에야 사람 손으로 일일이 Null Pointer Dereference를 찾는 것은 개발자 본인에게도, 그리고 회사에게도 비능률적입니다. 리눅스 커널은 수백만 라인이나 되는데 사람이 소스를 뒤져가면서 직접 찾아내는 것은 그야말로 삽질의 결정판이라 할 수 있습니다.

이 같은 삽질을 하지 않기 위해 많은 회사에서는 정적분석도구(Static Code Analysis Tool)를 사용합니다. 정적분석도구는 코드를 분석해서 결함을 찾아주는 소프트웨어입니다. 대표적인 정적분석도구로는 국내의 Sparrow 등이 있으며 외산으로는 Prevent, PRQA, Klocwork, Code Sonar 등이 대표적입니다.

이 글은 제가 작성한 글로서, 기존 블로그에서 옮긴 글입니다. 또한 이 글은 파수닷컴 회사 공식블로그에도 게재되었습니다.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s