This is how your function is called(a).
                   stack
---------------------------------      call             return
---------------------------------
               |                |
earlier frame  | earlier frame  |
               |                |
---------------------------------
---------------------------------
               |                |                     (5) pop old %ebp to %ebp
               |----------------|
caller's frame | func arguments | (1) push args
               |----------------|
               | return address | (2) push ret-addr   (6) reset %esp here
---------------------------------
---------------------------------
               | old %ebp       | (3) push old %ebp   (4) reset %esp = %ebp
               |----------------|     reset %ebp here
               |                |
callee's frame | local variables|
               |                |
---------------------------------
---------------------------------
When a function returns, the data in its frame may be kept. 
So, in your case, boo() used the same frame with old data of foo().
(a) Nothing in the standard guarantees this, it's just the most likely explanation for your particular scenario.