strncpy并不总是空终止

时间:2013-06-13 20:12:53

标签: c strncpy

我正在使用以下代码:

char filename[ 255 ];
strncpy( filename, getenv( "HOME" ), 235 );
strncat( filename, "/.config/stationlist.xml", 255 );

收到此消息:

(warning) Dangerous usage of strncat - 3rd parameter is the maximum number of characters to append.
(error) Dangerous usage of 'filename' (strncpy doesn't always null-terminate it).

6 个答案:

答案 0 :(得分:6)

我通常会避免使用str*cpy()str*cat()。您必须应对边界条件,神秘的API定义以及意外的性能后果。

您可以改用snprintf()。您只需要与目标缓冲区的大小竞争。并且,它更安全,因为它不会溢出,并且将永远为你终止NUL。

char filename[255];
const char *home = getenv("HOME");
if (home == 0) home = ".";
int r = snprintf(filename, sizeof(filename), "%s%s", home, "/.config/stationlist.xml");
if (r >= sizeof(filename)) {
    /* need a bigger filename buffer... */
} else if (r < 0) {
    /* handle error... */
}

答案 1 :(得分:4)

您的filename电话可能会导致strncat超时。

使用:

strncat(filename, "/.config/stationlist.xml",
        sizeof filename - strlen(filename) - 1);

还要确保在strncpy调用后终止缓冲区:

strncpy( filename, getenv( "HOME" ), 235 );
filename[235] = '\0';

如果源的长度大于或等于要复制的最大字符数,则strncpy不会终止其目标缓冲区。

答案 2 :(得分:3)

man strncpy有这样的说法:

Warning: If there is no null byte among the first n bytes
of src, the string placed in dest will not be null terminated.

如果它在耗尽最大长度之前遇到源中的0字节,它将被复制。但是,如果在源中的第一个0之前达到最大长度,则不会终止目标。最好在strncpy()返回后确保自己是...

答案 3 :(得分:2)

strncpy()和(更多)strncat()都有非显而易见的行为,你最好不使用它们。

函数strncpy()

如果您的目标字符串为了参数,长度为255个字节,strncpy()将始终写入所有255个字节。如果源字符串短于255个字节,则将填充余数。如果源字符串长度超过255个字节,它将在255个字节后停止复制,使目标没有空终止符。

strncat函数()

大多数'大小'函数的大小参数(strncpy()memcpy()memmove()等)是目标字符串(内存)中的字节数。对于strncat(),大小是已经在目标中的字符串结尾之后剩余的空间量。因此,当您知道目标缓冲区的大小(strncat())和目标字符串当前的长度(S)时,您只能安全地使用Lstrncat()的安全参数是S-L(我们会担心其他时间是否有一个异地)。但是,如果您知道L,则strncat()跳过L字符是没有意义的;您可以通过target+L作为开始的地方,并简单地复制数据。您可以使用memmove()memcpy(),也可以使用strcpy(),甚至strncpy()。如果您不知道源字符串的长度,您必须确信截断它是有意义的。

分析有问题的代码

char filename[255];
strncpy(filename, getenv("HOME"), 235);
strncat(filename, "/.config/stationlist.xml", 255);

除非大小太小(或者你在未设置$HOME的情况下运行程序),否则第一行是无法使用的,但这超出了这个问题的范围。对strncpy()的调用不会使用sizeof(filename)作为大小,而是使用任意小的数字。它不是世界末日,但通常不能保证变量的最后20个字节是零字节(或者甚至是任何一个都是零字节)。在某些情况下(filename是一个全局变量,以前未使用过),可能会保证零。

strncat()调用尝试在filename中的字符串末尾添加24个字符,这些字符可能已经是232-234字节长,或者可能任意长于235字节。无论哪种方式,这都是有保证的缓冲区溢出。 strncat()的使用也直接落入关于其大小的陷阱。您已经说过,在filename已经存在的内容之后最多可以添加255个字符,这显然是错误的(除非来自getenv("HOME")的字符串恰好为空)。

更安全的代码:

char filename[255];
static const char config_file[] = "/.config/stationlist.xml";
const char *home = getenv("HOME");
size_t len = strlen(home);
if (len > sizeof(filename) - sizeof(config_file))
    ...error file name will be too long...
else
{
    memmove(filename, home, len);
    memmove(filename+len, config_file, sizeof(config_file));
}

会有人坚持认为'memcpy()是安全的,因为字符串不能重叠',并且在一个级别上他们是正确的,但重叠应该是非问题的,并且memmove(),这不是问题。所以,我一直使用memmove() ......但是我还没有完成时序测量,看它有多大问题,如果它根本就是问题。也许其他人已经完成了测量。

摘要

  1. 请勿使用strncat()
  2. 谨慎使用strncpy()(注意它在非常大的缓冲区上的行为!)。
  3. 计划使用memmove()memcpy()代替;如果你能安全地复制,你就知道了使这个变得合理的大小。

答案 4 :(得分:1)

1)你的strncpy不一定是null-terminate filename。实际上,如果getenv(“HOME”)长于235个字符且getenv(“HOME”)[234]不是0,则不会。 2)你的strncat()可能会尝试将文件扩展名超过255个字符,因为正如它所说的那样,

3rd parameter is the maximum number of characters to append. 

(不是dst允许的总长度)

答案 5 :(得分:0)

strncpy(Copied_to,Copied_from,sizeof_input) 在字符数组之后输出垃圾值(不用于字符串类型)。要使用遍历字符数组的 for 循环而不是简单地使用 cout<<var;

来解决它的输出
for(i=0;i<size;i++){cout<<var[i]} 

我找不到使用 minGW 编译器在 Windows 系统上进行遍历的解决方法。 空终止并不能解决问题。在线编译器工作正常。

相关问题