尝试缓冲区溢出malloc()分配的值

3

我对malloc()函数有些困惑。

如果sizeof(char)1字节,并且malloc()函数接受N个字节的参数来分配内存,那么如果我这样做:

char* buffer = malloc(3);

我分配了一个可以存储3个字符的缓冲区,对吗?
char* s = malloc(3);
int i = 0;
while(i < 1024) { s[i] = 'b'; i++; }
s[i++] = '$';
s[i] = '\0';
printf("%s\n",s);

它运行良好,并将1024个s存储在s中。

bbbb[...]$

为什么上面的代码不会导致缓冲区溢出?有人能解释一下吗?

1
修复错误,谜团就会消失。有bug的代码会做出奇怪的事情,这就是为什么我们不写有bug的代码。 - David Schwartz
这是一个缓冲区溢出;你只是没有做任何事情来利用它。只要内存地址确实存在,C语言就可以让你写入该地址。这并不是什么高深的科学。 - tbert
@tbert “只要它确实存在”并不严格正确——如果它在您进程的地址空间之外,C将不允许您写入它。 - Timothy Jones
@TimothyJones 好的,“实际上存在于您的虚拟机空间中”;考虑到问题本身的混乱性,我不确定我们需要如此严谨。 - tbert
6个回答

5

malloc(size) 会返回一个内存地址,其中至少有size个字节可供您使用。您可能可以直接写入s [size]之后的字节,但是:

  • 这些字节可能属于程序的其他部分,在执行后期会导致问题。
  • 或者,这些字节可能适合您进行写入-它们可能属于程序使用的页面,但未用于任何内容。
  • 或者,它们可能属于malloc()用于跟踪您的程序已经使用了什么的结构。破坏这一点非常糟糕!
  • 或者,它们可能不属于您的程序,这将导致立即分段错误。如果您访问s [size + large_number],则很可能会出现这种情况。

很难说哪种情况会发生,因为访问要求malloc()空间外的空间将导致未定义的行为。

在您的示例中,您正在溢出缓冲区,但不会导致立即崩溃。请记住,C不会对数组/指针访问进行边界检查。


此外,malloc()在堆上创建内存,但缓冲区溢出通常涉及堆栈上的内存。如果您想作为练习创建一个缓冲区溢出,请使用

char s[3];

取而代之,这将在堆栈上创建一个3个字符的数组。在大多数系统上,数组后面不会有任何空闲空间,因此s[2]之后的空间将属于堆栈。写入该空间可能会覆盖堆栈上的其他变量,并最终通过(例如)覆盖当前堆栈帧的返回指针导致分段错误。


还有一件事:

如果sizeof(char)是1字节

sizeof(char)实际上被标准定义为始终为1字节。但是,在一些奇特的系统上,该1字节的大小可能不是8位。当然,大多数情况下您不必担心这个问题。


1

在编程中,超出已分配内存的边界进行写入是未定义行为(UB)

任何行为都有可能发生,对于UB不需要诊断,也可能遇到任何行为。
UB并不一定会导致分段错误。


1
从某种意义上说,您确实溢出了3个字符缓冲区。但是,您尚未溢出程序的地址空间(尚未)。因此,您已经超出了s*的范围,但是您正在覆盖程序中的其他随机数据。由于程序拥有这些数据,因此程序不会崩溃,但仍然会做非常错误的事情,并且未来的行为是未定义的。

1
实际上,这样做的结果是破坏了堆。影响可能不会立即出现(事实上,这正是此类错误难以调试的部分)。但是,您可能会破坏堆中的任何其他内容,或者在程序地址空间的那一部分中的任何其他内容。很可能您还破坏了malloc()内部数据结构,因此后续的malloc()或free()调用可能会导致程序崩溃,使许多程序员(错误地)认为他们已经发现了malloc()中的一个bug。

0

你正在溢出缓冲区。取决于你溢出到哪个内存,才会得到错误消息。


0

你尝试在发布模式下执行代码了吗?或者你尝试释放了你的内存吗?这是一种未定义的行为。

这有点像语言黑客,对于它的使用有些可疑。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接