为什么要使用strncpy而不是strcpy?

时间:2009-08-11 05:17:58

标签: c buffer-overflow strcpy c89 strncpy

编辑:我添加了示例的来源。

我遇到了this example

char source[MAX] = "123456789";
char source1[MAX] = "123456789";
char destination[MAX] = "abcdefg";
char destination1[MAX] = "abcdefg";
char *return_string;
int index = 5;

/* This is how strcpy works */
printf("destination is originally = '%s'\n", destination);
return_string = strcpy(destination, source);
printf("after strcpy, dest becomes '%s'\n\n", destination);

/* This is how strncpy works */
printf( "destination1 is originally = '%s'\n", destination1 );
return_string = strncpy( destination1, source1, index );
printf( "After strncpy, destination1 becomes '%s'\n", destination1 );

产生了这个输出:

destination is originally = 'abcdefg'
After strcpy, destination becomes '123456789'

destination1 is originally = 'abcdefg'
After strncpy, destination1 becomes '12345fg'

这让我想知道为什么有人会想要这种效果。看起来好像很混乱。这个程序让我觉得你基本上可以用Tom Bro763复制一个人的名字(例如Tom Brokaw)。

使用 strncpy() 优于 strcpy()有什么好处?

10 个答案:

答案 0 :(得分:157)

strncpy()函数的设计考虑了一个非常特殊的问题:操作以原始UNIX目录条目的方式存储的字符串。这些使用了固定大小的数组,只有当文件名比数组短时才使用nul-terminator。

这就是strncpy()的两个奇怪背后的原因:

  • 如果完全填满,它不会在目的地上放置一个nul-terminator;和
  • 如果有必要,它总是完全填满目的地。

对于“更安全的strcpy()”,您最好使用strncat(),如下所示:

if (dest_size > 0)
{
    dest[0] = '\0';
    strncat(dest, source, dest_size - 1);
}

总是会终止结果,并且不会复制超过必要的内容。

答案 1 :(得分:87)

strncpy要求你在其中加一个长度来对抗缓冲区溢出。 strcpy取决于尾随\0,但可能并非总是如此。

其次,为什么你选择只复制7个字符串上的5个字符超出了我,但它产生了预期的行为。它只复制第一个n个字符,其中n是第三个参数。

n函数都用作防止缓冲区溢出的防御编码。请使用它们代替较旧的功能,例如strcpy

答案 2 :(得分:30)

虽然我知道strncpy背后的意图,但它并不是一个很好的功能。避免两者。 Raymond Chen explains

  

就个人而言,我的结论只是为了避免strncpy及其所有朋友,如果你正在处理以null结尾的字符串。尽管名称中包含“str”,但这些函数不会生成以null结尾的字符串。它们将以null结尾的字符串转换为原始字符缓冲区。使用它们作为第二个缓冲区的空终止字符串是明显错误的。如果源太长,您不仅无法获得正确的空终止,而且如果源很短,则会得到不必要的空填充。

另见Why is strncpy insecure?

答案 3 :(得分:27)

strncpy并不比strcpy更安全,它只是将一种类型的错误与另一种错误进行交易。在C中,当处理C字符串时,您需要知道缓冲区的大小,没有办法解决它。 strncpy对于其他人提到的目录事物是合理的,但除此之外,你永远不应该使用它:

  • 如果您知道字符串和缓冲区的长度,为什么要使用strncpy?这充其量是浪费计算能力(添加无用的0)
  • 如果你不知道长度,那么你冒险默默地截断你的字符串,这不比缓冲区溢出好多了

答案 4 :(得分:21)

您正在寻找的是函数strlcpy(),它始终使用0终止字符串并初始化缓冲区。它还能够检测溢出。唯一的问题是,它不是(真的)可移植的,只存在于某些系统(BSD,Solaris)上。这个功能的问题在于,它可以打开另一种蠕虫,正如讨论所见 http://en.wikipedia.org/wiki/Strlcpy

我个人认为它比strncpy()strcpy()更有用。它具有更好的性能,是snprintf()的良好伴侣。对于没有它的平台,它实现起来相对容易。 (对于应用程序的开发阶段,我将这两个函数(snprintf()strlcpy())替换为一个陷阱版本,该程序在缓冲区溢出或截断时粗暴地中止程序。这样可以快速捕获最严重的违规者。特别是如果您从其他人处理代码库。

编辑:strlcpy()可以轻松实现:

size_t strlcpy(char *dst, const char *src, size_t dstsize)
{
  size_t len = strlen(src);
  if(dstsize) {
    size_t bl = (len < dstsize-1 ? len : dstsize-1);
    ((char*)memcpy(dst, src, bl))[bl] = 0;
  }
  return len;
}

答案 5 :(得分:3)

strncpy()函数更安全:您必须传递目标缓冲区可以接受的最大长度。否则可能会发生源字符串未正确终止0,在这种情况下strcpy()函数可能会向目标写入更多字符,从而破坏目标缓冲区之后的内存中的任何内容。这是许多漏洞利用中的缓冲区溢出问题

对于像read()这样的POSIX API函数,它不会将终止0放在缓冲区中,但会返回读取的字节数,您可以手动输入0,也可以使用strncpy()复制它

在您的示例代码中,index实际上不是索引,而是count - 它表示从源到目标复制多少个字符 。如果源的前n个字节中没有空字节,则放置在目标中的字符串将不会以空终止

答案 6 :(得分:1)

strncpy使用'\ 0'填充目的地以获取源的大小,尽管目标的大小较小....

手册页:

  

如果src的长度小于n,则strncpy()将填充其余部分   dest为空字节。

     

并且不仅仅是其余的...也在此之后直到n个字符为止   到达。因此你得到溢出......(参见手册页   实现)

答案 7 :(得分:0)

这可以在许多其他场景中使用,您只需要将原始字符串的一部分复制到目标。使用strncpy()可以复制原始字符串的有限部分,而不是strcpy()。我看到您提供的代码来自publib.boulder.ibm.com

答案 8 :(得分:0)

这取决于我们的要求。 对于Windows用户

每当我们不想复制整个字符串或者我们只想复制n个字符时,我们使用strncpy。但是strcpy会复制整个字符串,包括终止空字符。

这些链接可以帮助您更多地了解strcpy和strncpy 以及我们可以使用的地方。

about strcpy

about strncpy

答案 9 :(得分:-8)

strncpy是一个更安全的strcpy版本,事实上你永远不应该使用strcpy,因为它潜在的缓冲区溢出漏洞会让你的系统容易受到各种攻击