Certain frameworks (e.g., IOSurface, IOKit) map and unmap VM ranges
through direct interaction with the kernel. So unfortunately, there is
no API that we can intercept to observe these operations and clear the
associated shadow memory. When such a previously-used VM region is
reused for a new thread, the shadow memory for the pthread_t struct may
have leftover, non-zero shadow bytes. Previously, we erroneously
interpreted these non-zero bytes as a pointer to the ThreadState
metadata and crashed.
In SignalSafeGetOrAllocate() we assumed all non-zero values to be
valid pointers, which led to crashes when a pthread_t struct later is
placed the the corresponding app memory and we interpret the shadow as
our pointer to the ThreadState metadata.
Now we always allocate this ThreadState metadata once and only once at
thread creation and remove the "get or allocate" semantics. We
therefore don't depend on the previous value anymore.
This requires that we don't call any interceptors before THREAD_CREATE
runs, which is a condition that currently holds. I added additional
asserts and tests to ensure we notice if this changes.
This change also fixes the following bug: previously we deallocated the
ThreadState metadata in THREAD_TERMINATE, however, some interceptors
still get called after that, which re-creates a new ThreadState
instance for the shutting down thread. Usually this worked "by
accident". The created metadata was either leaked or reused when
another thread was created in its place.