【发布时间】:2015-12-07 07:55:04
【问题描述】:
Visual Studio 2013 C++ 项目有一个/GS 开关,用于在运行时启用缓冲区安全检查验证。自升级到 VS 2013 以来,我们遇到了更多的 STATUS_STACK_BUFFER_OVERRUN 错误,并且怀疑这与改进了新编译器中的缓冲区溢出检查有关。我一直在尝试验证这一点并更好地了解如何检测缓冲区溢出。即使由语句更新的内存仅更改同一范围内堆栈上另一个局部变量的内容,也会报告缓冲区溢出,这让我感到困惑!因此,它不仅必须检查更改不会破坏不属于局部变量“拥有”的内存,而且该更改不会影响除分配给单个更新语句引用的局部变量之外的任何局部变量。这是如何运作的?自 VS 2010 以来它是否发生了变化?
编辑: 这是一个示例,说明了 Mysticial 的解释未涵盖的情况:
void TestFunc1();
int _tmain(int argc, _TCHAR* argv[])
{
TestFunc1();
return 0;
}
void TestFunc1()
{
char buffer1[4] = ("123");
char buffer2[4] = ("456");
int diff = buffer1 - buffer2;
printf("%d\n", diff);
getchar();
buffer2[4] = '\0';
}
输出为4,表示即将被覆盖的内存在buffer1 的范围内(紧跟在buffer2 之后),但随后程序因缓冲区溢出而终止。从技术上讲,它应该被视为缓冲区溢出,但我不知道它是如何被检测到的,因为它仍在局部变量的存储中,并没有真正破坏局部变量之外的任何东西。
这张带有内存布局的截图证明了这一点。单步执行后,程序因缓冲区溢出错误而中止。
我刚刚在 VS 2010 中尝试了相同的代码,虽然调试模式捕获了缓冲区溢出(缓冲区偏移量为 12),但在释放模式下它确实没有捕获它(缓冲区偏移量8).所以我认为 VS 2013 收紧了/GS 开关的行为。
编辑 2: 我设法用这段代码偷偷通过了 VS 2013 范围检查。它仍然没有检测到更新一个局部变量的尝试实际上更新了另一个:
void TestFunc()
{
char buffer1[4] = "123";
char buffer2[4] = "456";
int diff;
if (buffer1 < buffer2)
{
puts("Sequence 1,2");
diff = buffer2 - buffer1;
}
else
{
puts("Sequence 2,1");
diff = buffer1 - buffer2;
}
printf("Offset: %d\n", diff);
switch (getchar())
{
case '1':
puts("Updating buffer 1");
buffer1[diff] = '!';
break;
case '2':
puts("Updating buffer 2");
buffer2[diff] = '!';
break;
}
getchar(); // Eat enter keypress
printf("%s,%s\n", buffer1, buffer2);
}
【问题讨论】:
-
我相信它所做的一件事是在堆栈对象附近插入虚拟数据,并在方便时检查它们(例如退出函数)。如果数据发生了变化,那么它就知道有什么东西损坏了它。不过这只是我的猜测。
-
@Mysticial 这无法解释我的测试用例,其中我计算了两个局部变量相邻的缓冲区之间的偏移量,但仍然检测到更新第一个变量的溢出。
-
显示一个例子来说明你的意思,包括变量地址的输出。我很确定 Mystical 是对的。
-
@MatsPetersson 我已经添加了证明必须有更多事情发生的证据。
-
在 VS2013、Win32、Debug 中,我看到偏移量为 12,间隙用
0xcc填充。
标签: c++ visual-c++ memory buffer-overflow