cs

[크래프톤 정글] c프로그래밍에서의 고통된 메모리 관련 버그(CSAP 9.11장)

하루이2222 2024. 10. 14. 19:12

다음은 CSAPP 9.11장에서 설명된 메모리 관련 버그들에 대한 설명과 각 상황에 대한 예시를 들어 정리 해보고자 한다.

9.11.1 잘못된 포인터 역참조

잘못된 포인터를 역참조하면 가상 메모리의 잘못된 위치를 참조하게 되어 프로그램이 비정상 종료된다.

예시:

int *ptr = NULL;
*ptr = 100;  // NULL 포인터 역참조, 프로그램이 종료됨

위 코드는 NULL 포인터를 역참조하려고 하기 때문에 실행 중에 프로그램이 크래시된다.

9.11.2 초기화되지 않은 메모리 읽기

초기화되지 않은 메모리를 읽으면 해당 메모리에는 쓰레기 값이 포함될 수 있다.

예시:

int *arr = (int *)malloc(5 * sizeof(int));
printf("%d\n", arr[0]);  // 초기화되지 않은 메모리 읽기
free(arr);

위 코드에서 arr 배열은 동적으로 할당되었지만, 초기화되지 않았기 때문에 arr[0]에 있는 값은 예측할 수 없는 값이 된다.

9.11.3 스택 버퍼 오버플로우 허용

스택에 할당된 버퍼의 크기를 초과하여 데이터를 쓰면 메모리 오버플로우가 발생한다.

예시:

void overflow() {
    char buf[10];
    gets(buf);  // 입력 길이를 제한하지 않음, 버퍼 오버플로우 발생 가능
}

gets 함수는 입력 길이에 제한을 두지 않기 때문에, buf의 크기(10)를 초과하는 입력을 받을 경우 스택 오버플로우가 발생한다.

9.11.4 포인터와 객체의 크기가 같다고 가정

포인터와 포인터가 가리키는 객체의 크기가 다를 수 있으며, 이를 혼동하면 잘못된 메모리 접근이 발생할 수 있다.

예시:

int *ptr;
char c = 'a';
ptr = (int *)&c;  // 잘못된 타입 캐스팅
*ptr = 100;       // 메모리 손상 발생 가능

char 타입 변수를 int 포인터로 변환하여 처리하는 것은 위험하다. int 크기만큼 메모리에 쓰려고 할 때 메모리 손상이 발생할 수 있다.

9.11.5 1차이 오류 (Off-by-One Error)

배열의 경계를 벗어난 인덱스로 접근하려고 할 때 발생하는 오류.

예시:

int arr[10];
for (int i = 0; i <= 10; i++) {
    arr[i] = i;  // 마지막 반복에서 배열 경계 초과
}

위 코드에서 i가 10일 때, 배열의 크기를 초과한 인덱스에 접근하려고 하므로 arr[10]은 유효하지 않다.

9.11.6 포인터가 가리키는 객체 대신 포인터 자체를 참조

포인터 연산을 잘못 사용하여 포인터 자체를 참조하거나 조작하는 경우가 발생할 수 있다.

예시:

int *ptr;
int value = 10;
ptr = &value;
*ptr++;  // 잘못된 연산: 포인터를 증가시키고 나서 역참조

*ptr++는 포인터를 증가시킨 후 값을 역참조하는데, 의도한 것과 다르게 동작할 수 있다.

9.11.7 포인터 연산 오해

포인터 연산은 포인터가 가리키는 타입의 크기 단위로 수행되므로, 이를 잘못 이해하면 오류가 발생할 수 있다.

예시:

int arr[5] = {1, 2, 3, 4, 5};
int *ptr = arr;
ptr = ptr + 1;  // 포인터가 4바이트(int 크기)만큼 이동
printf("%d\n", *ptr);  // 출력: 2

ptr + 1은 1바이트가 아닌 int의 크기만큼 포인터가 이동한다는 점을 이해해야 한다.

9.11.8 존재하지 않는 변수를 참조

스택에 할당된 지역 변수를 함수가 반환한 후 참조하면 예상치 못한 동작이 발생한다.

예시:

int* bad_pointer() {
    int local_var = 10;
    return &local_var;  // 함수가 반환되면 local_var는 사라짐
}

int main() {
    int *ptr = bad_pointer();
    printf("%d\n", *ptr);  // 잘못된 참조
}

위 코드에서 local_var는 함수가 반환된 후 더 이상 존재하지 않으므로, 이를 참조하면 오류가 발생할 수 있다.

9.11.9 해제된 힙 블록의 데이터 참조

이미 free된 메모리 블록을 다시 참조하면 비정상적인 동작을 유발할 수 있다.

예시:

int *ptr = (int *)malloc(sizeof(int));
*ptr = 100;
free(ptr);
printf("%d\n", *ptr);  // 해제된 메모리 참조, 예기치 않은 동작 발생 가능

free된 메모리 블록은 더 이상 유효하지 않으므로, 이를 참조하면 예기치 않은 결과가 발생할 수 있다.

9.11.10 메모리 누수 발생

메모리 누수는 할당한 메모리를 해제하지 않고 계속 남겨두는 경우 발생한다.

예시:

void memory_leak() {
    int *ptr = (int *)malloc(sizeof(int));
    *ptr = 100;
    // free(ptr);  // 메모리 해제하지 않음, 메모리 누수 발생
}

위 함수에서 malloc을 통해 할당된 메모리를 free하지 않으면 메모리 누수가 발생 할 수 있다.