openlang

getline이 쓰레기 값을 넣고, 메모리 누수도 있어요

거의 막혔어요. 어떤 버그부터 봐야 할까요?

while (getline(&line, &linecap, fp) != -1) {
	char *name, *role, *port;

	lineno++;
	line[strcspn(line, "\r\n")] = '\0';
	if (line[0] == '#' || line[0] == '\0')
		continue;
	name = strtok(line, " \t");
	role = strtok(NULL, " \t");
	port = strtok(NULL, " \t");
	if (!name || !role) {
		fprintf(stderr, "%s:%d: expected 'name role [port]'\n",
			path, lineno);
		fclose(fp);
		return -1;
	}
	if (inv->count == inv->cap && grow(inv) != 0) {
		fclose(fp);
		return -1;
	}
	inv->hosts[inv->count].name = name;
	inv->hosts[inv->count].role = role;
	inv->hosts[inv->count].port = port ? atoi(port) : 22;
	inv->count++;
}
free(line);
fclose(fp);
return 0;

hosts.txt:

# prod inventory
web-01 web 8080
web-02 web 8080
db-01 db 5432
cache-01 cache

hosts_bad.txt:

web-01 web 8080
db-01
cache-01 cache 6379
영어에서 번역원문 보기

이어지는 대화 6

서로의 근거를 따라
  1. 고유 링크

    쓰레기 값이 먼저고, 누수는 그다음이에요.

    name, role, port는 line 안쪽을 가리키는 포인터예요. strtok은 그 버퍼에 NUL만 끼워 넣고 버퍼 안의 주소를 돌려줄 뿐이에요. 그런데 getline이 다음 행을 읽을 때 같은 버퍼를 다시 써요. 그래서 모든 호스트가 같은 메모리를 가리키게 되고, 마지막 줄이 남긴 내용만 보이는 거예요. line이 realloc되면 더 심해요. 예전 포인터가 댕글링 포인터가 되니까요.

    행마다 자기만의 복사본이 필요해요:

    inv->hosts[inv->count].name = strdup(name);
    inv->hosts[inv->count].role = strdup(role);

    NULL 체크도 하고, inventory를 해제할 때 둘 다 free하세요. 이게 수명 경계예요. line은 반복 한 번 동안만 살고, 호스트 문자열은 inv가 살아 있는 동안 살아요.

    누수는 이래요. 일찍 빠져나가는 return -1 경로(잘못된 행, grow 실패)에서 free(line)을 건너뛰어요. 정상 경로에서만 free하고 있어요. return 직전마다 free하거나, goto done으로 한 군데에서 정리하세요.

    getline이 버퍼를 할당하니까 호출한 쪽이 소유자예요. POSIX 문서에 그렇게 나와 있어요: https://pubs.opengroup.org/onlinepubs/9699919799/functions/getline.html

    영어에서 번역원문 보기

같은 질문의 다른 갈래

  1. 댕글링 포인터 경고는 사라졌고 포인터는 남았다

    래퍼를 통해 C++ 라이브러리에 이름을 넘기는데, 처음엔 한 줄로 CString::new(name).unwrap().as_ptr()라고 썼어요. rustc가 바로 임시 CString이 문장 끝에서 사라지니 포인터가 댕글링이…

    답글 2최근 활동 rustcpp
  2. 같은 sink 호출인데 한 번은 복사, 한 번은 이동

    Nim 2는 복사냐 이동이냐를 호출 지점마다 정한다. proc 단위로 정하지 않는다. 같은 인자, 같은 proc. 첫 호출은 a가 아직 쓰이니 복사한다. 둘째 호출은 이동한다. 호출 지점에는 어느 쪽인지 아무 표시가 없다.…

    답글 2최근 활동 nim
  3. 같은 인스턴스를 가리키는 인터페이스 참조와 객체 참조

    안녕하세요, 인터페이스 뒤에 CSV sink가 있는데, 마지막에 Flush와 Rows를 쓰고 싶어서 sink를 클래스 타입 변수로 만들었어요: Announce가 있으면 배너는 출력되는데 CSV는 비어 있고, error 216…

    답글 1최근 활동 object-pascal