Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/64.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 缓冲区溢出的后果?_C++_C_Printf_Buffer Overflow - Fatal编程技术网

C++ 缓冲区溢出的后果?

C++ 缓冲区溢出的后果?,c++,c,printf,buffer-overflow,C++,C,Printf,Buffer Overflow,所以在这里,我认为我在查看其他人的代码时发现了一个小的缓冲区溢出问题。我立刻意识到这是不正确的,而且有潜在的危险,但我承认我无法解释这一“错误”的实际后果(如果有的话) 我编写了一个测试应用程序来演示错误,但(令我沮丧的是)发现,不管溢出情况如何,它似乎都能正确运行。我想相信这只是偶然的,但我想得到一些反馈,以确定我的想法是否错误,或者这里是否真的有一个问题,只是没有在我的测试应用程序中显示它的头部 问题代码(无论如何,我认为是): 现在,这一点在我看来很突出,我想将其标记为可能的缓冲区溢出,这

所以在这里,我认为我在查看其他人的代码时发现了一个小的缓冲区溢出问题。我立刻意识到这是不正确的,而且有潜在的危险,但我承认我无法解释这一“错误”的实际后果(如果有的话)

我编写了一个测试应用程序来演示错误,但(令我沮丧的是)发现,不管溢出情况如何,它似乎都能正确运行。我想相信这只是偶然的,但我想得到一些反馈,以确定我的想法是否错误,或者这里是否真的有一个问题,只是没有在我的测试应用程序中显示它的头部

问题代码(无论如何,我认为是):

现在,这一点在我看来很突出,我想将其标记为可能的缓冲区溢出,这是因为第一个
strlen
。由于指针运算,
+1
的“不正确”位置将导致
strlen
返回
26
,而不是
27
(取“他的字符串长度为27个字符”)<我认为,代码>sprintf,然后将27个字符打印到缓冲区,导致缓冲区溢出

这是正确的评估吗

我编写了一个测试应用程序来向我正在查看代码的人演示这一点,发现即使在调试器中,字符串也能正确打印。我还尝试在这段代码之前和之后在堆栈和堆上放置其他变量,以查看是否可以影响内存的相邻区域,但仍然收到正确的输出。我意识到我新分配的堆内存可能不是相邻的,这可以解释为什么没有有用的溢出,但我只是想确认一下其他人的观点,这是否真的是一个问题

因为这是一个非常简单的“问题”,如果你能提供一些参考来支持你的答案,那就太好了。虽然我重视并欢迎您的意见,但我不会接受“是的,是的”作为最终答案。提前谢谢你





更新:许多好的答案都有很多额外的见解。不幸的是,我不能全部接受。感谢您分享您的知识,并成为我的“第二意见”。非常感谢您的帮助。

您的评估是正确的,只是springf将在缓冲区中放入28个字符,并在末尾计算字符串NUL的结尾(这就是为什么您首先需要放错位置的“+1”)


请注意,根据我的经验,如果在调试器外部出现故障,但在调试器中单步执行,则在100%的时间内,您已经溢出了本地缓冲区。调试器将更多的内容推到堆栈上,因此重要内容被覆盖的可能性较小。

您的评估是正确的。 [编辑]添加了James Curran提到的更正。[/编辑]

很可能,您的测试应用程序没有显示问题,因为分配被四舍五入到4、8或16的下一个倍数(这是常见的分配粒度)

这意味着您应该能够使用31个字符长的字符串进行演示


或者,使用“检测”本机内存探查器,该探查器可以将保护字节紧密地放置在此类分配周围。

是的,您是正确的。分配的缓冲区太小,无法容纳字符串


由于这是在堆上分配的,因此可能会导致堆损坏。然而,这种可能性取决于在此之前发生的其他内存分配和释放,也取决于正在使用的堆管理器。有关详细信息,请参阅。

问题在于您正在内存中的某个位置写入,而不是在堆栈上。 因此,很难看出到底出了什么问题。 如果要查看损坏情况,请尝试在堆栈上分配字符串

char buffer[strlen("This string is 27 char long" + 1)];
然后写过去。 将编写其他变量,如果您真的知道二进制如何工作,还可以添加一些要执行的代码


要利用这样的缓冲区溢出,您需要写入所需的数据,然后找到一种方法“跳转”到此要执行的数据。

您是正确的,本例中的指针算法将产生不正确的(较短的)传递给new的长度。您无法造成此崩溃的最可能原因是,内存分配实际提供了多少缓冲区空间存在一些不确定性

允许库提供大于请求的缓冲区。此外,缓冲区后面的任何内容都可能以分配头作为前缀,分配头受机器字对齐规则的约束。这意味着在下一个分配头之前可能有多达三个填充字节(取决于平台)


即使您重写了下一个分配头(用于管理分配的内存块),它也不会显示为问题,直到下一个块的所有者尝试将其返回堆。

许多历史
malloc
实现将簿记数据放在分配的块之前和/或之后。您可能正在覆盖这些数据,在这种情况下,您将不会看到任何错误/崩溃,除非您尝试释放内存(或者可能释放下一个块)。同样,后续分配的簿记信息也可能稍后覆盖您的字符串


我怀疑现代的
malloc
实现会通过使用完整性检查数据填充分配来防止堆损坏,因此如果幸运的话,不会发生什么坏事,或者在以后的分配/释放操作中可能会收到警告消息。

如其他人所述,你完全正确地认为这是不好的,你没有看到这一点的原因是填充。尝试一下valgrind,这应该会发现错误。

你真正的问题是你在写作

char* buffer = new char[strlen("This string is 27 char long" + 1)];
而不是

char* buffer = new char[strlen("This string is 27 char long") + 1];
意思是在第一个中,您
char* buffer = new char[strlen("This string is 27 char long") + 1];
const char szText[] = "This string is 27 char long";
char* buffer = new char[strlen(szText) + 1];
sprintf(buffer, szText);
#include "stdio.h"
#include "string.h"

int main()
{
     unsigned int  y      = (0xFFFFFFFF);
     char buffer[strlen("This string is 27 char long" + 1)];
      unsigned int  x      = (0xFFFFFFFF);
      sprintf(buffer, "This string is 27 char long");

      printf("X (%#x) is %#x, Y (%#x) is %#x, buffer '%s' (%#x) \n", &x, x,&y, y, buffer, buffer);
      return 0;
  }
    char* x = new char;
    sprintf(x, "This is way longer than one character");
    printf("%s", x);