在C中,alloca()函数在alloca()的调用者的堆栈帧上分配内存。
当您尝试分配无法分配的大量字节时会发生什么?
或者它什么都没有分配?
alloca(100000000000000000000);
手册提到:
alloca()函数返回指向开头的指针 分配空间。如果分配导致堆栈溢出,则编程 行为未定义。
我理解这种行为是未定义的。但必须有更多话要说:
有没有人有更多关于此的信息?
答案 0 :(得分:3)
发生什么取决于您的编译器和使用的加固选项;通常情况下,您没有迹象表明它失败了,您在调用alloca
后不久就会破坏随机无关内存或崩溃。通过一些加固选项,您可以使崩溃可靠,但您永远无法检测并从故障中恢复。不应该使用alloca
。这是一个糟糕的错误,看起来好得令人难以置信,因为它是。
答案 1 :(得分:2)
alloca()
的{{3}}实现读取:
# define alloca(size) __builtin_alloca (size)
它使用内置的编译器,因此完全依赖于 编译器如何实现它。更具体地说,它取决于堆栈的处理方式,这恰好是一台机器和ABI相关的数据结构。
让我们研究具体案例。在我的机器上,这是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.
使用以下程序中的gcc-4.2 -g test.c -o test
进行编译:
有了可以参考的内容,现在可以回答您的问题:
在堆栈遇到堆段之前,它是否分配了尽可能多的字节数?
它只是按请求的字节数盲目地增加堆栈。在所有中都没有执行边界检查,因此堆栈指针和返回值现在可能都处于非法位置。尝试从返回值读取/写入(或推入堆栈)将导致SIGSEGV
。
它返回什么,指向main之前的堆栈顶部之后的第一个字节的指针?
它返回指向已分配内存的第一个字节的指针。
返回
alloca()
后的堆栈指针与调用alloca()
之前的不同之处是什么?
是的,请参阅上面的解释。此外,当调用alloca
的函数返回时,堆栈将恢复到前一帧,并且它将再次可用。
答案 2 :(得分:1)
严格来说,没有人知道,因为“未定义的行为”本身并未定义。 (例如,alloca不是由C或POSIX标准定义的。)
仅供说明,C对“未定义行为”的定义是(ISO 9899:1999,第3.4.3节):
“使用不可移植或错误的程序结构或错误数据的行为,本国际标准没有要求
“注意可能的未定义行为包括完全忽略不可预测结果的情况,在翻译或程序执行期间以环境特征(有或没有发出诊断消息)的文件化方式行事,终止翻译或执行(发出诊断信息)。“
所以:绝对可能发生任何事情。您的硬盘可能会被重新格式化。天空可能会落入。(好吧,可能不会,但是根据你的输入,它是完全可以接受的。)你不能做出任何假设或陈述。
如果您的程序在alloca引起的堆栈溢出后对程序行为做出任何此类假设,那么您的程序就会被破坏。最好不要推测特定编译器在这种情况下可能会做什么。你的节目坏了,故事的结尾。
答案 3 :(得分:0)
在Windows上,您可以从中恢复。使用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;
}