4

我正在使用下面的代码:

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).
4

6 回答 6

7

我通常避免使用str*cpy()and 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... */
}
于 2013-06-13T20:21:32.937 回答
5

filename你的电话可能会溢出strncat

采用:

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

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

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

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

于 2013-06-13T20:14:31.013 回答
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()最好在退货后确定是你自己...

于 2013-06-13T20:19:15.010 回答
2

两者strncpy()和(甚至更多)strncat()都有不明显的行为,你最好不要使用任何一个。

strncpy()

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

strncat()

大多数“大小”函数(、、、等)的大小参数strncpy()memcpy()目标memmove()字符串(内存)中的字节数。使用strncat(),大小是在目标中已经存在的字符串末尾之后剩余的空间量。strncat()因此,只有知道目标缓冲区有多大 ( S) 以及目标字符串当前有多长( ) 时,才能安全使用L。的安全参数strncat()S-L(我们会担心其他时间是否有一个off-by-one)。但是鉴于您知道,跳过字符L是没有意义的;您可以作为开始的地方通过,然后简单地复制数据。你可以使用or ,或者你可以使用strncat()Ltarget+Lmemmove()memcpy()strcpy(),甚至strncpy()。如果您不知道源字符串的长度,您必须确信截断它是有意义的。

分析有问题的代码

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

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

strncat()调用尝试将 24 个字符附加到filename可能已经 232-234 字节长的字符串末尾,或者可能任意长于 235 字节。无论哪种方式,这都是保证缓冲区溢出。的使用strncat()也直接落入了关于它的大小的陷阱。您说过可以在已经存在的内容的末尾添加最多 255 个字符filename,这是明显错误的(除非字符串 fromgetenv("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()代替;如果您可以安全地进行复制,那么您就知道使其合理所需的尺寸。
于 2013-06-13T20:29:14.817 回答
1

1)您的 strncpy 不一定以空终止文件名。事实上,如果 getenv("HOME") 超过 235 个字符并且 getenv("HOME")[234] 不是 0,它不会。2)您的 strncat() 可能会尝试将文件名扩展超过 255 个字符,因为正如它所说,

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

(不是 dst 的总允许长度)

于 2013-06-13T20:20:31.267 回答
0

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

for(i=0;i<size;i++){cout<<var[i]} 

我找不到使用 minGW 编译器在 Windows 系统上遍历的工作。空终止并不能解决问题。在线编译器工作得很好。

于 2021-05-15T16:55:20.103 回答