问题就在这里:
strncpy(buffer,str,strlen(str));
^^^^^^^^^^^
如果字符串的长度大于目标缓冲区的长度,strncpy仍会将其复制到缓冲区中。你是以字符串的字符数作为要复制的数量,而不是缓冲区的大小。正确的做法如下:
strncpy(buffer,str, sizeof(buff) - 1);
buffer[sizeof(buff) - 1] = '\0';
这样做的作用是限制复制到缓冲区实际大小减去一个空终止字符的数据量。然后,我们将缓冲区中的最后一个字节设置为空字符,作为额外的保障措施。这样做的原因是,如果strlen(str) < len - 1,则strncpy将复制n个字节,包括终止空字符。否则,空字符不被复制,您将面临崩溃场景,因为现在您的缓冲区具有未终止的字符串。
希望这可以帮助您。
编辑:经过进一步检查和他人的建议,该函数的可能编码如下:
int func (char *str)
{
char buffer[100];
unsigned short size = sizeof(buffer);
unsigned short len = strlen(str);
if (len > size - 1) return(-1);
memcpy(buffer, str, len + 1);
buffer[size - 1] = '\0';
return(0);
}
由于我们已经知道字符串的长度,因此可以使用memcpy将字符串从str引用的位置复制到缓冲区中。请注意,根据FreeBSD 9.3系统上strlen(3)的手册页面,以下内容已说明:
The strlen() function returns the number of characters that precede the
terminating NUL character. The strnlen() function returns either the
same result as strlen() or maxlen, whichever is smaller.
我理解为字符串的长度不包括空字符,所以我复制 len+1 个字节来包含空字符,测试将检查长度是否小于缓冲区大小减2。减一是因为缓冲区从位置0开始,再减一个确保有空间放置空字符。
编辑:结果发现,某些东西的大小从1开始,而访问则从0开始,所以之前的-2是错误的,因为它会返回任何大于98个字节的错误,但实际上应该是>99个字节。
编辑:尽管无符号短整型的答案通常是正确的,因为可以表示的最大长度为65,535个字符,但这并不重要,因为如果字符串超过该长度,值将会回绕。就像取75,231(即0x000125DF)并屏蔽掉前16位,得到9695(0x000025DF)。我唯一看到的问题是超过65,535个字符后的前100个字符,长度检查将允许复制,但在所有情况下只会复制字符串的前100个字符并对字符串进行空终止处理。因此,即使出现回绕问题,缓冲区仍然不会溢出。
这本身可能或可能不构成安全风险,这取决于字符串的内容以及您用它做什么。如果只是纯文本,那么通常没有问题。您只需获得一个被截断的字符串。但是,如果它是类似于URL或甚至是SQL命令序列之类的东西,您可能会遇到问题。
strncpy
后继续使用该字符串时,不对字符串进行零结尾会成为一个问题。但在此情况下,没有这种情况发生。 - R Sahustrlen
被计算后用于验证检查,然后荒谬地再次计算--这是DRY原则的失败。如果第二个strlen(str)
被替换成len
,那么无论len
的类型是什么,都不会有缓冲区溢出的可能性。这些答案没有解决这一点,只是设法回避它。 - Jim Balter