flawopen.com/메모리 안전성/strcpy vs strncpy
strncpy()가 strcpy()보다 안전한가요?
고정 너비 필드는 칸이 나열된 상자와 같습니다. 이후 코드가 바이트를 C 문자열로 취급한다면 마지막에 종료 표식이 있어야 합니다. strncpy()는 최대 n바이트를 쓰며 실제 버퍼 크기보다 n이 크지 않을 때만 대상 범위를 넘지 않습니다. 원본이 제한을 채우면 널 종료 문자를 추가하지 않습니다. 이후 문자열 함수가 표식을 찾으려고 대상 범위 밖을 읽을 수 있습니다.
char dst[16]; /* 1. Unbounded — classic overflow */ strcpy(dst, src); /* 2. Bounded, but may not terminate. If strlen(src) >= 16, dst has no '\0' and every later strlen/printf reads out of bounds. */ strncpy(dst, src, sizeof(dst)); /* 3. Off-by-one: room for 16 chars plus a terminator = 17 bytes, but dst only holds 16. */ strncpy(dst, src, sizeof(dst)); dst[sizeof(dst)] = '\0'; /* OOB! */
char dst[16]; /* Preferred: snprintf always null-terminates (when size > 0), and tells you about truncation. */ int n = snprintf(dst, sizeof(dst), "%s", src); if (n < 0 || (size_t)n >= sizeof(dst)) { /* truncated — handle it */ return ERR_TOO_LONG; } /* Or strlcpy where available — always terminates, returns the length it tried to create. */ if (strlcpy(dst, src, sizeof(dst)) >= sizeof(dst)) { return ERR_TOO_LONG; }
strncpy의 원래 용도
이 동작은 결함이 아닙니다. strncpy()는 고정 너비의 널 패딩 필드용이지 일반적인 안전 C 문자열 복사용이 아닙니다. 원본 문자열이 n보다 짧으면 대상에 n - 1바이트까지 널 바이트를 채웁니다. 원본의 처음 n바이트에 널 바이트가 없으면 정확히 n바이트를 복사하고 대상을 종료하지 않습니다. 고정 너비 필드에는 맞지만 널 종료 문자열을 요구하는 API에 전달하면 위험합니다.
원본이 n보다 짧을 때 필요한 패딩은 필드 너비 전체를 씁니다. 큰 버퍼에 짧은 원본을 복사하면 종료 문자에서 멈추는 함수보다 더 많은 작업을 할 수 있습니다.
잘림을 항상 확인하세요
오버플로 방지는 절반에 불과합니다. 조용한 잘림도 보안 문제입니다. 잘린 호스트명, 경로 또는 권한 값은 의미가 달라질 수 있습니다. snprintf는 쓰려고 했던 길이를 반환하므로 버퍼 크기와 비교하세요. 이 값을 실제 기록 바이트 수로 오해한 점은 Citrix Bleed에서도 관련이 있었습니다.
자주 묻는 질문
널 종료와 경계 검사를 제공하지만 이식성은 고르지 않습니다. Windows에서는 지원이 좋지만 다른 환경에서는 구현이 일정하지 않습니다. 여러 플랫폼을 대상으로 하면 snprintf가 더 널리 제공됩니다.
아니요. OpenBSD에서 시작했고 BSD와 macOS에서 널리 사용할 수 있으며 최근 glibc에도 추가됐습니다. 대상 플랫폼 지원 여부를 확인하세요.
길이를 관리하는 std::string과 std::string_view를 우선 사용하세요. raw buffer가 필요한 API 경계에서만 C 함수를 사용하세요.