Program Club

strncpy가 null이 아닌 이유는 무엇입니까?

proclub 2020. 10. 29. 20:12
반응형

strncpy가 null이 아닌 이유는 무엇입니까?


strncpy()아마도 버퍼 오버플로로부터 보호합니다. 그러나 null로 끝나지 않고 오버플로를 방지하는 경우 후속 문자열 작업이 오버플로 될 가능성이 높습니다. 그래서 이것으로부터 보호하기 위해 나는 스스로를 발견했습니다.

strncpy( dest, src, LEN );
dest[LEN - 1] = '\0';

man strncpy 제공합니다 :

strncpy () 함수는 n 바이트 이하의 src가 복사된다는 점을 제외하면 비슷합니다. 따라서 src의 처음 n 바이트 중 null 바이트가 없으면 결과는 null로 종료되지 않습니다.

null 없이는 다음과 같이 결백 해 보이는 것을 종료합니다.

   printf( "FOO: %s\n", dest );

... 충돌 할 수 있습니다.


더 좋고 안전한 대안이 strncpy()있습니까?


strncpy더 안전한 것으로 사용하기위한 것이 아니라 strcpy다른 문자열의 중간에 하나의 문자열을 삽입하는 데 사용되어야합니다.

예컨대 모든 "안전한"문자열 처리 기능 snprintfvsnprintf버퍼 오버 플로우 공격 등 완화 이후의 기준으로 추가 된 수정되어

Wikipediastrncat자신의 금고를 작성하는 대안으로 언급 합니다 strncpy.

*dst = '\0'; strncat(dst, src, LEN);

편집하다

LEN 문자보다 길거나 같으면 문자열을 null로 종료 할 때 strncat이 LEN 문자를 초과한다는 사실을 놓쳤습니다.

어쨌든 memcpy (..., strlen (...))과 같은 자체 개발 솔루션 대신 strncat을 사용하는 요점은 strncat의 구현이 라이브러리에서 대상 / 플랫폼에 최적화 될 수 있다는 것입니다.

물론 dst에 적어도 nullchar가 있는지 확인해야하므로 strncat의 올바른 사용은 다음과 같습니다.

if(LEN) { *dst = '\0'; strncat(dst, src, LEN-1); }

또한 strncpy가 부분 문자열을 다른 문자열로 복사하는 데별로 유용하지 않다는 것을 인정합니다. src가 n 문자보다 짧으면 대상 문자열이 잘립니다.


안전한 복사를 수행하는 strlcpy 와 같은 오픈 소스 구현이 이미 있습니다 .

http://en.wikipedia.org/wiki/Strlcpy

참고 문헌에는 출처에 대한 링크가 있습니다.


원래 7th Edition UNIX 파일 시스템 (DIR (5) 참조)에는 파일 이름을 14 바이트로 제한하는 디렉토리 항목이있었습니다. 디렉토리의 각 항목은 inode 번호에 대해 2 바이트와 이름에 대해 14 바이트를 더한 것으로 구성되어 있으며, 14 자로 채워졌지만 반드시 null로 끝나는 것은 아닙니다. strncpy()이러한 디렉토리 구조와 함께 작동하도록 설계된 것이 제 믿음입니다. 또는 적어도 해당 구조에서 완벽하게 작동합니다.

중히 여기다:

  • 14 자 파일 이름이 널로 끝나지 않았습니다.
  • 이름이 14 바이트보다 짧은 경우 전체 길이 (14 바이트)로 널이 채워졌습니다.

이것은 정확히 다음과 같은 방법으로 달성됩니다.

strncpy(inode->d_name, filename, 14);

따라서 strncpy()원래 틈새 응용 프로그램에 이상적으로 적합했습니다. 우연히도 null로 끝나는 문자열의 오버플로를 방지하는 것이 었습니다.

(길이 14까지의 널 패딩은 심각한 오버 헤드가 아닙니다. 버퍼의 길이가 4KB이고 원하는 모든 것이 20자를 안전하게 복사하는 것이라면 추가 4075 널은 심각한 과잉이며 쉽게 할 수 있습니다. 긴 버퍼에 재질을 반복적으로 추가하는 경우 2 차 동작으로 이어집니다.)


일부 새로운 대안은 ISO / IEC TR 24731에 지정되어 있습니다 (자세한 내용은 https://buildsecurityin.us-cert.gov/daisy/bsi/articles/knowledge/coding/317-BSI.html 확인 ). 이러한 함수의 대부분은 대상 변수의 최대 길이를 지정하는 추가 매개 변수를 취하고, 모든 문자열이 널로 끝나는 지 확인하고, _s이전 "안전하지 않은"버전과 구별하기 위해 이름이 ( "안전한"?의 경우)로 끝나는 지 확인 합니다. . 1

안타깝게도 여전히 지원을 받고 있으며 특정 도구 세트에서 사용하지 못할 수 있습니다. 이전 버전의 안전하지 않은 함수를 사용하면 이후 버전의 Visual Studio에서 경고가 발생합니다.

도구 새 기능을 지원 하지 않는 경우 이전 기능에 대한 자체 래퍼를 만드는 것이 매우 쉽습니다. 예를 들면 다음과 같습니다.

errCode_t strncpy_safe(char *sDst, size_t lenDst,
                       const char *sSrc, size_t count)
{
    // No NULLs allowed.
    if (sDst == NULL  ||  sSrc == NULL)
        return ERR_INVALID_ARGUMENT;

   // Validate buffer space.
   if (count >= lenDst)
        return ERR_BUFFER_OVERFLOW;

   // Copy and always null-terminate
   memcpy(sDst, sSrc, count);
   *(sDst + count) = '\0';

   return OK;
}

예를 들어, 오버플로없이 항상 가능한 한 많은 문자열을 복사하기 위해 필요에 맞게 함수를 변경할 수 있습니다. 당신이 통과하면 사실, VC ++ 구현은이 작업을 수행 할 수 _TRUNCATE는 AS count.




1 물론, 대상 버퍼의 크기에 대해 여전히 정확해야합니다. 3 문자 버퍼를 제공하지만 strcpy_s()25 문자를위한 공간이 있다고 말하면 여전히 문제가 있습니다.


Strncpy is safer against stack overflow attacks by the user of your program, it doesn't protect you against errors you the programmer do, such as printing a non-null-terminated string, the way you've described.

You can avoid crashing from the problem you've described by limiting the number of chars printed by printf:

char my_string[10];
//other code here
printf("%.9s",my_string); //limit the number of chars to be printed to 9

Use strlcpy(), specified here: http://www.courtesan.com/todd/papers/strlcpy.html

If your libc doesn't have an implementation, then try this one:

size_t strlcpy(char* dst, const char* src, size_t bufsize)
{
  size_t srclen =strlen(src);
  size_t result =srclen; /* Result is always the length of the src string */
  if(bufsize>0)
  {
    if(srclen>=bufsize)
       srclen=bufsize-1;
    if(srclen>0)
       memcpy(dst,src,srclen);
    dst[srclen]='\0';
  }
  return result;
}

(Written by me in 2004 - dedicated to the public domain.)


strncpy works directly with the string buffers available, if you are working directly with your memory, you MUST now buffer sizes and you could set the '\0' manually.

I believe there is no better alternative in plain C, but its not really that bad if you are as careful as you should be when playing with raw memory.


Instead of strncpy(), you could use

snprintf(buffer, BUFFER_SIZE, "%s", src);

Here's a one-liner which copies at most size-1 non-null characters from src to dest and adds a null terminator:

static inline void cpystr(char *dest, const char *src, size_t size)
{ if(size) while((*dest++ = --size ? *src++ : 0)); }

I have always preferred:

 memset(dest, 0, LEN);
 strncpy(dest, src, LEN - 1);

to the fix it up afterwards approach, but that is really just a matter of preference.


These functions have evolved more than being designed, so there really is no "why". You just have to learn "how". Unfortunately the linux man pages at least are devoid of common use case examples for these functions, and I've noticed lots of misuse in code I've reviewed. I've made some notes here: http://www.pixelbeat.org/programming/gcc/string_buffers.html


Without relying on newer extensions, I have done something like this in the past:

/* copy N "visible" chars, adding a null in the position just beyond them */
#define MSTRNCPY( dst, src, len) ( strncpy( (dst), (src), (len)), (dst)[ (len) ] = '\0')

and perhaps even:

/* pull up to size - 1 "visible" characters into a fixed size buffer of known size */
#define MFBCPY( dst, src) MSTRNCPY( (dst), (src), sizeof( dst) - 1)

Why the macros instead of newer "built-in" (?) functions? Because there used to be quite a few different unices, as well as other non-unix (non-windows) environments that I had to port to back when I was doing C on a daily basis.

참고URL : https://stackoverflow.com/questions/1453876/why-does-strncpy-not-null-terminate

반응형