在Apple的libdispatch
queue.h
标题文件中,出现以下警告:
// The declaration of a block allocates storage on the stack.
// Therefore, this is an invalid construct:
dispatch_block_t block;
if (x) {
block = ^{ printf("true\n"); };
} else {
block = ^{ printf("false\n"); };
}
block(); // unsafe!!!
// What is happening behind the scenes:
if (x) {
struct Block __tmp_1 = ...; // setup details
block = &__tmp_1;
} else {
struct Block __tmp_2 = ...; // setup details
block = &__tmp_2;
}
// As the example demonstrates, the address of a stack variable is
// escaping the scope in which it is allocated. That is a classic C bug.
尽我所能,我无法想出一个例证这个bug的测试用例。我可以创建在堆栈上实例化的块,但它们(似乎)总是出现在堆栈上的唯一地址,即使在相互之间超出范围时也是如此。
我想这个问题的答案很简单,但它逃脱了我。任何人都能填补我(有限)理解的空白吗?
编辑:我看过this响应,但我不太明白该实例如何转换为我上面发布的示例。有人可以使用if
构造向我展示一个示例吗?
答案 0 :(得分:5)
为了使函数内的堆栈闭包崩溃:
您需要确保闭包确实是堆栈闭包。从Apple Clang 2.1开始,在当前上下文中不引用变量的闭包(如queue.h中的变量)被实现为全局闭包。这是一个实现细节,可以在不同的编译器/编译器版本之间变化;
编译器必须发出有效重用/重写闭包曾经存在的堆栈区域的代码。否则,该函数内的每个对象都存在于函数堆栈框架中的不同地址中,这意味着您不会在该函数内部发生崩溃。似乎Apple Clang 2.1没有重用堆栈内存地址。 GCC 4.6可以重用它们,但它不支持闭包。
由于Apple Clang 2.1没有在函数堆栈框架中重用地址而且GCC 4.6不支持闭包,所以我可以告诉它不可能制作这个特定的例子 - 在函数内部调用超出范围的堆栈关闭 - 崩溃。
我在my blog上写了一篇关于此问题的更详细的文字。