我观察到Microsoft strncat
实现的一个有趣问题。它接触源缓冲区之外的1个字节。请考虑以下代码:
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <string.h>
void main()
{
char dstBuf[1024];
char* src = malloc(112);
memset(src, 'a', 112);
dstBuf[0] = 0;
strncat(dstBuf, src, 112);
}
strncat
在112字节块后读取1个字节。因此,如果您不幸在无效页面边界上进行分配,则应用程序崩溃。大型应用程序可能会在这些地方间歇性地崩溃。 (请注意,可以使用 gflags PageHeap 设置模拟此类条件;块大小必须能够被指针大小整除才能正确对齐。)
这是预期的行为还是错误?任何确认的链接? (我阅读了strncat
的几个描述,但它们可以根据你最初的思路解释两种方式......)
更新(回答有关证据的问题):
如果从上面的文字中不清楚,我道歉,但这是一个实验性的事实。我在strncat
读取地址src + srcBufSize的应用程序中观察到间歇性崩溃。在这个小例子中, gflags PageHeap 在崩溃时一直运行(100%)。所以据我所知,证据非常可靠。
Update2 (有关编译器的信息) MS Visual Studio 2005版本8.0.50727.867。 构建平台:64位版本(32位无repro)。 用于重现崩溃的操作系统:Windows Server 2008 R2。
更新3 使用MS Visual Studio 2012 11.0.50727.1中内置的二进制文件也可以重现此问题
更新4 Link to issue on Microsoft Connect; link to discussion on MSDN Forums
更新5 问题将在下一个VS版本中修复。旧版本没有计划修复。请参阅上面的“Microsoft Connect”链接。
答案 0 :(得分:3)
src - 指向要从
复制的以null结尾的字节字符串的指针
因此,实现可以假设src
输入参数实际上是NUL终止的,即使它长于count
个字符。
如需进一步确认,Microsoft's own documentation说明:
strSource
以空值终止的源字符串。
另一方面,actual C standard表示如下:
strncat
函数附加的n
字符不超过{null}字符 从s2
指向的数组到结尾的跟随它的字符不会被附加s1
指向的字符串。
正如下面的评论中所指出的,这将第二个参数s2
标识为数组,而不是以NUL结尾的字符串。但是,对于原始问题,这仍然是模棱两可的,因为此文档描述了对s1
的最终影响,而不是从s2
阅读时函数的行为。
这当然可以通过查阅C运行时库源代码来解决特定的 Microsoft实现。
答案 1 :(得分:2)
s2
<{1}}中的“字符串”。
因此,如果Microsoft正在读取strncat(s1, s2, n)
个字节,那么它不符合C11。
C11 7.24.2.3.1 n
提及
“将s2指向的字符串的副本(包括终止空字符)附加到s1”所指向的字符串的末尾。
C11 7.24.2.3.2 strcat()
说
“strncat函数从s2指向的数组到s1指向的字符串的末尾附加不超过n个字符(空字符及其后面的字符不会被追加)。 。终止空字符始终附加到结果“
显然,在strncat
案例中,strncat
被视为一个“数组”,对s2
附加了多少字符串有限制。因此,在连接期间,不需要检查s1
超过绝对需要的内容。最终写的s2
来自代码,而不是\0
。
不了解较旧的C99标准。
答案 2 :(得分:1)
英语是一种不完美的语言,比C语言还要多。
文档说“在大多数 n个字符”(我的重点)。没有证据表明strncat复制超过112个字符。是什么让你相信它?
strncat的代码可能索引超过112的偏移量,但实际上不是参考偏移量113,这可能导致存储故障。该ptr行为在K&amp; R中被定义为可接受的。
最后,这又是一个英语/推理问题,文档可能会说空终止字符串。但实际上,说字符串是否为空终止不是多余的吗?它们是定义的,否则它们将是一个字符数组。因此,文档含糊不清且不具体。程序员可以在两行之间进行读取。软件文档不是合法的文档,它们是本领域技术人员理解的描述。