8

In one of Apple's header files for libdispatch, queue.h, the following warning appears:

// 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.

Try as I may, I cannot come up with a test case that exemplifies this bug. I can create blocks that are instantiated on the stack, but they (seem to) always appear at unique addresses on the stack, even when out of scope with respect to each other.

I imagine that the answer to this is simple, but it escapes me. Can anyone fill the gaps in my (limited) understanding?

EDIT: I've seen this response, but I don't quite understand how that instance can translate to my example posted above. Can someone show me an example using if constructs?

Community
  • 1
  • 1
Aidan Steele
  • 10,999
  • 6
  • 38
  • 59
  • The link you posted has to do with a different problem, namely, that closures seem to act weird in the presence of mutable variables. See the question ["Javascript: closure of loop?"](http://stackoverflow.com/questions/5555464/javascript-closure-of-loop), which is the exact same problem in JavaScript. However, it appears they made the mistake warned against by this comment. Do blocks copy themselves automatically these days? I'd like to know, too. – Joey Adams Sep 16 '11 at 02:40
  • I've tried a bit but always get the same result as you, the block structs seem to be at the function scope. Perhaps a lot of people got bitten and they changed it such? – Per Johansson Sep 16 '11 at 06:52

1 Answers1

5

In order to crash a stack closure inside a function:

  • You need to make sure that the closure is indeed a stack closure. As of Apple Clang 2.1, a closure that doesn’t reference variables in its current context (like the one in queue.h) is realised as a global closure. This is an implementation detail that can vary amongst different compilers/compiler versions;

  • The compiler must emit code that effectively reuses/rewrites the stack area where the closure once lived. Otherwise, every object inside that function lives in a different address in the function stack frame, which means you won’t get a crash inside that function. It seems that Apple Clang 2.1 doesn’t reuse stack memory addresses. GCC 4.6 can reuse them, but it doesn’t support closures.

Since Apple Clang 2.1 doesn’t reuse addresses in a function stack frame and GCC 4.6 doesn’t support closures, from what I can tell it’s not possible to make this particular example — inside a function, invoke an out of scope stack closure — crash.

I wrote a more detailed text about this on my blog.

  • Thanks for the response, Bavarious. You were one of the people I had hoped would respond to my query. :) I had realised that blocks must capture state (otherwise they are rendered global) to be placed on the stack, but I found the same as you -- stack addresses are not reused for closures. – Aidan Steele Sep 23 '11 at 00:00
  • @Sed It’s more general — stack addresses apparently aren’t reused for any kind of object, closures included. This effectively means that everything that’s created in a function stack frame is alive until the end of the function. I’m not sure whether Clang intentionally avoids that or if it might be implemented in the future. –  Sep 23 '11 at 00:10
  • I was planning on following that with an "aha!", having found that stack addresses were indeed being reused for `long` types, but I had my compiler set to GCC 4.2. D'oh. – Aidan Steele Sep 23 '11 at 00:12
  • @Sed And they don’t get reused with Clang? –  Sep 23 '11 at 00:13
  • Nope, at least not in my limited tests. – Aidan Steele Sep 23 '11 at 00:42