Post

C언어 버그

목차


C언어의 위험한 코드를 정리한 문서입니다.

메모리누수, 널 문자열 비교

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main()
{
	int p_equal_flag = 0;

	char *p = (char *)malloc(20);
	p = "hello\0world\0";

	if (strcmp(p, "hello\0wall") == 0) {
		p_equal_flag = 1;
	}

	printf("p : %s\n", p);
	printf("p_equal_flag : %d\n", p_equal_flag);

	free(p);
	return 0;
}

버그

  1. char *p malloc 진행한 뒤 p에 대입연산자 (=) 로 문자열을 입력.
    • memcpy(p, "hello\0world\0", 20); 써줘야 함
  2. strcmp로 문자열을 비교.
    • 널문자가 포함된 비교를 진행하기에 for문으로 하나하나 비교하거나 memcmp를 써야 함.
    • strncmp 또한 길이를 지정한다 하더라도 중간에 널 문자를 만나면 비교를 멈추기 때문에 사용하면 안됨.

널검사 안한 포인터 사용

1
2
3
4
5
6
7
8
#include <string.h>

int get_maxlen(char *str1, char *str2) {
	if (strlen(str1) > strlen(str2))
		return strlen(str1);
	else
		return strlen(str2);
}

버그

  1. 널검사 없이 strlen 함수를 사용함.
    • str1 또는 str2 변수가 null 이면 strlen 함수에서 segmentation fault 발생.
    • 포인터를 참조할 때에는 항상 널검사 진행.
    • 널검사를 하지않는 위험한 함수임을 주석 또는 doxygen을 통해 사전에 정의. - 외부에서 null 검사가 완료된 검증된 포인터만 사용할 수 있도록 함
  2. 버그는 아니지만 속도상의 문제
    • strlen 함수가 총 3번 동작함. 2번 동작하게끔 줄일 수 있음

free 후 널 초기화 미진행

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
#include <stdlib.h>

struct test_t {
	int a;
	int b;
};

void initialize_test(struct test_t *p) {
	if (p == NULL) return ;
	p->a = 0;
	p->b = 0;
}

void free_test(struct test_t *p) {
	if (p == NULL) return ;
	free(p);
}

void func() {
	struct test_t *p = (struct test *)malloc(sizeof(struct test));
	initialize_test(p);
	// code
	free_test(p);

	initialize_test(p);
}

버그

  1. free 후 NULL 초기화를 하지 않아 이후 initialize_test 함수의 if (p == NULL) 구문에서 비정상 포인터를 걸러내지 못함.
    • #define nullfree(p) if (p) { free(p); p = NULL; } 처럼 매크로를 free를 재정의해서 사용하면 좋음.

댕글링 포인터를 만드는 행위

링크 참조

변수, 구조체, 할당 시 초기화

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void func()
{
	// code
}

int main()
{
	func();
	const int ptr1_size = 32000;
	char *ptr1 = malloc(ptr1_size);
	memset(ptr1, 0, ptr1_size);
	memcpy(ptr1, "asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf", 60);
	printf("ptr1 addr : %x\n", ptr1);
	printf("ptr1 : %s\n", ptr1);
	free(ptr1);

	char *ptr2 = malloc(8192);
	memcpy(ptr2, "qwerqwer", 8);
	printf("ptr2 addr : %x\n", ptr2);
	printf("ptr2 : %s\n", ptr2);
	free(ptr2);

	return 0;
}

ptr1, ptr2 변수 동적할당 시 memset 함수 등으로 메모리 초기화를 진행하지 않음.

코드 컴파일 후 실행 결과

1
2
3
4
ptr1 addr : 81da1920
ptr1 : asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf
ptr2 addr : 81da1920
ptr2 : qwerqwerasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf

ptr2 변수 출력 시 ptr1 초기화하지 못한 ptr1의 뒷 문자가 출력됨.
만약 ptr1에 민감한 정보가 들어있다면 보안상의 문제가 발생할 수 있음.

따라서 알 수 있는 결과 : 변수 선언 시 꼭 초기화를 진행하자.

This post is licensed under CC BY 4.0 by the author.