reorder thread list unlink in pthread_exit after all locks

since the backend for LOCK() skips locking if single-threaded, it's
unsafe to make the process appear single-threaded before the last use
of lock.

this fixes potential unsynchronized access to a linked list via
__dl_thread_cleanup.
This commit is contained in:
Rich Felker 2020-05-22 17:35:14 -04:00
parent cabc36969b
commit 4d5aa20a94

View File

@ -90,14 +90,7 @@ _Noreturn void __pthread_exit(void *result)
exit(0);
}
/* At this point we are committed to thread termination. Unlink
* the thread from the list. This change will not be visible
* until the lock is released, which only happens after SYS_exit
* has been called, via the exit futex address pointing at the lock. */
libc.threads_minus_1--;
self->next->prev = self->prev;
self->prev->next = self->next;
self->prev = self->next = self;
/* At this point we are committed to thread termination. */
/* Process robust list in userspace to handle non-pshared mutexes
* and the detached thread case where the robust list head will
@ -121,6 +114,16 @@ _Noreturn void __pthread_exit(void *result)
__do_orphaned_stdio_locks();
__dl_thread_cleanup();
/* Last, unlink thread from the list. This change will not be visible
* until the lock is released, which only happens after SYS_exit
* has been called, via the exit futex address pointing at the lock.
* This needs to happen after any possible calls to LOCK() that might
* skip locking if libc.threads_minus_1 is zero. */
libc.threads_minus_1--;
self->next->prev = self->prev;
self->prev->next = self->next;
self->prev = self->next = self;
/* This atomic potentially competes with a concurrent pthread_detach
* call; the loser is responsible for freeing thread resources. */
int state = a_cas(&self->detach_state, DT_JOINABLE, DT_EXITING);