[PATCH 3/6] nptl: Do not use pthread set_tid_address as state synchronization (BZ #19951)
Florian Weimer
fweimer@redhat.com
Thu Dec 18 14:49:36 GMT 2025
* Adhemerval Zanella:
> The use-after-free described in BZ#19951 is due to the use of two
> different PD fields, 'joinid' and 'cancelhandling', to describe the
> thread state and to synchronise the calls of pthread_join,
> pthread_detach, pthread_exit, and normal thread exit.
>
> Any state change may require checking both fields atomically to handle
> partial state (e.g., pthread_join() with a cancellation handler to
> issue a 'joinstate' field rollback).
>
> This patch uses a different PD member with 4 possible states (JOINABLE,
> DETACHED, EXITING, and EXITED) instead of the pthread 'tid' field, with
> the following logic:
>
> 1. On pthread_create, the initial state is set either to JOINABLE or
> DETACHED depending on the pthread attribute used.
>
> 2. On pthread_detach, a CAS is issued on the state. If the CAS fails,
> the thread is already detached (DETACHED) or being terminated (EXITING).
> For the former, an EINVAL is returned; for the latter, pthread_detach
> should be responsible for joining the thread (and for deallocating any
> internal resources).
>
> 3. In the exit phase of the wrapper function for the thread start routine
> (reached either if the thread function has returned, pthread_exit has
> been called, or cancellation handled has been acted upon), we issue a
> CAS on state to set it to the EXITING mode.
>
> If the thread is previously in DETACHED mode, the thread is responsible
> for deallocating any resources; otherwise, the thread must be joined
> (detached threads cannot deallocate themselves immediately).
>
> 4. The clear_tid_field on 'clone' call is changed to set the new 'state'
> field on thread exit (EXITED). This state is only reached at thread
> termination.
>
> 5. The pthread_join implementation is now simpler: the futex wait is done
> directly on thread state, and there is no need to reset it in case of
> timeout since the state is now set either by pthread_detach() or by the
> kernel on process termination.
>
> The race condition on pthread_detach is avoided with a single atomic
> operation on the PD state: once the mode is set to THREAD_STATE_DETACHED, it
> is up to the thread itself to deallocate its memory (done during the exit
> phase at pthread_create()).
>
> Also, the INVALID_NOT_TERMINATED_TD_P is removed since a negative yid is
> not possible, and the macro is not used anywhere.
>
> This change triggers an invalid C11 thread test: it creates a thread that
> detaches, and after a timeout, the creating thread checks whether the join
> fails. The issue is that once thrd_join() is called, the thread's lifetime
> is not defined.
>
> Checked on x86_64-linux-gnu, i686-linux-gnu, aarch64-linux-gnu,
> arm-linux-gnueabihf, and powerpc64-linux-gnu.
This version looks okay to me.
Reviewed-by: Florian Weimer <fweimer@redhat.com>
Thanks,
Florian
More information about the Libc-alpha
mailing list