[英]C alloca function - what happens when too much memory is tried to be allocated
In C the alloca() function allocates memory on the stackframe of the caller of alloca(). 在C语言中,alloca()函数在alloca()调用者的堆栈帧上分配内存。
What happens when you try to allocate a huge number of bytes that it cannot possibly allocate? 当您尝试分配无法分配的大量字节时会发生什么?
Or does it allocate nothing at all? 还是根本不分配任何东西?
alloca( 100000000000000000000 ); alloca(100000000000000000000);
The manual mentions: 手册中提到:
The alloca() function returns a pointer to the beginning of the allocated space.
alloca()函数返回一个指向分配空间开头的指针。 If the allocation causes stack overflow, program behavior is undefined.
如果分配导致堆栈溢出,则程序行为未定义。
I understand the behaviour is undefined. 我了解行为是不确定的。 But there must be more to say than that:
但是必须要说的比这还多:
Does anyone have some more info about this? 有人对此有更多信息吗?
What happens depends on your compiler and the hardening options in use; 会发生什么情况取决于您的编译器和使用的强化选项。 typically, you have no indication that it failed and you either clobber random unrelated memory, or crash, shortly after calling
alloca
. 通常,您没有迹象表明它失败了,或者在调用
alloca
之后不久就破坏了随机无关的内存,或者崩溃了。 With some hardening options you may be able to make the crash reliable, but you're never able to detect and recover from the failure. 使用某些强化选项,您也许可以使崩溃更可靠,但您永远无法检测到故障并从故障中恢复。
alloca
simply should not be used. 根本不应该使用
alloca
。 It was a bad mistake that looked too good to be true, because it is. 看起来好得令人难以置信,这是一个严重的错误,因为事实确实如此。
The GNU libc implementation of alloca()
reads: alloca()
的GNU libc实现如下:
# define alloca(size) __builtin_alloca (size)
It uses a compiler built-in , thus it depends entirely on how it's been implemented by the compiler. 它使用内置的编译器 ,因此完全取决于编译器的实现方式。 More specifically, it depends on how the stack is handled, which happens to be aa machine and ABI-dependent data structure.
更具体地说,这取决于堆栈的处理方式,而堆栈恰好是一台机器和与ABI相关的数据结构。
Let's examine a concrete case. 让我们研究一个具体案例。 On my machine, this is the assembly for
alloca(100000000000L)
: 在我的机器上,这是
alloca(100000000000L)
的程序集:
0e9b: movabsq $-100000000016, %rax ; * Loads (size + 16) into rax.
0ea5: addq %rax, %rsp ; * Adds it to the top of the stack.
0ea8: movq %rsp, -48(%rbp) ; * Temporarily stores it.
0eac: movq -48(%rbp), %rax ; * These five instructions round the
0eb0: addq $15, %rax ; value stored to the next multiple
0eb4: shrq $4, %rax ; of 0x10 by doing:
0eb8: shlq $4, %rax ; rax = ((rax+15) >> 4) << 4
0ebc: movq %rax, -48(%rbp) ; and storing it again in the stack.
0ec0: movq -48(%rbp), %rax ; * Reads the rounded value and copies
0ec4: movq %rax, -24(%rbp) ; it on the previous stack position.
Compiled using gcc-4.2 -g test.c -o test
from the following program: 使用
gcc-4.2 -g test.c -o test
从以下程序编译:
With something to refer to, your questions can now be answered: 通过参考,您的问题现在可以得到回答:
Does it allocate as many bytes as it can until the stack meets the heap segment?
它是否分配尽可能多的字节,直到堆栈遇到堆段为止?
It just blindly grows the stack by the requested number of bytes. 它只是盲目地使堆栈增加所请求的字节数。 No bounds checking is performed at all , so both the stack pointer and the returned value will probably now be in a illegal location.
无边界检查全部执行,所以无论是堆栈指针和返回值将可能现在处于非法位置。 Attempting to read/write from the returned value (or push onto the stack) will result on a
SIGSEGV
. 尝试从返回的值进行读取/写入(或压入堆栈)将导致
SIGSEGV
。
What does it return, a pointer to the first byte after the top of the stack before main was called?
它返回什么,即指向main调用之前堆栈顶部之后的第一个字节的指针?
It returns a pointer to the first byte of allocated memory. 它返回一个指向已分配内存的第一个字节的指针。
After
alloca()
returned is the stack pointer different then what it was beforealloca()
was called?返回
alloca()
之后,堆栈指针是否不同于调用alloca()
之前的指针?
Yes, see explanation above. 是的,请参阅上面的说明。 Also, when the function that called
alloca
returns, the stack will be restored to previous frame and it will be usable again. 同样,当调用
alloca
的函数返回时,堆栈将恢复到前一帧,并且将再次可用。
Strictly speaking, nobody knows, because "undefined behavior" isn't itself defined. 严格来说,没有人知道,因为“未定义的行为”本身不是定义的。 (alloca isn't defined by the C or POSIX standards, for example).
(例如,alloca不是由C或POSIX标准定义的)。
For illustration only, C's definition of "undefined behavior" is (ISO 9899:1999, section 3.4.3): 仅出于说明目的,C对“未定义行为”的定义是(ISO 9899:1999,第3.4.3节):
"behavior, upon use of a nonportable or erroneous program construct or of erroneous data, for which this International Standard imposes no requirements “在使用非便携式或错误程序构造或错误数据时,本国际标准对此不施加任何要求的行为
"NOTE Possible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message)." “要点可能的不确定行为范围从完全忽略具有不可预测结果的情况到在以环境特征的文档化方式进行翻译或程序执行期间的行为(有或没有发出诊断消息),到终止翻译或执行(有发出诊断消息)。”
So: absolutely anything can happen. 所以:绝对会发生任何事情。 Your hard disk might get reformatted.
您的硬盘可能会重新格式化。 The sky might fall in. (OK, probably not, but it would be perfectly acceptable given your input.) You can't make any assumption or statement whatsoever.
天空可能会掉下来。(好的,可能不会,但是考虑到您的输入,这是完全可以接受的。)您无法做出任何假设或声明。
If your program makes any such assumptions about (or reliance on) program behaviour after an alloca-induced stack overflow then your program is broken. 如果您的程序在分配内存引起的堆栈溢出后对程序行为做出任何此类假设(或依赖),则您的程序将被破坏。 It's best not to surmise what a particular compiler might do in that situation.
最好不要推测在这种情况下特定的编译器可能会做什么。 Your program's broken, end of story.
您的程序坏了,故事结束了。
On windows you can recover from it. 在Windows上,您可以从中恢复。 Tested using gcc:
使用gcc测试:
/*
* Show how get memory from stack without crash
* Currently, compiles ok with mingw, and the latest version of tiny c (from git)
* Last Version: 29/june/2014
* Programmed by Carlos Montiers
*/
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <malloc.h>
#include <setjmp.h>
int _resetstkoflw(void);
static jmp_buf alloca_jmp_buf;
LONG WINAPI AllocaExceptionFilter(EXCEPTION_POINTERS * ExceptionInfo)
{
switch (ExceptionInfo->ExceptionRecord->ExceptionCode) {
case STATUS_STACK_OVERFLOW:
// reset the stack
if (0 == _resetstkoflw()) {
printf("Could not reset the stack!\n");
_exit(1);
}
longjmp(alloca_jmp_buf, 1);
break;
}
return EXCEPTION_EXECUTE_HANDLER;
}
int main()
{
void *m;
int alloca_jmp_res;
LPTOP_LEVEL_EXCEPTION_FILTER prev;
//replace the exception filter function saving the previous
prev = SetUnhandledExceptionFilter(AllocaExceptionFilter);
alloca_jmp_res = setjmp(alloca_jmp_buf);
if ((0 == alloca_jmp_res)) {
m = alloca(INT_MAX);
} else if ((1 == alloca_jmp_res)) {
m = NULL;
}
//restore exception filter function
SetUnhandledExceptionFilter(prev);
if (!m) {
printf("alloca Failed\n");
}
printf("Bye\n");
return 1;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.