为什么当我分配一个 11MB 的字符数组而堆栈上限为 10MB 时,我的程序不会溢出堆栈?

发布于 2024-11-29 21:17:52 字数 1355 浏览 4 评论 0原文

我这里有两个简单的 C++ 程序和两个问题。我在 CentOS 5.2 中工作,我的开发环境如下:

  • g++ (GCC) 4.1.2 20080704 (Red Hat 4.1.2-50)
  • "ulimit -s" 输出:10240 (kbytes),即 10MB

程序#1:

main.cpp:(

int main(int argc, char * argv[])
{
    char buf[1024*1024*11] = {0};
    return 0;
}

使用“g++ -g main.cpp”编译)

程序在堆栈上分配了 1024*1024*11 字节(即 11MB),但没有崩溃。当我将分配大小更改为1024*1024*12(即12MB)后,程序崩溃了。我认为这应该是堆栈溢出引起的。 但是为什么当分配大小为 11MB(也大于 10MB 上限)时程序不会崩溃?

程序 #2:

main.cpp:

#include <iostream>

int main(int argc, char * argv[])
{
    char buf[1024*1024*11] = {0};

    std::cout << "*** separation ***" << std::endl;

    char buf2[1024*1024] = {0};

    return 0;
}

(使用“g++ -g main.cpp”编译)

该程序会导致程序崩溃,因为它在堆栈上分配了 12MB 字节。但是,根据核心转储文件(见下文),崩溃发生在 buf 上,而不是 buf2 上。 崩溃是否应该发生在 buf2 上,因为我们从程序 #1 中知道 char buf[1024*1024*11] 的分配是正确的,因此在我们分配另一个 1024*1024 字节后堆栈将溢出?

我认为一定有一些非常基本的概念我没有建立扎实的理解。但它们是什么?

附录:程序 #2 生成的核心转储信息:

Core was generated by `./a.out'.
Program terminated with signal 11, Segmentation fault.
[New process 16433]
#0  0x08048715 in main () at main.cpp:5
5           char buf[1024*1024*11] = {0};

I have two simple C++ programs and two questions here. I'm working in CentOS 5.2 and my dev environment is as follows:

  • g++ (GCC) 4.1.2 20080704 (Red Hat 4.1.2-50)
  • "ulimit -s" output: 10240 (kbytes), that is, 10MB

Program #1:

main.cpp:

int main(int argc, char * argv[])
{
    char buf[1024*1024*11] = {0};
    return 0;
}

(Compiled with "g++ -g main.cpp")

The program allocates 1024*1024*11 bytes(that is, 11MB) on the stack but it doesn't crash. After I change the allocation size to 1024*1024*12(that is, 12MB), the program crashes. I think this should be caused by a stack overflow. But Why does the program not crash when the allocation size is 11MB, which is also greater than the 10MB-upper-limit??

Program #2:

main.cpp:

#include <iostream>

int main(int argc, char * argv[])
{
    char buf[1024*1024*11] = {0};

    std::cout << "*** separation ***" << std::endl;

    char buf2[1024*1024] = {0};

    return 0;
}

(Compiled with "g++ -g main.cpp")

This program would result in a program crash because it allocates 12MB bytes on the stack. However, according to the core dump file(see below) the crash occurs on the buf but not buf2. Shouldn't the crash happen to buf2 because we know from program #1 that the allocation of char buf[1024*1024*11] is OK thus after we allocate another 1024*1024 bytes the stack would overflow?

I think there must be some quite fundamental concepts that I didn't build a solid understanding. But what are they??

Appendix: The core-dump info generated by program #2:

Core was generated by `./a.out'.
Program terminated with signal 11, Segmentation fault.
[New process 16433]
#0  0x08048715 in main () at main.cpp:5
5           char buf[1024*1024*11] = {0};

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

任谁 2024-12-06 21:17:53

理想情况下,您的两个程序都应该出现段错误。

通常,每当进入一个函数时,其中定义的所有变量都会被分配到堆栈上。然而,这也取决于编译代码的优化级别。

优化级别零,在编译期间指示为 -O0 表示根本没有优化。它也是编译代码时使用的默认优化级别。上述程序在使用 -O0 编译时会出现段错误。

但是,当您使用更高的优化级别编译程序时,编译器会注意到函数中未使用定义的变量。因此,它从汇编语言代码中删除了变量定义。因此,您的程序不会出现任何段错误。

Both of your programs should ideally give segfault.

Normally whenever a function is entered, all the variables defined in it are allocated memory onto the stack. This said, it however also depends on the optimization level with which the code is compiled.

Optimization level zero, indicated during compilation as -O0 indicates no optimzation at all. It is also the default optimization level with which a code is compiled. The above mentioned programs when compiled with -O0, gives segfault.

However, when you compile the programs using higher optimization levels, the compiler notices that the variable which is defined is not used in the function. It therefore removes the variable definition from the assembly language code. As a result, your programs won't give any segfault.

神爱温柔 2024-12-06 21:17:52

您错误地假设堆栈分配发生在代码中出现的位置。任何时候,只要您的局部变量的大小在编译时已知,这些变量的空间就会在输入函数时一起分配。稍后仅分配动态大小的局部变量(VLA 和 alloca)。

此外,错误是在写入内存时立即发生的,而不是在首次分配内存时发生。最有可能的是 buf 位于堆栈上的 buf2 之前,因此溢出发生在 buf 中,而不是 buf2 中。

You're wrongly assuming the stack allocations happens where they appear in your code. Anytime you have local variables whose size are known at compile time, space for those will be allocated together when the function is entered. Only dynamic sized local variables are allocated later (VLAs and alloca).

Furthermore the error happens as soon as you write to the memory, not when it's first allocated. Most likely buf is located before buf2 on the stack and the overflow thus happens in buf, not buf2.

路还长,别太狂 2024-12-06 21:17:52

要分析这些谜团,查看生成的代码总是有用的。我的猜测是,您的特定编译器版本正在执行不同的操作,因为我的段错误与 -O0 相关,但与 -O1 不同。

从程序 #1 开始,使用 g++ ac -g -O0,然后使用 objdump -S a.out

int main(int argc, char * argv[])
{
 8048484:       55                      push   %ebp
 8048485:       89 e5                   mov    %esp,%ebp

这是标准堆栈帧。这里没什么可看的。

 8048487:       83 e4 f0                and    $0xfffffff0,%esp

将堆栈对齐到 16 的倍数,以防万一。

 804848a:       81 ec 30 00 b0 00       sub    $0xb00030,%esp

分配0xB00030字节的堆栈空间。即 1024*1024*11 + 48 字节。还没有访问内存,所以也不例外。额外的 48 个字节供编译器内部使用。

 8048490:       8b 45 0c                mov    0xc(%ebp),%eax
 8048493:       89 44 24 1c             mov    %eax,0x1c(%esp)   <--- SEGFAULTS

第一次访问堆栈时超出了 ulimit,因此出现段错误。

 8048497:       65 a1 14 00 00 00       mov    %gs:0x14,%eax

Thiis 是堆栈保护器

 804849d:       89 84 24 2c 00 b0 00    mov    %eax,0xb0002c(%esp)
 80484a4:       31 c0                   xor    %eax,%eax
    char buf[1024*1024*11] = {0};
 80484a6:       8d 44 24 2c             lea    0x2c(%esp),%eax
 80484aa:       ba 00 00 b0 00          mov    $0xb00000,%edx
 80484af:       89 54 24 08             mov    %edx,0x8(%esp)
 80484b3:       c7 44 24 04 00 00 00    movl   $0x0,0x4(%esp)
 80484ba:       00 
 80484bb:       89 04 24                mov    %eax,(%esp)
 80484be:       e8 d1 fe ff ff          call   8048394 <memset@plt>

初始化数组,调用 memset

    return 0;
 80484c3:       b8 00 00 00 00          mov    $0x0,%eax
}

如您所见,当访问内部变量时会发生段错误,因为它们恰好位于大数组下方(它们必须位于大数组下方,因为有堆栈保护器,以检测堆栈粉碎)。

如果您使用优化进行编译,编译器会注意到您对数组没有做任何有用的事情,并将其优化掉。所以没有 sigseg。

可能您的 GCC 版本在非优化模式下有点过于聪明,并删除了数组。如果您发布 objdump -S a.out 的输出,我们可以进一步分析它。

To analyze these kind of mysteries, it is always useful to look at the generated code. My guess is that your particular compiler version is doing something different, because mine segfaults with -O0, but not with -O1.

From your program #1, with g++ a.c -g -O0, and then objdump -S a.out

int main(int argc, char * argv[])
{
 8048484:       55                      push   %ebp
 8048485:       89 e5                   mov    %esp,%ebp

This is the standard stack frame. Nothing to see here.

 8048487:       83 e4 f0                and    $0xfffffff0,%esp

Align the stack to multiple of 16, just in case.

 804848a:       81 ec 30 00 b0 00       sub    $0xb00030,%esp

Allocate 0xB00030 bytes of stack space. That is 1024*1024*11 + 48 bytes. No access to the memory yet, so no exception. The extra 48 bytes are of internal use of the compiler.

 8048490:       8b 45 0c                mov    0xc(%ebp),%eax
 8048493:       89 44 24 1c             mov    %eax,0x1c(%esp)   <--- SEGFAULTS

The first time the stack is accessed is beyond the ulimit, so it segfaults.

 8048497:       65 a1 14 00 00 00       mov    %gs:0x14,%eax

Thiis is the stack-protector.

 804849d:       89 84 24 2c 00 b0 00    mov    %eax,0xb0002c(%esp)
 80484a4:       31 c0                   xor    %eax,%eax
    char buf[1024*1024*11] = {0};
 80484a6:       8d 44 24 2c             lea    0x2c(%esp),%eax
 80484aa:       ba 00 00 b0 00          mov    $0xb00000,%edx
 80484af:       89 54 24 08             mov    %edx,0x8(%esp)
 80484b3:       c7 44 24 04 00 00 00    movl   $0x0,0x4(%esp)
 80484ba:       00 
 80484bb:       89 04 24                mov    %eax,(%esp)
 80484be:       e8 d1 fe ff ff          call   8048394 <memset@plt>

Initialize the array, calling memset

    return 0;
 80484c3:       b8 00 00 00 00          mov    $0x0,%eax
}

As you can see, the segfault happens when the internal variables are accessed, because they happen to be below the big array (they have to be, because there is the stack protector, to detect stack smashing).

If you compile with optimizations, the compiler notices that you do nothing useful with the array and optimizes it out. So no sigseg.

Probably your version of GCC is a bit oversmart in non-optimization mode, and removes the array. We can analyze it further if you post the output of objdump -S a.out.

南巷近海 2024-12-06 21:17:52

在堆栈上定义局部变量时,没有像在堆中那样真正分配内存。堆栈内存分配更简单地包括更改堆栈指针的地址(将由被调用函数使用)以保留所需的内存。

我怀疑更改堆栈指针的操作仅在函数开始时执行一次,以便为所有使用局部变量保留空间(反对每个局部变量更改一次)。这解释了为什么您的程序 #2 会在第一次分配时发生错误。

When defining local variables on the stack, there's no real allocation of memory like it is done in the heap. The stack memory allocation consists more simply in changing the address of the stack pointer (that is going to be used by called functions) to reserve the wanted memory.

I suspect that this operation of changing the stack pointer is done only once, at the beginning of the function, to reserve space for all the used local variable (by oposition of changing it once per local variable). This explains why the error on your program #2 occurs on the first allocation.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文