It turns out that DontNeedShadowFor() on Darwin doesn't actually zero out nor release the memory, and this can lead to a crash when the memory is reused (by a new thread). The particular problem on Darwin is because we actually use the shadow memory to store ThreadState * (as a fake thread-local storage), and if this contains a stale value, we'll crash.
I'll try to add a test case, but it seems it's pretty hard to trigger.