I suspect it would have corrupted the heap instead of the stack. If so, this would be even harder to diagnose, because the corruption would show up elsewhere unrelated to the actual problem.
The underlying problem is that you
1) queue an asynchronous callback and reserve some memory for it to use
2) wait (or so you think) for the function that calls the callback to be done and return
3) something interrupts the wait, but the callback is still queued for execution sometime in the future.
4) since the wait is over, you assume the callback was called, and the memory it was going to use can be safely freed
5) if the memory was on the stack, it gets freed purely by returning. if it was in the heap, you'd deallocate it.
6) the callback that was still queued fires up and writes to memory it no longer should be able to use.
To really fix this situation, you'd need to free the memory in the callback. This is not possible in this case unless you can rewrite the library select() function significantly, perhaps even rewriting other parts (not familiar enough with those APIs to be really sure).
Even in similar situations where it is possible (e.g. if you have garbage collection), it will often turn a memory corruption into a logical corruption, because you have a callback fired related to something that is not relevant anymore, and any side effects of that callback will happen without the proper context. (in this case the only side effect is probably the memory access, so in this particular instance the problem would be "fixed")
[Edit: if the heap allocation was exception-aware (e.g. RAII that gets called during exception unwinding, then you would get heap corruption. If it was manual deallocation and thus skipped during exception unwind, then indeed you would just get a memory leak exactly as you said]