【发布时间】:2013-08-30 03:36:57
【问题描述】:
我发现 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 位无复制)。 用于重现崩溃的操作系统: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
Update 5 该问题将在下一个 VS 版本中修复。没有计划对旧版本进行修复。请参阅上面的“Microsoft Connect”链接。
【问题讨论】:
-
你有什么证据证明这个说法?
-
因此,如果
src指向一个非 NUL 终止的指针数组,其大小是 8 的倍数而不是 0,则读取数组之外的 dword,尽管读取的值未使用.mov (%rdx),%rax; sub $0x8,%r8; jb 0x400011a7← 我可以想出几种方法来改变这一点,但会牺牲性能。 -
在
jb 0x400011a7之后下沉mov (%rdx),%rax也可以,但是下面的一些指令依赖于%rax,这可能会导致性能下降。总之,我同意这是一个错误。 -
微软不是在 2008 年或 2009 年就已经解决了这个问题吗?请参阅support.microsoft.com/kb/956420 和connect.microsoft.com/VisualStudio/Downloads/…。那么为什么 VS 2012 中仍然存在这个 bug?
-
@JosephQuinsey:请注意,该修复是针对 strncpy,而不是 strncat。这个问题是关于 strncat 的。